Merge "Move SmsApplication to telephony/common." am: f7aec5de09
am: 23c8673670

Change-Id: Ib21670e2ceac1ef25194aa409fa9e01245b26f24
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 26c2c0c..f4e465a 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -563,6 +563,21 @@
      * account, or the AbstractAcccountAuthenticator managing the account did so or because the
      * client shares a signature with the managing AbstractAccountAuthenticator.
      *
+     * <div class="caution"><p><b>Caution: </b>This method returns personal and sensitive user data.
+     * If your app accesses, collects, uses, or shares personal and sensitive data, you must clearly
+     * disclose that fact to users. For apps published on Google Play, policies protecting user data
+     * require that you do the following:</p>
+     * <ul>
+     * <li>Disclose to the user how your app accesses, collects, uses, or shares personal and
+     * sensitive data. Learn more about
+     * <a href="https://play.google.com/about/privacy-security-deception/user-data/#!#personal-sensitive">acceptable
+     * disclosure and consent</a>.</li>
+     * <li>Provide a privacy policy that describes your use of this data on- and off-device.</li>
+     * </ul>
+     * <p>To learn more, visit the
+     * <a href="https://play.google.com/about/privacy-security-deception/user-data">Google Play
+     * Policy regarding user data</a>.</p></div>
+     *
      * <p>
      * It is safe to call this method from the main thread.
      *
@@ -649,6 +664,22 @@
      * the account. For example, there are types corresponding to Google and Facebook. The exact
      * string token to use will be published somewhere associated with the authenticator in
      * question.
+     * </p>
+     *
+     * <div class="caution"><p><b>Caution: </b>This method returns personal and sensitive user data.
+     * If your app accesses, collects, uses, or shares personal and sensitive data, you must clearly
+     * disclose that fact to users. For apps published on Google Play, policies protecting user data
+     * require that you do the following:</p>
+     * <ul>
+     * <li>Disclose to the user how your app accesses, collects, uses, or shares personal and
+     * sensitive data. Learn more about
+     * <a href="https://play.google.com/about/privacy-security-deception/user-data/#!#personal-sensitive">acceptable
+     * disclosure and consent</a>.</li>
+     * <li>Provide a privacy policy that describes your use of this data on- and off-device.</li>
+     * </ul>
+     * <p>To learn more, visit the
+     * <a href="https://play.google.com/about/privacy-security-deception/user-data">Google Play
+     * Policy regarding user data</a>.</p></div>
      *
      * <p>
      * It is safe to call this method from the main thread.
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 955093d..90ecce2 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -390,6 +390,8 @@
             }
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+                    new ArrayList<>(supportedSpecs));
             final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
             if (res == null) {
                 return null;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2a17800..9e011ac 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2677,6 +2677,9 @@
      * that application is first launched (that is the first time it is moved
      * out of the stopped state).  The data contains the name of the package.
      *
+     * <p>When the application is first launched, the application itself doesn't receive this
+     * broadcast.</p>
+     *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      */
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3cecd7f..a15caa0 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -101,19 +101,6 @@
  * <p>
  * The ApiDemos project contains examples of using this API:
  * <code>ApiDemos/src/com/example/android/apis/content/InstallApk*.java</code>.
- * <p>
- * On Android Q or above, an app installed notification will be posted
- * by system after a new app is installed.
- * To customize installer's notification icon, you should declare the following in the manifest
- * &lt;application> as follows: </p>
- * <pre>
- * &lt;meta-data android:name="com.android.packageinstaller.notification.smallIcon"
- * android:resource="@drawable/installer_notification_icon"/>
- * </pre>
- * <pre>
- * &lt;meta-data android:name="com.android.packageinstaller.notification.color"
- * android:resource="@color/installer_notification_color"/>
- * </pre>
  */
 public class PackageInstaller {
     private static final String TAG = "PackageInstaller";
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 2b1b32e..56a6285 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -16,12 +16,17 @@
 
 package android.content.pm;
 
+import android.annotation.IntDef;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.DebugUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Per-user information.
@@ -94,6 +99,25 @@
      */
     public static final int FLAG_DEMO = 0x00000200;
 
+    /**
+     * @hide
+     */
+    @IntDef(flag = true, prefix = "FLAG_", value = {
+            FLAG_PRIMARY,
+            FLAG_ADMIN,
+            FLAG_GUEST,
+            FLAG_RESTRICTED,
+            FLAG_INITIALIZED,
+            FLAG_MANAGED_PROFILE,
+            FLAG_DISABLED,
+            FLAG_QUIET_MODE,
+            FLAG_EPHEMERAL,
+            FLAG_DEMO
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UserInfoFlag {
+    }
+
     public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
 
     @UnsupportedAppUsage
@@ -128,6 +152,18 @@
     @UnsupportedAppUsage
     public boolean guestToRemove;
 
+    /**
+     * This is used to optimize the creation of an user, i.e. OEMs might choose to pre-create a
+     * number of users at the first boot, so the actual creation later is faster.
+     *
+     * <p>A {@code preCreated} user is not a real user yet, so it should not show up on regular
+     * user operations (other than user creation per se).
+     *
+     * <p>Once the pre-created is used to create a "real" user later on, {@code preCreate} is set to
+     * {@code false}.
+     */
+    public boolean preCreated;
+
     @UnsupportedAppUsage
     public UserInfo(int id, String name, int flags) {
         this(id, name, null, flags);
@@ -155,6 +191,13 @@
 
     @UnsupportedAppUsage
     public boolean isGuest() {
+        return isGuest(flags);
+    }
+
+    /**
+     * Checks if the flag denotes a guest user.
+     */
+    public static boolean isGuest(@UserInfoFlag int flags) {
         return (flags & FLAG_GUEST) == FLAG_GUEST;
     }
 
@@ -165,6 +208,13 @@
 
     @UnsupportedAppUsage
     public boolean isManagedProfile() {
+        return isManagedProfile(flags);
+    }
+
+    /**
+     * Checks if the flag denotes a managed profile.
+     */
+    public static boolean isManagedProfile(@UserInfoFlag int flags) {
         return (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE;
     }
 
@@ -252,6 +302,7 @@
         lastLoggedInTime = orig.lastLoggedInTime;
         lastLoggedInFingerprint = orig.lastLoggedInFingerprint;
         partial = orig.partial;
+        preCreated = orig.preCreated;
         profileGroupId = orig.profileGroupId;
         restrictedProfileParentId = orig.restrictedProfileParentId;
         guestToRemove = orig.guestToRemove;
@@ -268,6 +319,22 @@
         return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
     }
 
+    /** @hide */
+    public String toFullString() {
+        return "UserInfo[id=" + id
+                + ", name=" + name
+                + ", flags=" + flagsToString(flags)
+                + (preCreated ? " (pre-created)" : "")
+                + (partial ? " (partial)" : "")
+                + "]";
+    }
+
+    /** @hide */
+    public static String flagsToString(int flags) {
+        return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags);
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
@@ -281,9 +348,10 @@
         dest.writeLong(creationTime);
         dest.writeLong(lastLoggedInTime);
         dest.writeString(lastLoggedInFingerprint);
-        dest.writeInt(partial ? 1 : 0);
+        dest.writeBoolean(partial);
+        dest.writeBoolean(preCreated);
         dest.writeInt(profileGroupId);
-        dest.writeInt(guestToRemove ? 1 : 0);
+        dest.writeBoolean(guestToRemove);
         dest.writeInt(restrictedProfileParentId);
         dest.writeInt(profileBadge);
     }
@@ -308,9 +376,10 @@
         creationTime = source.readLong();
         lastLoggedInTime = source.readLong();
         lastLoggedInFingerprint = source.readString();
-        partial = source.readInt() != 0;
+        partial = source.readBoolean();
+        preCreated = source.readBoolean();
         profileGroupId = source.readInt();
-        guestToRemove = source.readInt() != 0;
+        guestToRemove = source.readBoolean();
         restrictedProfileParentId = source.readInt();
         profileBadge = source.readInt();
     }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index f5426cd..1386b87 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -243,7 +243,8 @@
         public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
 
         /**
-         * The user-visible security patch level.
+         * The user-visible security patch level. This value represents the date when the device
+         * most recently applied a security patch.
          */
         public static final String SECURITY_PATCH = SystemProperties.get(
                 "ro.build.version.security_patch", "");
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 63641e5..c30491a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -41,6 +41,7 @@
      */
 
     UserInfo createUser(in String name, int flags);
+    UserInfo preCreateUser(int flags);
     UserInfo createProfileForUser(in String name, int flags, int userHandle,
             in String[] disallowedPackages);
     UserInfo createRestrictedProfile(String name, int parentUserHandle);
@@ -53,7 +54,7 @@
     void setUserIcon(int userHandle, in Bitmap icon);
     ParcelFileDescriptor getUserIcon(int userHandle);
     UserInfo getPrimaryUser();
-    List<UserInfo> getUsers(boolean excludeDying);
+    List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated);
     List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
     int[] getProfileIds(int userId, boolean enabledOnly);
     boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne);
@@ -92,6 +93,7 @@
     boolean someUserHasSeedAccount(in String accountName, in String accountType);
     boolean isManagedProfile(int userId);
     boolean isDemoUser(int userId);
+    boolean isPreCreated(int userId);
     UserInfo createProfileForUserEvenWhenDisallowed(in String name, int flags, int userHandle,
             in String[] disallowedPackages);
     boolean isUserUnlockingOrUnlocked(int userId);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9c9829f..af9e592 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -37,6 +37,7 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -2003,18 +2004,20 @@
 
     /**
      * Creates a user with the specified name and options. For non-admin users, default user
-     * restrictions are going to be applied.
-     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * restrictions will be applied.
+     *
+     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
      * @param name the user's name
-     * @param flags flags that identify the type of user and other properties.
+     * @param flags UserInfo flags that identify the type of user and other properties.
      * @see UserInfo
      *
-     * @return the UserInfo object for the created user, or null if the user could not be created.
+     * @return the UserInfo object for the created user, or {@code null} if the user could not be
+     * created.
      * @hide
      */
     @UnsupportedAppUsage
-    public UserInfo createUser(String name, int flags) {
+    public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
         UserInfo user = null;
         try {
             user = mService.createUser(name, flags);
@@ -2031,6 +2034,36 @@
     }
 
     /**
+     * Pre-creates a user with the specified name and options. For non-admin users, default user
+     * restrictions will be applied.
+     *
+     * <p>This method can be used by OEMs to "warm" up the user creation by pre-creating some users
+     * at the first boot, so they when the "real" user is created (for example,
+     * by {@link #createUser(String, int)} or {@link #createGuest(Context, String)}), it takes
+     * less time.
+     *
+     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @param flags UserInfo flags that identify the type of user and other properties.
+     * @see UserInfo
+     *
+     * @return the UserInfo object for the created user, or {@code null} if the user could not be
+     * created.
+     *
+     * @throw {@link IllegalArgumentException} if {@code flags} contains
+     * {@link UserInfo#FLAG_MANAGED_PROFILE}.
+     *
+     * @hide
+     */
+    public @Nullable UserInfo preCreateUser(@UserInfoFlag int flags) {
+        try {
+            return mService.preCreateUser(flags);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Creates a guest user and configures it.
      * @param context an application context
      * @param name the name to set for the user
@@ -2346,15 +2379,26 @@
     /**
      * Returns information for all users on this device, including ones marked for deletion.
      * To retrieve only users that are alive, use {@link #getUsers(boolean)}.
-     * <p>
-     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
      * @return the list of users that exist on the device.
      * @hide
      */
     @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public List<UserInfo> getUsers() {
+        return getUsers(/* excludeDying= */ false);
+    }
+
+    /**
+     * Returns information for all users on this device, based on the filtering parameters.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+            boolean excludePreCreated) {
         try {
-            return mService.getUsers(false);
+            return mService.getUsers(excludePartial, excludeDying, excludePreCreated);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -2370,16 +2414,12 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public long[] getSerialNumbersOfUsers(boolean excludeDying) {
-        try {
-            List<UserInfo> users = mService.getUsers(excludeDying);
-            long[] result = new long[users.size()];
-            for (int i = 0; i < result.length; i++) {
-                result[i] = users.get(i).serialNumber;
-            }
-            return result;
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
+        List<UserInfo> users = getUsers(excludeDying);
+        long[] result = new long[users.size()];
+        for (int i = 0; i < result.length; i++) {
+            result[i] = users.get(i).serialNumber;
         }
+        return result;
     }
 
     /**
@@ -2765,11 +2805,8 @@
      */
     @UnsupportedAppUsage
     public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
-        try {
-            return mService.getUsers(excludeDying);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
+        return getUsers(/*excludePartial= */ true, excludeDying,
+                /* excludePreCreated= */ true);
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 79b6ebc..ee36dfe 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7855,6 +7855,19 @@
                 NON_NEGATIVE_INTEGER_VALIDATOR;
 
         /**
+         * Number of successful "Motion Sense" tap gestures to pause media.
+         * @hide
+         */
+        public static final String AWARE_TAP_PAUSE_GESTURE_COUNT = "aware_tap_pause_gesture_count";
+
+        /**
+         * Number of touch interactions to pause media when a "Motion Sense" gesture could
+         * have been used.
+         * @hide
+         */
+        public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count";
+
+        /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
@@ -9030,8 +9043,6 @@
             DOZE_PICK_UP_GESTURE,
             DOZE_DOUBLE_TAP_GESTURE,
             DOZE_TAP_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,
@@ -9039,9 +9050,6 @@
             FACE_UNLOCK_DISMISSES_KEYGUARD,
             FACE_UNLOCK_APP_ENABLED,
             FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
-            ASSIST_GESTURE_ENABLED,
-            ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
-            ASSIST_GESTURE_WAKE_ENABLED,
             VR_DISPLAY_MODE,
             NOTIFICATION_BADGING,
             NOTIFICATION_DISMISS_RTL,
@@ -9074,12 +9082,9 @@
             TRUST_AGENTS_EXTEND_UNLOCK,
             UI_NIGHT_MODE,
             LOCK_SCREEN_WHEN_TRUST_LOST,
-            SKIP_GESTURE,
             SKIP_DIRECTION,
-            SILENCE_GESTURE,
             THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
             NAVIGATION_MODE,
-            AWARE_ENABLED,
             SKIP_GESTURE_COUNT,
             SKIP_TOUCH_COUNT,
             SILENCE_ALARMS_GESTURE_COUNT,
@@ -9090,7 +9095,9 @@
             SILENCE_TIMER_TOUCH_COUNT,
             DARK_MODE_DIALOG_SEEN,
             GLOBAL_ACTIONS_PANEL_ENABLED,
-            AWARE_LOCK_ENABLED
+            AWARE_LOCK_ENABLED,
+            AWARE_TAP_PAUSE_GESTURE_COUNT,
+            AWARE_TAP_PAUSE_TOUCH_COUNT
         };
 
         /**
@@ -9285,6 +9292,8 @@
             VALIDATORS.put(UI_NIGHT_MODE, UI_NIGHT_MODE_VALIDATOR);
             VALIDATORS.put(GLOBAL_ACTIONS_PANEL_ENABLED, GLOBAL_ACTIONS_PANEL_ENABLED_VALIDATOR);
             VALIDATORS.put(AWARE_LOCK_ENABLED, AWARE_LOCK_ENABLED_VALIDATOR);
+            VALIDATORS.put(AWARE_TAP_PAUSE_GESTURE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
+            VALIDATORS.put(AWARE_TAP_PAUSE_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
         }
 
         /**
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8a1fd62..c67fca5 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -870,6 +870,94 @@
         return null;
     }
 
+    private static class StreamingPictureCallbackHandler implements AutoCloseable,
+            HardwareRenderer.PictureCapturedCallback, Runnable {
+        private final HardwareRenderer mRenderer;
+        private final Callable<OutputStream> mCallback;
+        private final Executor mExecutor;
+        private final ReentrantLock mLock = new ReentrantLock(false);
+        private final ArrayDeque<byte[]> mQueue = new ArrayDeque<>(3);
+        private final ByteArrayOutputStream mByteStream = new ByteArrayOutputStream();
+        private boolean mStopListening;
+        private Thread mRenderThread;
+
+        private StreamingPictureCallbackHandler(HardwareRenderer renderer,
+                Callable<OutputStream> callback, Executor executor) {
+            mRenderer = renderer;
+            mCallback = callback;
+            mExecutor = executor;
+            mRenderer.setPictureCaptureCallback(this);
+        }
+
+        @Override
+        public void close() {
+            mLock.lock();
+            mStopListening = true;
+            mLock.unlock();
+            mRenderer.setPictureCaptureCallback(null);
+        }
+
+        @Override
+        public void onPictureCaptured(Picture picture) {
+            mLock.lock();
+            if (mStopListening) {
+                mLock.unlock();
+                mRenderer.setPictureCaptureCallback(null);
+                return;
+            }
+            if (mRenderThread == null) {
+                mRenderThread = Thread.currentThread();
+            }
+            boolean needsInvoke = true;
+            if (mQueue.size() == 3) {
+                mQueue.removeLast();
+                needsInvoke = false;
+            }
+            picture.writeToStream(mByteStream);
+            mQueue.add(mByteStream.toByteArray());
+            mByteStream.reset();
+            mLock.unlock();
+
+            if (needsInvoke) {
+                mExecutor.execute(this);
+            }
+        }
+
+        @Override
+        public void run() {
+            mLock.lock();
+            final byte[] picture = mQueue.poll();
+            final boolean isStopped = mStopListening;
+            mLock.unlock();
+            if (Thread.currentThread() == mRenderThread) {
+                close();
+                throw new IllegalStateException(
+                        "ViewDebug#startRenderingCommandsCapture must be given an executor that "
+                        + "invokes asynchronously");
+            }
+            if (isStopped) {
+                return;
+            }
+            OutputStream stream = null;
+            try {
+                stream = mCallback.call();
+            } catch (Exception ex) {
+                Log.w("ViewDebug", "Aborting rendering commands capture "
+                        + "because callback threw exception", ex);
+            }
+            if (stream != null) {
+                try {
+                    stream.write(picture);
+                } catch (IOException ex) {
+                    Log.w("ViewDebug", "Aborting rendering commands capture "
+                            + "due to IOException writing to output stream", ex);
+                }
+            } else {
+                close();
+            }
+        }
+    }
+
     /**
      * Begins capturing the entire rendering commands for the view tree referenced by the given
      * view. The view passed may be any View in the tree as long as it is attached. That is,
@@ -915,18 +1003,7 @@
         }
         final HardwareRenderer renderer = attachInfo.mThreadedRenderer;
         if (renderer != null) {
-            return new PictureCallbackHandler(renderer, (picture -> {
-                try {
-                    OutputStream stream = callback.call();
-                    if (stream != null) {
-                        picture.writeToStream(stream);
-                        return true;
-                    }
-                } catch (Exception ex) {
-                    // fall through
-                }
-                return false;
-            }), executor);
+            return new StreamingPictureCallbackHandler(renderer, callback, executor);
         }
         return null;
     }
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 23d1237..3824c22 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -22,7 +22,10 @@
 
 /**
  * Manages the cookies used by an application's {@link WebView} instances.
- * Cookies are manipulated according to RFC2109.
+ * <p>
+ * CookieManager represents cookies as strings in the same format as the
+ * HTTP {@code Cookie} and {@code Set-Cookie} header fields (defined in
+ * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>).
  */
 public abstract class CookieManager {
     /**
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 4db6308..f8522ed 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -71,11 +71,24 @@
     }
 
     /**
-     * Notify the host application that the current page has entered full
-     * screen mode. The host application must show the custom View which
-     * contains the web contents &mdash; video or other HTML content &mdash;
-     * in full screen mode. Also see "Full screen support" documentation on
-     * {@link WebView}.
+     * Notify the host application that the current page has entered full screen mode. After this
+     * call, web content will no longer be rendered in the WebView, but will instead be rendered
+     * in {@code view}. The host application should add this View to a Window which is configured
+     * with {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN} flag in order to
+     * actually display this web content full screen.
+     *
+     * <p>The application may explicitly exit fullscreen mode by invoking {@code callback} (ex. when
+     * the user presses the back button). However, this is generally not necessary as the web page
+     * will often show its own UI to close out of fullscreen. Regardless of how the WebView exits
+     * fullscreen mode, WebView will invoke {@link #onHideCustomView()}, signaling for the
+     * application to remove the custom View.
+     *
+     * <p>If this method is not overridden, WebView will report to the web page it does not support
+     * fullscreen mode and will not honor the web page's request to run in fullscreen mode.
+     *
+     * <p class="note"><b>Note:</b> if overriding this method, the application must also override
+     * {@link #onHideCustomView()}.
+     *
      * @param view is the View object to be shown.
      * @param callback invoke this callback to request the page to exit
      * full screen mode.
@@ -98,10 +111,13 @@
             CustomViewCallback callback) {};
 
     /**
-     * Notify the host application that the current page has exited full
-     * screen mode. The host application must hide the custom View, ie. the
-     * View passed to {@link #onShowCustomView} when the content entered fullscreen.
-     * Also see "Full screen support" documentation on {@link WebView}.
+     * Notify the host application that the current page has exited full screen mode. The host
+     * application must hide the custom View (the View which was previously passed to {@link
+     * #onShowCustomView(View, CustomViewCallback) onShowCustomView()}). After this call, web
+     * content will render in the original WebView again.
+     *
+     * <p class="note"><b>Note:</b> if overriding this method, the application must also override
+     * {@link #onShowCustomView(View, CustomViewCallback) onShowCustomView()}.
      */
     public void onHideCustomView() {}
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 407a85f..068056f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -388,21 +388,24 @@
         mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
                 mSystemWindowInsets.right, 0);
 
-        View emptyView = findViewById(R.id.empty);
-        if (emptyView != null) {
-            emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
-                    + getResources().getDimensionPixelSize(
-                            R.dimen.chooser_edge_margin_normal) * 2);
-        }
-
-        if (mFooterSpacer == null) {
-            mFooterSpacer = new Space(getApplicationContext());
+        // Need extra padding so the list can fully scroll up
+        if (useLayoutWithDefault()) {
+            if (mFooterSpacer == null) {
+                mFooterSpacer = new Space(getApplicationContext());
+            } else {
+                ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+            }
+            mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
+                                                                       mSystemWindowInsets.bottom));
+            ((ListView) mAdapterView).addFooterView(mFooterSpacer);
         } else {
-            ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+            View emptyView = findViewById(R.id.empty);
+            if (emptyView != null) {
+                emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+                                     + getResources().getDimensionPixelSize(
+                                             R.dimen.chooser_edge_margin_normal) * 2);
+            }
         }
-        mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
-                mSystemWindowInsets.bottom));
-        ((ListView) mAdapterView).addFooterView(mFooterSpacer);
 
         resetButtonBar();
 
@@ -561,7 +564,7 @@
                         intent.getData().getHost(),
                         mAdapter.getFilteredItem().getDisplayLabel());
             } else if (mAdapter.areAllTargetsBrowsers()) {
-                dialogTitle =  getString(ActionTitle.BROWSABLE_TITLE_RES);
+                dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
             } else {
                 dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES,
                         intent.getData().getHost());
@@ -1304,6 +1307,7 @@
         // In case this method is called again (due to activity recreation), avoid adding a new
         // header if one is already present.
         if (useHeader && listView != null && listView.getHeaderViewsCount() == 0) {
+            listView.setHeaderDividersEnabled(true);
             listView.addHeaderView(LayoutInflater.from(this).inflate(
                     R.layout.resolver_different_item_header, listView, false));
         }
@@ -1346,11 +1350,13 @@
         final ViewGroup buttonLayout = findViewById(R.id.button_bar);
         if (buttonLayout != null) {
             buttonLayout.setVisibility(View.VISIBLE);
-            int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
-            buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
-                    buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
-                        R.dimen.resolver_button_bar_spacing) + inset);
 
+            if (!useLayoutWithDefault()) {
+                int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
+                buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
+                        buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
+                                R.dimen.resolver_button_bar_spacing) + inset);
+            }
             mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
             mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
 
@@ -2057,7 +2063,9 @@
             CharSequence subLabel = info.getExtendedInfo();
             if (TextUtils.equals(label, subLabel)) subLabel = null;
 
-            if (!TextUtils.equals(holder.text2.getText(), subLabel)) {
+            if (!TextUtils.equals(holder.text2.getText(), subLabel)
+                    && !TextUtils.isEmpty(subLabel)) {
+                holder.text2.setVisibility(View.VISIBLE);
                 holder.text2.setText(subLabel);
             }
 
diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
index 173818b..d443fd8 100644
--- a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
+++ b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
@@ -1,6 +1,7 @@
 #include "ByteBufferStreamAdaptor.h"
 #include "core_jni_helpers.h"
 #include "Utils.h"
+#include <jni.h>
 
 #include <SkStream.h>
 
@@ -9,6 +10,24 @@
 static jmethodID gByteBuffer_getMethodID;
 static jmethodID gByteBuffer_setPositionMethodID;
 
+/**
+ * Helper method for accessing the JNI interface pointer.
+ *
+ * Image decoding (which this supports) is started on a thread that is already
+ * attached to the Java VM. But an AnimatedImageDrawable continues decoding on
+ * the AnimatedImageThread, which is not attached. This will attach if
+ * necessary.
+ */
+static JNIEnv* requireEnv(JavaVM* jvm) {
+    JNIEnv* env;
+    if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+            LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+        }
+    }
+    return env;
+}
+
 class ByteBufferStream : public SkStreamAsset {
 private:
     ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length,
@@ -46,7 +65,7 @@
     }
 
     ~ByteBufferStream() override {
-        auto* env = get_env_or_die(mJvm);
+        auto* env = requireEnv(mJvm);
         env->DeleteGlobalRef(mByteBuffer);
         env->DeleteGlobalRef(mStorage);
     }
@@ -63,7 +82,7 @@
             return this->setPosition(mPosition + size) ? size : 0;
         }
 
-        auto* env = get_env_or_die(mJvm);
+        auto* env = requireEnv(mJvm);
         size_t bytesRead = 0;
         do {
             const size_t requested = (size > kStorageSize) ? kStorageSize : size;
@@ -146,7 +165,7 @@
 
     // Range has already been checked by the caller.
     bool setPosition(size_t newPosition) {
-        auto* env = get_env_or_die(mJvm);
+        auto* env = requireEnv(mJvm);
         env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID,
                               newPosition + mInitialPosition);
         if (env->ExceptionCheck()) {
@@ -185,7 +204,7 @@
     }
 
     ~ByteArrayStream() override {
-        auto* env = get_env_or_die(mJvm);
+        auto* env = requireEnv(mJvm);
         env->DeleteGlobalRef(mByteArray);
     }
 
@@ -197,7 +216,7 @@
             return 0;
         }
 
-        auto* env = get_env_or_die(mJvm);
+        auto* env = requireEnv(mJvm);
         if (buffer) {
             env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size,
                                     reinterpret_cast<jbyte*>(buffer));
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 61799ee..ef413b9 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -211,6 +211,11 @@
         optional SettingProto silence_timer_touch_count = 11 [ (android.privacy).dest =
             DEST_AUTOMATIC ];
         optional SettingProto skip_touch_count = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto aware_tap_pause_gesture_count = 13 [
+            (android.privacy).dest =
+            DEST_AUTOMATIC ];
+        optional SettingProto aware_tap_pause_touch_count = 14 [ (android.privacy).dest =
+            DEST_AUTOMATIC ];
     }
     optional Gesture gesture = 74;
 
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 0bdb25a..4857095 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -22,8 +22,6 @@
               android:layout_height="wrap_content"
               android:layout_width="match_parent"
               android:minHeight="?attr/listPreferredItemHeightSmall"
-              android:paddingTop="4dp"
-              android:paddingBottom="4dp"
               android:background="?attr/activatedBackgroundIndicator">
 
     <!-- Activity icon when presenting dialog
@@ -32,7 +30,8 @@
                android:layout_width="@dimen/resolver_icon_size"
                android:layout_height="@dimen/resolver_icon_size"
                android:layout_gravity="start|center_vertical"
-               android:layout_marginStart="?attr/listPreferredItemPaddingStart"
+               android:layout_marginStart="@dimen/resolver_icon_margin"
+               android:layout_marginEnd="@dimen/resolver_icon_margin"
                android:layout_marginTop="12dp"
                android:layout_marginBottom="12dp"
                android:scaleType="fitCenter" />
@@ -40,8 +39,7 @@
     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:gravity="start|center_vertical"
               android:orientation="vertical"
-              android:paddingStart="?attr/listPreferredItemPaddingStart"
-              android:paddingEnd="?attr/listPreferredItemPaddingEnd"
+              android:paddingEnd="@dimen/resolver_edge_margin"
               android:layout_height="wrap_content"
               android:layout_width="wrap_content"
               android:layout_gravity="start|center_vertical">
@@ -49,14 +47,20 @@
         <TextView android:id="@android:id/text1"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
-                  android:textAppearance="?attr/textAppearanceMedium"
-                  android:textColor="?attr/textColorPrimary"
+                  android:layout_gravity="start|center_vertical"
+                  android:textColor="?android:attr/textColorPrimary"
+                  android:fontFamily="@android:string/config_bodyFontFamily"
+                  android:textSize="16sp"
                   android:minLines="1"
                   android:maxLines="1"
                   android:ellipsize="marquee" />
         <!-- Extended activity info to distinguish between duplicate activity names -->
         <TextView android:id="@android:id/text2"
-                  android:textAppearance="?android:attr/textAppearanceSmall"
+                  android:textColor="?android:attr/textColorSecondary"
+                  android:fontFamily="@android:string/config_bodyFontFamily"
+                  android:layout_gravity="start|center_vertical"
+                  android:textSize="14sp"
+                  android:visibility="gone"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:minLines="1"
diff --git a/core/res/res/layout/resolver_different_item_header.xml b/core/res/res/layout/resolver_different_item_header.xml
index 7d9ffd7..0a35edc 100644
--- a/core/res/res/layout/resolver_different_item_header.xml
+++ b/core/res/res/layout/resolver_different_item_header.xml
@@ -22,12 +22,12 @@
     android:layout_height="wrap_content"
     android:layout_alwaysShow="true"
     android:text="@string/use_a_different_app"
-    android:minHeight="56dp"
-    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:textColor="?android:attr/textColorPrimary"
+    android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+    android:textSize="16sp"
     android:gravity="start|center_vertical"
-    android:paddingStart="16dp"
-    android:paddingEnd="16dp"
-    android:paddingTop="8dp"
-    android:paddingBottom="8dp"
-    android:elevation="8dp"
-    />
+    android:paddingStart="@dimen/resolver_edge_margin"
+    android:paddingEnd="@dimen/resolver_edge_margin"
+    android:paddingTop="@dimen/resolver_small_margin"
+    android:paddingBottom="@dimen/resolver_edge_margin"
+    android:elevation="1dp" />
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 1dd4207..6e45e7a 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -29,16 +29,18 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alwaysShow="true"
-        android:elevation="8dp"
-        android:background="?attr/colorBackgroundFloating">
+        android:elevation="@dimen/resolver_elevation"
+        android:paddingTop="@dimen/resolver_small_margin"
+        android:paddingStart="@dimen/resolver_edge_margin"
+        android:paddingEnd="@dimen/resolver_edge_margin"
+        android:paddingBottom="@dimen/resolver_edge_margin"
+        android:background="@drawable/bottomsheet_background">
 
         <TextView
             android:id="@+id/profile_button"
             android:layout_width="wrap_content"
             android:layout_height="48dp"
             android:layout_marginEnd="8dp"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
             android:visibility="gone"
             style="?attr/borderlessButtonStyle"
             android:textAppearance="?attr/textAppearanceButton"
@@ -50,36 +52,49 @@
 
         <TextView
             android:id="@+id/title"
-            android:layout_width="wrap_content"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="56dp"
-            android:textAppearance="?attr/textAppearanceMedium"
-            android:gravity="start|center_vertical"
-            android:paddingStart="?attr/dialogPreferredPadding"
-            android:paddingEnd="?attr/dialogPreferredPadding"
-            android:paddingTop="8dp"
             android:layout_below="@id/profile_button"
             android:layout_alignParentStart="true"
-            android:paddingBottom="8dp" />
+            android:textColor="?android:attr/textColorPrimary"
+            android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+            android:textSize="16sp"
+            android:gravity="start|center_vertical" />
     </RelativeLayout>
 
+    <View
+        android:layout_alwaysShow="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/colorBackgroundFloating"
+        android:foreground="?attr/dividerVertical" />
     <ListView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:id="@+id/resolver_list"
         android:clipToPadding="false"
-        android:scrollbarStyle="outsideOverlay"
         android:background="?attr/colorBackgroundFloating"
-        android:elevation="8dp"
+        android:elevation="@dimen/resolver_elevation"
         android:nestedScrollingEnabled="true"
+        android:scrollbarStyle="outsideOverlay"
         android:scrollIndicators="top|bottom"
-        android:divider="@null" />
+        android:divider="?attr/dividerVertical"
+        android:footerDividersEnabled="false"
+        android:headerDividersEnabled="false"
+        android:dividerHeight="1dp" />
+    <View
+        android:layout_alwaysShow="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/colorBackgroundFloating"
+        android:foreground="?attr/dividerVertical" />
+
 
     <TextView android:id="@+id/empty"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:background="?attr/colorBackgroundFloating"
-              android:elevation="8dp"
+              android:elevation="@dimen/resolver_elevation"
               android:layout_alwaysShow="true"
               android:text="@string/noApplications"
               android:padding="32dp"
@@ -102,18 +117,19 @@
         android:background="?attr/colorBackgroundFloating"
         android:paddingTop="@dimen/resolver_button_bar_spacing"
         android:paddingBottom="@dimen/resolver_button_bar_spacing"
-        android:paddingStart="12dp"
-        android:paddingEnd="12dp"
-        android:elevation="8dp">
+        android:paddingStart="@dimen/resolver_edge_margin"
+        android:paddingEnd="@dimen/resolver_small_margin"
+        android:elevation="@dimen/resolver_elevation">
 
         <Button
             android:id="@+id/button_once"
             android:layout_width="wrap_content"
             android:layout_gravity="start"
             android:maxLines="2"
-            style="?attr/buttonBarNegativeButtonStyle"
-            android:minHeight="@dimen/alert_dialog_button_bar_height"
+            style="?attr/buttonBarButtonStyle"
+            android:fontFamily="@android:string/config_headlineFontFamilyMedium"
             android:layout_height="wrap_content"
+            android:textAllCaps="false"
             android:enabled="false"
             android:text="@string/activity_resolver_use_once"
             android:onClick="onButtonClick" />
@@ -123,8 +139,9 @@
             android:layout_width="wrap_content"
             android:layout_gravity="end"
             android:maxLines="2"
-            android:minHeight="@dimen/alert_dialog_button_bar_height"
-            style="?attr/buttonBarPositiveButtonStyle"
+            style="?attr/buttonBarButtonStyle"
+            android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+            android:textAllCaps="false"
             android:layout_height="wrap_content"
             android:enabled="false"
             android:text="@string/activity_resolver_use_always"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 740a7eb..dbba0b7 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -29,22 +29,22 @@
         android:layout_height="wrap_content"
         android:layout_alwaysShow="true"
         android:orientation="vertical"
-        android:background="?attr/colorBackgroundFloating"
-        android:elevation="8dp">
+        android:background="@drawable/bottomsheet_background"
+        android:paddingTop="@dimen/resolver_small_margin"
+        android:elevation="@dimen/resolver_elevation">
 
         <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="64dp"
-            android:orientation="horizontal">
-
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:paddingBottom="@dimen/resolver_edge_margin"
+            android:paddingEnd="@dimen/resolver_edge_margin">
             <ImageView
                 android:id="@+id/icon"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
+                android:layout_width="@dimen/resolver_icon_size"
+                android:layout_height="@dimen/resolver_icon_size"
                 android:layout_gravity="start|top"
-                android:layout_marginStart="16dp"
-                android:layout_marginEnd="16dp"
-                android:layout_marginTop="20dp"
+                android:layout_marginStart="@dimen/resolver_icon_margin"
                 android:src="@drawable/resolver_icon_placeholder"
                 android:scaleType="fitCenter" />
 
@@ -52,9 +52,11 @@
                 android:id="@+id/title"
                 android:layout_width="0dp"
                 android:layout_weight="1"
-                android:layout_height="?attr/listPreferredItemHeight"
-                android:layout_marginStart="16dp"
-                android:textAppearance="?attr/textAppearanceMedium"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="@dimen/resolver_icon_margin"
+                android:textColor="?android:attr/textColorPrimary"
+                android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+                android:textSize="16sp"
                 android:gravity="start|center_vertical"
                 android:paddingEnd="16dp" />
 
@@ -107,21 +109,22 @@
             android:orientation="horizontal"
             android:layoutDirection="locale"
             android:measureWithLargestChild="true"
-            android:paddingTop="8dp"
-            android:paddingBottom="8dp"
-            android:paddingStart="12dp"
-            android:paddingEnd="12dp"
-            android:elevation="8dp">
+            android:paddingTop="@dimen/resolver_button_bar_spacing"
+            android:paddingBottom="@dimen/resolver_button_bar_spacing"
+            android:paddingStart="@dimen/resolver_edge_margin"
+            android:paddingEnd="@dimen/resolver_small_margin"
+            android:elevation="@dimen/resolver_elevation">
 
             <Button
                 android:id="@+id/button_once"
                 android:layout_width="wrap_content"
                 android:layout_gravity="start"
                 android:maxLines="2"
-                style="?attr/buttonBarNegativeButtonStyle"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
+                style="?attr/buttonBarButtonStyle"
+                android:fontFamily="@android:string/config_headlineFontFamilyMedium"
                 android:layout_height="wrap_content"
                 android:enabled="false"
+                android:textAllCaps="false"
                 android:text="@string/activity_resolver_use_once"
                 android:onClick="onButtonClick" />
 
@@ -130,29 +133,40 @@
                 android:layout_width="wrap_content"
                 android:layout_gravity="end"
                 android:maxLines="2"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                style="?attr/buttonBarPositiveButtonStyle"
+                style="?attr/buttonBarButtonStyle"
+                android:fontFamily="@android:string/config_headlineFontFamilyMedium"
                 android:layout_height="wrap_content"
                 android:enabled="false"
+                android:textAllCaps="false"
                 android:text="@string/activity_resolver_use_always"
                 android:onClick="onButtonClick" />
         </LinearLayout>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:background="?attr/dividerVertical" />
     </LinearLayout>
 
+    <View
+        android:layout_alwaysShow="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/colorBackgroundFloating"
+        android:foreground="?attr/dividerVertical" />
     <ListView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:id="@+id/resolver_list"
         android:clipToPadding="false"
-        android:scrollbarStyle="outsideOverlay"
         android:background="?attr/colorBackgroundFloating"
-        android:elevation="8dp"
+        android:elevation="@dimen/resolver_elevation"
         android:nestedScrollingEnabled="true"
-        android:divider="@null" />
-
+        android:scrollbarStyle="outsideOverlay"
+        android:scrollIndicators="top|bottom"
+        android:divider="?attr/dividerVertical"
+        android:footerDividersEnabled="false"
+        android:headerDividersEnabled="false"
+        android:dividerHeight="1dp" />
+    <View
+        android:layout_alwaysShow="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/colorBackgroundFloating"
+        android:foreground="?attr/dividerVertical" />
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 77fca8f..c8e835b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -349,10 +349,10 @@
          will be given a single shared user ID, so they can for example run
          in the same process.  Note that for them to actually get the same
          user ID, they must also be signed with the same signature.
-         @deprecated Shared user id's cause non-deterministic behaviour within the
-         package manager. As such, it's use is discouraged, deprecated, and will
-         be removed altogether in a future version of Android. Instead, proper
-         communication mechanisms such as services and providers should be used
+         @deprecated Shared user IDs cause non-deterministic behavior within the
+         package manager. As such, its use is strongly discouraged and may be
+         removed in a future version of Android. Instead, apps should use proper
+         communication mechanisms, such as services and content providers,
          to facilitate interoperability between shared components. -->
     <attr name="sharedUserId" format="string" />
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 609659b..a01bbe3 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -750,7 +750,7 @@
 
     <dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen>
 
-    <!-- chooser (sharesheet) spacing -->
+    <!-- chooser/resolver (sharesheet) spacing -->
     <dimen name="chooser_corner_radius">8dp</dimen>
     <dimen name="chooser_row_text_option_translate">25dp</dimen>
     <dimen name="chooser_view_spacing">18dp</dimen>
@@ -759,11 +759,15 @@
     <dimen name="chooser_preview_image_font_size">20sp</dimen>
     <dimen name="chooser_preview_image_border">1dp</dimen>
     <dimen name="chooser_preview_width">-1px</dimen>
-    <dimen name="resolver_icon_size">42dp</dimen>
-    <dimen name="resolver_button_bar_spacing">8dp</dimen>
-    <dimen name="resolver_badge_size">18dp</dimen>
     <dimen name="chooser_target_width">90dp</dimen>
     <dimen name="chooser_header_scroll_elevation">4dp</dimen>
     <dimen name="chooser_max_collapsed_height">288dp</dimen>
     <dimen name="chooser_direct_share_label_placeholder_max_width">72dp</dimen>
+    <dimen name="resolver_icon_size">32dp</dimen>
+    <dimen name="resolver_button_bar_spacing">8dp</dimen>
+    <dimen name="resolver_badge_size">18dp</dimen>
+    <dimen name="resolver_icon_margin">16dp</dimen>
+    <dimen name="resolver_small_margin">18dp</dimen>
+    <dimen name="resolver_edge_margin">24dp</dimen>
+    <dimen name="resolver_elevation">1dp</dimen>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 33895a5..4f5b777 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3816,6 +3816,10 @@
   <java-symbol type="dimen" name="resolver_icon_size"/>
   <java-symbol type="dimen" name="resolver_badge_size"/>
   <java-symbol type="dimen" name="resolver_button_bar_spacing"/>
+  <java-symbol type="dimen" name="resolver_icon_margin"/>
+  <java-symbol type="dimen" name="resolver_small_margin"/>
+  <java-symbol type="dimen" name="resolver_edge_margin"/>
+  <java-symbol type="dimen" name="resolver_elevation"/>
 
   <!-- For DropBox -->
   <java-symbol type="integer" name="config_dropboxLowPriorityBroadcastRateLimitPeriod" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index e60e555..bdb58f6 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -595,7 +595,10 @@
                  Settings.Secure.ANR_SHOW_BACKGROUND,
                  Settings.Secure.ASSISTANT,
                  Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
+                 Settings.Secure.ASSIST_GESTURE_ENABLED,
                  Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
+                 Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
+                 Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
                  Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
                  Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
                  Settings.Secure.ASSIST_STRUCTURE_ENABLED,
@@ -723,6 +726,11 @@
                  Settings.Secure.BIOMETRIC_DEBUG_ENABLED,
                  Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
                  Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED,
+                 Settings.Secure.AWARE_ENABLED,
+                 Settings.Secure.SKIP_GESTURE,
+                 Settings.Secure.SILENCE_GESTURE,
+                 Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
+                 Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
                  Settings.Secure.FACE_UNLOCK_RE_ENROLL);
 
     @Test
diff --git a/media/Android.bp b/media/Android.bp
index a768b81..2f75e44 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -33,6 +33,8 @@
         "framework_media_annotation",
         "android_system_stubs_current",
     ],
+
+    plugins: ["java_api_finder"],
 }
 
 filegroup {
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index f813d1b..7bc2b31 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -192,13 +192,15 @@
 
         mMaxImages = maxImages;
 
-        if (format == ImageFormat.UNKNOWN) {
-            format = SurfaceUtils.getSurfaceFormat(surface);
-        }
         // Note that the underlying BufferQueue is working in synchronous mode
         // to avoid dropping any buffers.
         mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format);
 
+        // nativeInit internally overrides UNKNOWN format. So does surface format query after
+        // nativeInit and before getEstimatedNativeAllocBytes().
+        if (format == ImageFormat.UNKNOWN) {
+            format = SurfaceUtils.getSurfaceFormat(surface);
+        }
         // Estimate the native buffer allocation size and register it so it gets accounted for
         // during GC. Note that this doesn't include the buffers required by the buffer queue
         // itself and the buffers requested by the producer.
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..8247211
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/car_top_bar"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/system_bar_background"
+    android:orientation="vertical">
+
+  <RelativeLayout
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_weight="1">
+
+    <FrameLayout
+        android:id="@+id/left_hvac_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentStart="true"
+        >
+
+      <com.android.systemui.statusbar.car.CarNavigationButton
+          android:id="@+id/hvacleft"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@null"
+          systemui:broadcast="true"
+          systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+          />
+
+      <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+          android:id="@+id/lefttext"
+          android:layout_width="wrap_content"
+          android:layout_height="match_parent"
+          android:paddingStart="@*android:dimen/car_padding_4"
+          android:paddingEnd="16dp"
+          android:gravity="center_vertical|start"
+          android:minEms="4"
+          android:textAppearance="@style/TextAppearance.CarStatus"
+          systemui:hvacAreaId="49"
+          systemui:hvacMaxText="@string/hvac_max_text"
+          systemui:hvacMaxValue="@dimen/hvac_max_value"
+          systemui:hvacMinText="@string/hvac_min_text"
+          systemui:hvacMinValue="@dimen/hvac_min_value"
+          systemui:hvacPivotOffset="60dp"
+          systemui:hvacPropertyId="358614275"
+          systemui:hvacTempFormat="%.0f\u00B0"
+          />
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/clock_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_centerInParent="true">
+      <com.android.systemui.statusbar.car.CarNavigationButton
+          android:id="@+id/qs"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@null"/>
+      <com.android.systemui.statusbar.policy.Clock
+          android:id="@+id/clock"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_gravity="center"
+          android:elevation="5dp"
+          android:singleLine="true"
+          android:textAppearance="@style/TextAppearance.StatusBar.Clock"/>
+    </FrameLayout>
+
+    <LinearLayout
+        android:id="@+id/system_icon_area"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_centerHorizontal="true"
+        android:layout_centerVertical="true"
+        android:layout_toEndOf="@+id/clock_container"
+        android:paddingStart="@*android:dimen/car_padding_1"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        >
+
+      <include
+          layout="@layout/system_icons"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_weight="1"
+          android:paddingStart="4dp"
+          android:gravity="center_vertical"
+          />
+    </LinearLayout>
+
+    <FrameLayout
+        android:id="@+id/right_hvac_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentEnd="true"
+        >
+
+      <com.android.systemui.statusbar.car.CarNavigationButton
+          android:id="@+id/hvacright"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@null"
+          systemui:broadcast="true"
+          systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+          />
+
+      <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+          android:id="@+id/righttext"
+          android:layout_width="wrap_content"
+          android:layout_height="match_parent"
+          android:paddingStart="16dp"
+          android:paddingEnd="@*android:dimen/car_padding_4"
+          android:gravity="center_vertical|end"
+          android:minEms="4"
+          android:textAppearance="@style/TextAppearance.CarStatus"
+          systemui:hvacAreaId="68"
+          systemui:hvacMaxText="@string/hvac_max_text"
+          systemui:hvacMaxValue="@dimen/hvac_max_value"
+          systemui:hvacMinText="@string/hvac_min_text"
+          systemui:hvacMinValue="@dimen/hvac_min_value"
+          systemui:hvacPivotOffset="60dp"
+          systemui:hvacPropertyId="358614275"
+          systemui:hvacTempFormat="%.0f\u00B0"
+          />
+    </FrameLayout>
+  </RelativeLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index e1bcc2e..0b34626 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -69,10 +69,10 @@
             android:visibility="gone"
         />
 
-        <include layout="@layout/car_top_navigation_bar"
+        <FrameLayout
+            android:id="@+id/car_top_navigation_bar_container"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-        />
+            android:layout_height="wrap_content"/>
     </LinearLayout>
 
     <include layout="@layout/brightness_mirror"/>
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 467c4a4..cbf2287 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -40,4 +40,19 @@
          slots that may be reused for things like IME control. -->
     <integer name="config_maxNotificationIcons">0</integer>
 
+    <!--
+        Initial alpha percent value for the background when the notification
+        shade is open. Should be a number between, and inclusive, 0 and 100.
+        If the number is 0, then the background alpha starts off fully
+        transparent. If the number if 100, then the background alpha starts off
+        fully opaque. -->
+    <integer name="config_initialNotificationBackgroundAlpha">0</integer>
+    <!--
+        Final alpha percent value for the background when the notification
+        shade is fully open. Should be a number between, and inclusive, 0 and
+        100. If this value is smaller than
+        config_initialNotificationBackgroundAlpha, the background will default
+        to a constant alpha percent value using the initial alpha. -->
+    <integer name="config_finalNotificationBackgroundAlpha">100</integer>
+
 </resources>
diff --git a/packages/CarSystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
index fb67b30..d6c16cb 100644
--- a/packages/CarSystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -34,4 +34,7 @@
     <!-- The delay before the unlock dialog pops up -->
     <integer name="unlock_dialog_delay_ms">0</integer>
 
+    <!-- Timeout values in milliseconds for displaying volume dialog-->
+    <integer name="car_volume_dialog_display_normal_timeout">3000</integer>
+    <integer name="car_volume_dialog_display_hovering_timeout">16000</integer>
 </resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
index afd722b..447e579 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -22,6 +22,7 @@
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -34,8 +35,9 @@
     @Inject
     public CarNotificationInterruptionStateProvider(Context context,
             NotificationFilter filter,
-            StatusBarStateController stateController) {
-        super(context, filter, stateController);
+            StatusBarStateController stateController,
+            BatteryController batteryController) {
+        super(context, filter, stateController, batteryController);
     }
 
     @Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index 58f80a4..d79849c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -257,6 +257,11 @@
         return false;
     }
 
+    @Override
+    public boolean isAodPowerSave() {
+        return false;
+    }
+
     private void notifyBatteryLevelChanged() {
         for (int i = 0, size = mChangeCallbacks.size(); i < size; i++) {
             mChangeCallbacks.get(i)
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 25191f6..1a1a8ea 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -26,7 +26,6 @@
 import android.car.drivingstate.CarDrivingStateEvent;
 import android.car.drivingstate.CarUxRestrictionsManager;
 import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
-import android.car.trust.CarTrustAgentEnrollmentManager;
 import android.content.Context;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
@@ -101,6 +100,9 @@
     private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
     private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
 
+    private float mBackgroundAlphaDiff;
+    private float mInitialBackgroundAlpha;
+
     private TaskStackListenerImpl mTaskStackListener;
 
     private FullscreenUserSwitcher mFullscreenUserSwitcher;
@@ -109,9 +111,11 @@
     private BatteryMeterView mBatteryMeterView;
     private Drawable mNotificationPanelBackground;
 
+    private ViewGroup mTopNavigationBarContainer;
     private ViewGroup mNavigationBarWindow;
     private ViewGroup mLeftNavigationBarWindow;
     private ViewGroup mRightNavigationBarWindow;
+    private CarNavigationBarView mTopNavigationBarView;
     private CarNavigationBarView mNavigationBarView;
     private CarNavigationBarView mLeftNavigationBarView;
     private CarNavigationBarView mRightNavigationBarView;
@@ -123,7 +127,7 @@
     private CarFacetButtonController mCarFacetButtonController;
     private ActivityManagerWrapper mActivityManagerWrapper;
     private DeviceProvisionedController mDeviceProvisionedController;
-    private boolean mDeviceIsProvisioned = true;
+    private boolean mDeviceIsSetUpForUser = true;
     private HvacController mHvacController;
     private DrivingStateHelper mDrivingStateHelper;
     private PowerManagerHelper mPowerManagerHelper;
@@ -144,6 +148,9 @@
     private boolean mNotificationListAtBottom;
     // Was the notification list at the bottom when the user first touched the screen
     private boolean mNotificationListAtBottomAtTimeOfTouch;
+    // To be attached to the top navigation bar (i.e. status bar) to pull down the notification
+    // panel.
+    private View.OnTouchListener mTopNavBarNotificationTouchListener;
     // To be attached to the navigation bars such that they can close the notification panel if
     // it's open.
     private View.OnTouchListener mNavBarNotificationTouchListener;
@@ -174,6 +181,8 @@
     private boolean mHideNavBarForKeyboard;
     private boolean mBottomNavBarVisible;
 
+    private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper;
+
     private final CarPowerStateListener mCarPowerStateListener =
             (int state) -> {
                 // When the car powers on, clear all notifications and mute/unread states.
@@ -193,7 +202,7 @@
         // get the provisioned state before calling the parent class since it's that flow that
         // builds the nav bar
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
-        mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
+        mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup();
 
         // Keyboard related setup, before nav bars are created.
         mHideNavBarForKeyboard = mContext.getResources().getBoolean(
@@ -205,6 +214,29 @@
         mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
         mScreenLifecycle.addObserver(mScreenObserver);
 
+        // Need to initialize HVAC controller before calling super.start - before system bars are
+        // created.
+        mHvacController = new HvacController(mContext);
+
+      	// Notification bar related setup.
+        mInitialBackgroundAlpha = (float) mContext.getResources().getInteger(
+            R.integer.config_initialNotificationBackgroundAlpha) / 100;
+        if (mInitialBackgroundAlpha < 0 || mInitialBackgroundAlpha > 100) {
+            throw new RuntimeException(
+              "Unable to setup notification bar due to incorrect initial background alpha"
+                      + " percentage");
+        }
+        float finalBackgroundAlpha = Math.max(
+            mInitialBackgroundAlpha,
+            (float) mContext.getResources().getInteger(
+                R.integer.config_finalNotificationBackgroundAlpha) / 100);
+        if (finalBackgroundAlpha < 0 || finalBackgroundAlpha > 100) {
+            throw new RuntimeException(
+              "Unable to setup notification bar due to incorrect final background alpha"
+                      + " percentage");
+        }
+        mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha;
+
         super.start();
         mTaskStackListener = new TaskStackListenerImpl();
         mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
@@ -223,25 +255,22 @@
 
         mHvacController.connectToCarService();
 
-        CarSystemUIFactory factory = SystemUIFactory.getInstance();
-        if (!mDeviceIsProvisioned) {
-            mDeviceProvisionedController.addCallback(
-                    new DeviceProvisionedController.DeviceProvisionedListener() {
-                        @Override
-                        public void onDeviceProvisionedChanged() {
-                            mHandler.post(() -> {
-                                // on initial boot we are getting a call even though the value
-                                // is the same so we are confirming the reset is needed
-                                boolean deviceProvisioned =
-                                        mDeviceProvisionedController.isDeviceProvisioned();
-                                if (mDeviceIsProvisioned != deviceProvisioned) {
-                                    mDeviceIsProvisioned = deviceProvisioned;
-                                    restartNavBars();
-                                }
-                            });
-                        }
-                    });
-        }
+        mDeviceProvisionedController.addCallback(
+                new DeviceProvisionedController.DeviceProvisionedListener() {
+                    @Override
+                    public void onUserSetupChanged() {
+                        mHandler.post(() -> restartNavBarsIfNecessary());
+                    }
+
+                    @Override
+                    public void onUserSwitched() {
+                        mHandler.post(() -> restartNavBarsIfNecessary());
+                    }
+                });
+
+        // Used by onDrivingStateChanged and it can be called inside
+        // DrivingStateHelper.connectToCarService()
+        mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
 
         // Register a listener for driving state changes.
         mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
@@ -249,8 +278,14 @@
 
         mPowerManagerHelper = new PowerManagerHelper(mContext, mCarPowerStateListener);
         mPowerManagerHelper.connectToCarService();
+    }
 
-        mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
+    private void restartNavBarsIfNecessary() {
+        boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+        if (mDeviceIsSetUpForUser != currentUserSetup) {
+            mDeviceIsSetUpForUser = currentUserSetup;
+            restartNavBars();
+        }
     }
 
     /**
@@ -261,8 +296,8 @@
         // remove and reattach all hvac components such that we don't keep a reference to unused
         // ui elements
         mHvacController.removeAllComponents();
-        addTemperatureViewToController(mStatusBarWindow);
         mCarFacetButtonController.removeAll();
+
         if (mNavigationBarWindow != null) {
             mNavigationBarWindow.removeAllViews();
             mNavigationBarView = null;
@@ -356,7 +391,6 @@
     @Override
     protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
         super.makeStatusBarView(result);
-        mHvacController = new HvacController(mContext);
 
         CarSystemUIFactory factory = SystemUIFactory.getInstance();
         mCarFacetButtonController = factory.getCarDependencyComponent()
@@ -381,7 +415,8 @@
      * touch listeners needed for opening and closing the notification panel
      */
     private void connectNotificationsUI() {
-        // Attached to the status bar to detect pull down of the notification shade.
+        // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
+        // notification shade.
         GestureDetector openGestureDetector = new GestureDetector(mContext,
                 new OpenNotificationGestureListener() {
                     @Override
@@ -414,6 +449,18 @@
         GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
                 new HandleBarCloseNotificationGestureListener());
 
+        mTopNavBarNotificationTouchListener = (v, event) -> {
+            if (!mDeviceIsSetUpForUser) {
+                return true;
+            }
+            boolean consumed = openGestureDetector.onTouchEvent(event);
+            if (consumed) {
+                return true;
+            }
+            maybeCompleteAnimation(event);
+            return true;
+        };
+
         mNavBarNotificationTouchListener =
                 (v, event) -> {
                     boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
@@ -424,21 +471,6 @@
                     return true;
                 };
 
-        // The following are the ui elements that the user would call the status bar.
-        // This will set the status bar so it they can make call backs.
-        CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
-        topBar.setStatusBar(this);
-        topBar.setStatusBarWindowTouchListener((v1, event1) -> {
-
-                    boolean consumed = openGestureDetector.onTouchEvent(event1);
-                    if (consumed) {
-                        return true;
-                    }
-                    maybeCompleteAnimation(event1);
-                    return true;
-                }
-        );
-
         mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(
                 mBarService,
                 launchResult -> {
@@ -447,13 +479,23 @@
                         animateCollapsePanels();
                     }
                 });
-        Car car = Car.createCar(mContext);
-        CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager)
-                car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+
         CarNotificationListener carNotificationListener = new CarNotificationListener();
-        CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
-                new CarUxRestrictionManagerWrapper();
-        carUxRestrictionManagerWrapper.setCarUxRestrictionsManager(carUxRestrictionsManager);
+        mCarUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper();
+        // This can take time if car service is not ready up to this time.
+        // TODO(b/142808072) Refactor CarUxRestrictionManagerWrapper to allow setting
+        // CarUxRestrictionsManager later and switch to Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT.
+        Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
+                (car, ready) -> {
+                    if (!ready) {
+                        return;
+                    }
+                    CarUxRestrictionsManager carUxRestrictionsManager =
+                            (CarUxRestrictionsManager)
+                                    car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+                    mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager(
+                            carUxRestrictionsManager);
+                });
 
         mNotificationDataManager = new NotificationDataManager();
         mNotificationDataManager.setOnUnseenCountUpdateListener(
@@ -482,7 +524,7 @@
                         mNotificationClickHandlerFactory, mNotificationDataManager);
         mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
 
-        carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
+        carNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper,
                 carHeadsUpNotificationManager, mNotificationDataManager);
 
         mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
@@ -582,7 +624,7 @@
                 mNotificationView,
                 PreprocessingManager.getInstance(mContext),
                 carNotificationListener,
-                carUxRestrictionManagerWrapper,
+                mCarUxRestrictionManagerWrapper,
                 mNotificationDataManager);
         mNotificationViewController.enable();
     }
@@ -745,23 +787,30 @@
     }
 
     private void buildNavBarContent() {
+        // Always build top bar.
+        buildTopBar((mDeviceIsSetUpForUser) ? R.layout.car_top_navigation_bar :
+                R.layout.car_top_navigation_bar_unprovisioned);
+
         if (mShowBottom) {
-            buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
+            buildBottomBar((mDeviceIsSetUpForUser) ? R.layout.car_navigation_bar :
                     R.layout.car_navigation_bar_unprovisioned);
         }
 
         if (mShowLeft) {
-            buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
+            buildLeft((mDeviceIsSetUpForUser) ? R.layout.car_left_navigation_bar :
                     R.layout.car_left_navigation_bar_unprovisioned);
         }
 
         if (mShowRight) {
-            buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
+            buildRight((mDeviceIsSetUpForUser) ? R.layout.car_right_navigation_bar :
                     R.layout.car_right_navigation_bar_unprovisioned);
         }
     }
 
     private void buildNavBarWindows() {
+        mTopNavigationBarContainer = mStatusBarWindow
+            .findViewById(R.id.car_top_navigation_bar_container);
+
         if (mShowBottom) {
             mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
                     R.layout.navigation_bar_window, null);
@@ -794,15 +843,25 @@
         }
 
         boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
-        if (!isKeyboardVisible) {
-            attachBottomNavBarWindow();
-        } else {
-            detachBottomNavBarWindow();
-        }
+        showBottomNavBarWindow(isKeyboardVisible);
     }
 
     private void attachNavBarWindows() {
-        attachBottomNavBarWindow();
+        if (mShowBottom && !mBottomNavBarVisible) {
+            mBottomNavBarVisible = true;
+
+            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                            | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                    PixelFormat.TRANSLUCENT);
+            lp.setTitle("CarNavigationBar");
+            lp.windowAnimations = 0;
+            mWindowManager.addView(mNavigationBarWindow, lp);
+        }
 
         if (mShowLeft) {
             int width = mContext.getResources().getDimensionPixelSize(
@@ -840,47 +899,32 @@
         }
     }
 
-    /**
-     * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of
-     * attaching the bottom nav bar.
-     */
-    protected void attachBottomNavBarWindow() {
+    private void showBottomNavBarWindow(boolean isKeyboardVisible) {
         if (!mShowBottom) {
             return;
         }
 
-        if (mBottomNavBarVisible) {
+        // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do
+        // nothing. Same with if keyboard is not visible and bottom nav bar is visible.
+        if (isKeyboardVisible ^ mBottomNavBarVisible) {
             return;
         }
-        mBottomNavBarVisible = true;
 
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.TRANSLUCENT);
-        lp.setTitle("CarNavigationBar");
-        lp.windowAnimations = 0;
-        mWindowManager.addView(mNavigationBarWindow, lp);
+        mNavigationBarWindow.setVisibility(isKeyboardVisible ? View.GONE : View.VISIBLE);
+        mBottomNavBarVisible = !isKeyboardVisible;
     }
 
-    /**
-     * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of
-     * detaching the bottom nav bar.
-     */
-    protected void detachBottomNavBarWindow() {
-        if (!mShowBottom) {
-            return;
+    private void buildTopBar(int layout) {
+        mTopNavigationBarContainer.removeAllViews();
+        View.inflate(mContext, layout, mTopNavigationBarContainer);
+        mTopNavigationBarView = (CarNavigationBarView) mTopNavigationBarContainer.getChildAt(0);
+        if (mTopNavigationBarView == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_top_navigation_bar");
+            throw new RuntimeException("Unable to build top nav bar due to missing layout");
         }
-
-        if (!mBottomNavBarVisible) {
-            return;
-        }
-        mBottomNavBarVisible = false;
-        mWindowManager.removeView(mNavigationBarWindow);
+        mTopNavigationBarView.setStatusBar(this);
+        addTemperatureViewToController(mTopNavigationBarView);
+        mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener);
     }
 
     private void buildBottomBar(int layout) {
@@ -891,7 +935,7 @@
         mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
         if (mNavigationBarView == null) {
             Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
-            throw new RuntimeException("Unable to build botom nav bar due to missing layout");
+            throw new RuntimeException("Unable to build bottom nav bar due to missing layout");
         }
         mNavigationBarView.setStatusBar(this);
         addTemperatureViewToController(mNavigationBarView);
@@ -902,7 +946,7 @@
         View.inflate(mContext, layout, mLeftNavigationBarWindow);
         mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
         if (mLeftNavigationBarView == null) {
-            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_left_navigation_bar");
             throw new RuntimeException("Unable to build left nav bar due to missing layout");
         }
         mLeftNavigationBarView.setStatusBar(this);
@@ -915,7 +959,7 @@
         View.inflate(mContext, layout, mRightNavigationBarWindow);
         mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
         if (mRightNavigationBarView == null) {
-            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_right_navigation_bar");
             throw new RuntimeException("Unable to build right nav bar due to missing layout");
         }
         mRightNavigationBarView.setStatusBar(this);
@@ -1030,12 +1074,8 @@
         UserSwitcherController userSwitcherController =
                 Dependency.get(UserSwitcherController.class);
         if (userSwitcherController.useFullscreenUserSwitcher()) {
-            Car car = Car.createCar(mContext);
-            CarTrustAgentEnrollmentManager enrollmentManager = (CarTrustAgentEnrollmentManager) car
-                    .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
             mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
-                    mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub),
-                    enrollmentManager, mContext);
+                    mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext);
         } else {
             super.createUserSwitcher();
         }
@@ -1146,17 +1186,22 @@
             mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin);
         }
         if (mNotificationView.getHeight() > 0) {
-            // Calculates the alpha value for the background based on how much of the notification
-            // shade is visible to the user. When the notification shade is completely open then
-            // alpha value will be 1.
-            float alpha = (float) height / mNotificationView.getHeight();
             Drawable background = mNotificationView.getBackground().mutate();
-
-            background.setAlpha((int) (alpha * 255));
+            background.setAlpha((int) (getBackgroundAlpha(height) * 255));
             mNotificationView.setBackground(background);
         }
     }
 
+    /**
+     * Calculates the alpha value for the background based on how much of the notification
+     * shade is visible to the user. When the notification shade is completely open then
+     * alpha value will be 1.
+     */
+    private float getBackgroundAlpha(int height) {
+        return mInitialBackgroundAlpha +
+            ((float) height / mNotificationView.getHeight() * mBackgroundAlphaDiff);
+    }
+
     private void calculatePercentageFromBottom(float height) {
         if (mNotificationView.getHeight() > 0) {
             mPercentageFromBottom = (int) Math.abs(
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index a442426..cd87e78 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -17,14 +17,11 @@
 package com.android.systemui.statusbar.car;
 
 import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
 import android.car.drivingstate.CarDrivingStateEvent;
 import android.car.drivingstate.CarDrivingStateManager;
 import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.ServiceConnection;
-import android.os.IBinder;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -55,16 +52,11 @@
         if (mDrivingStateManager == null) {
             return false;
         }
-        try {
-            CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
-            if (currentState != null) {
-                return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
-                        || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
-            }
-        } catch (CarNotConnectedException e) {
-            Log.e(TAG, "Cannot determine current driving state. Car not connected", e);
+        CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
+        if (currentState != null) {
+            return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
+                    || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
         }
-
         return false; // Default to false.
     }
 
@@ -72,55 +64,25 @@
      * Establishes connection with the Car service.
      */
     public void connectToCarService() {
-        mCar = Car.createCar(mContext, mCarConnectionListener);
-        if (mCar != null) {
-            mCar.connect();
-        }
+        mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+                mCarServiceLifecycleListener);
     }
 
-    /**
-     * Disconnects from Car service and cleans up listeners.
-     */
-    public void disconnectFromCarService() {
-        if (mCar != null) {
-            mCar.disconnect();
+    private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+        if (!ready) {
+            return;
         }
-    }
-
-    private final ServiceConnection mCarConnectionListener =
-            new ServiceConnection() {
-                public void onServiceConnected(ComponentName name, IBinder service) {
-                    logD("Car Service connected");
-                    try {
-                        mDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager(
-                                Car.CAR_DRIVING_STATE_SERVICE);
-                        if (mDrivingStateManager != null) {
-                            mDrivingStateManager.registerListener(mDrivingStateHandler);
-                            mDrivingStateHandler.onDrivingStateChanged(
-                                    mDrivingStateManager.getCurrentCarDrivingState());
-                        } else {
-                            Log.e(TAG, "CarDrivingStateService service not available");
-                        }
-                    } catch (CarNotConnectedException e) {
-                        Log.e(TAG, "Car not connected", e);
-                    }
-                }
-
-                @Override
-                public void onServiceDisconnected(ComponentName name) {
-                    destroyDrivingStateManager();
-                }
-            };
-
-    private void destroyDrivingStateManager() {
-        try {
-            if (mDrivingStateManager != null) {
-                mDrivingStateManager.unregisterListener();
-            }
-        } catch (CarNotConnectedException e) {
-            Log.e(TAG, "Error unregistering listeners", e);
+        logD("Car Service connected");
+        mDrivingStateManager = (CarDrivingStateManager) car.getCarManager(
+                Car.CAR_DRIVING_STATE_SERVICE);
+        if (mDrivingStateManager != null) {
+            mDrivingStateManager.registerListener(mDrivingStateHandler);
+            mDrivingStateHandler.onDrivingStateChanged(
+                    mDrivingStateManager.getCurrentCarDrivingState());
+        } else {
+            Log.e(TAG, "CarDrivingStateService service not available");
         }
-    }
+    };
 
     private void logD(String message) {
         if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 0f7c1ee..31aced0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -18,6 +18,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.car.Car;
 import android.car.trust.CarTrustAgentEnrollmentManager;
 import android.car.userlib.CarUserManagerHelper;
 import android.content.BroadcastReceiver;
@@ -50,7 +51,7 @@
     private final CarStatusBar mStatusBar;
     private final Context mContext;
     private final UserManager mUserManager;
-    private final CarTrustAgentEnrollmentManager mEnrollmentManager;
+    private CarTrustAgentEnrollmentManager mEnrollmentManager;
     private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
     private UserGridRecyclerView.UserRecord mSelectedUser;
     private CarUserManagerHelper mCarUserManagerHelper;
@@ -64,13 +65,11 @@
             mContext.unregisterReceiver(mUserUnlockReceiver);
         }
     };
+    private final Car mCar;
 
-
-    public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub,
-            CarTrustAgentEnrollmentManager enrollmentManager, Context context) {
+    public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
         mStatusBar = statusBar;
         mParent = containerStub.inflate();
-        mEnrollmentManager = enrollmentManager;
         mContext = context;
 
         View container = mParent.findViewById(R.id.container);
@@ -86,6 +85,15 @@
         mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext);
         mUserManager = mContext.getSystemService(UserManager.class);
 
+        mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+                (car, ready) -> {
+                    if (!ready) {
+                        return;
+                    }
+                    mEnrollmentManager = (CarTrustAgentEnrollmentManager) car
+                            .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
+                });
+
         mShortAnimDuration = container.getResources()
                 .getInteger(android.R.integer.config_shortAnimTime);
         IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
@@ -201,6 +209,9 @@
     }
 
     private boolean hasTrustedDevice(int uid) {
+        if (mEnrollmentManager == null) { // car service not ready, so it cannot be available.
+            return false;
+        }
         return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
index 8de1439..a27dd34 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
@@ -18,13 +18,10 @@
 
 import android.annotation.NonNull;
 import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
 import android.car.hardware.power.CarPowerManager;
 import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.ServiceConnection;
-import android.os.IBinder;
 import android.util.Log;
 
 /**
@@ -39,55 +36,30 @@
     private Car mCar;
     private CarPowerManager mCarPowerManager;
 
-    private final ServiceConnection mCarConnectionListener =
-            new ServiceConnection() {
-                public void onServiceConnected(ComponentName name, IBinder service) {
-                    Log.d(TAG, "Car Service connected");
-                    try {
-                        mCarPowerManager = (CarPowerManager) mCar.getCarManager(Car.POWER_SERVICE);
-                        if (mCarPowerManager != null) {
-                            mCarPowerManager.setListener(mCarPowerStateListener);
-                        } else {
-                            Log.e(TAG, "CarPowerManager service not available");
-                        }
-                    } catch (CarNotConnectedException e) {
-                        Log.e(TAG, "Car not connected", e);
-                    }
-                }
-
-                @Override
-                public void onServiceDisconnected(ComponentName name) {
-                    destroyCarPowerManager();
-                }
-            };
+    private final CarServiceLifecycleListener mCarServiceLifecycleListener;
 
     PowerManagerHelper(Context context, @NonNull CarPowerStateListener listener) {
         mContext = context;
         mCarPowerStateListener = listener;
+        mCarServiceLifecycleListener = (car, ready) -> {
+            if (!ready) {
+                return;
+            }
+            Log.d(TAG, "Car Service connected");
+            mCarPowerManager = (CarPowerManager) car.getCarManager(Car.POWER_SERVICE);
+            if (mCarPowerManager != null) {
+                mCarPowerManager.setListener(mCarPowerStateListener);
+            } else {
+                Log.e(TAG, "CarPowerManager service not available");
+            }
+        };
     }
 
     /**
      * Connect to Car service.
      */
     void connectToCarService() {
-        mCar = Car.createCar(mContext, mCarConnectionListener);
-        if (mCar != null) {
-            mCar.connect();
-        }
-    }
-
-    /**
-     * Disconnects from Car service.
-     */
-    void disconnectFromCarService() {
-        if (mCar != null) {
-            mCar.disconnect();
-        }
-    }
-
-    private void destroyCarPowerManager() {
-        if (mCarPowerManager != null) {
-            mCarPowerManager.clearListener();
-        }
+        mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+                mCarServiceLifecycleListener);
     }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 30429ed..a8515f9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -20,15 +20,13 @@
 import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
 
 import android.car.Car;
+import android.car.Car.CarServiceLifecycleListener;
 import android.car.VehicleUnit;
 import android.car.hardware.CarPropertyValue;
 import android.car.hardware.hvac.CarHvacManager;
 import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.ServiceConnection;
 import android.os.Handler;
-import android.os.IBinder;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -52,6 +50,7 @@
     private Car mCar;
     private CarHvacManager mHvacManager;
     private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
+
     /**
      * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
      * match.
@@ -83,39 +82,17 @@
                     + " zone: " + zone);
         }
     };
-    /**
-     * If the connection to car service goes away then restart it.
-     */
-    private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
-        @Override
-        public void binderDied() {
-            Log.d(TAG, "Death of HVAC triggering a restart");
-            if (mCar != null) {
-                mCar.disconnect();
-            }
-            destroyHvacManager();
-            mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
-        }
-    };
-    /**
-     * Registers callbacks and initializes components upon connection.
-     */
-    private ServiceConnection mServiceConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            try {
-                service.linkToDeath(mRestart, 0);
-                mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
-                mHvacManager.registerCallback(mHardwareCallback);
-                initComponents();
-            } catch (Exception e) {
-                Log.e(TAG, "Failed to correctly connect to HVAC", e);
-            }
-        }
 
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            destroyHvacManager();
+    private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+        if (!ready) {
+            return;
+        }
+        try {
+            mHvacManager = (CarHvacManager) car.getCarManager(Car.HVAC_SERVICE);
+            mHvacManager.registerCallback(mHardwareCallback);
+            initComponents();
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to correctly connect to HVAC", e);
         }
     };
 
@@ -129,18 +106,8 @@
      */
     public void connectToCarService() {
         mHandler = new Handler();
-        mCar = Car.createCar(mContext, mServiceConnection, mHandler);
-        if (mCar != null) {
-            // note: this connect call handles the retries
-            mCar.connect();
-        }
-    }
-
-    private void destroyHvacManager() {
-        if (mHvacManager != null) {
-            mHvacManager.unregisterCallback(mHardwareCallback);
-            mHvacManager = null;
-        }
+        mCar = Car.createCar(mContext, /* handler= */ mHandler, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+                mCarServiceLifecycleListener);
     }
 
     /**
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index d0a63f0..09223e8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -24,12 +24,10 @@
 import android.app.Dialog;
 import android.app.KeyguardManager;
 import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
 import android.car.media.CarAudioManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.ServiceConnection;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.Color;
@@ -39,7 +37,6 @@
 import android.media.AudioManager;
 import android.os.Debug;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.util.AttributeSet;
@@ -65,7 +62,6 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -79,8 +75,6 @@
 
     private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
     private static final String XML_TAG_VOLUME_ITEM = "item";
-    private static final int HOVERING_TIMEOUT = 16000;
-    private static final int NORMAL_TIMEOUT = 3000;
     private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
     private static final int DISMISS_DELAY_IN_MILLIS = 50;
     private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
@@ -94,12 +88,23 @@
     // Volume items in the RecyclerView.
     private final List<CarVolumeItem> mCarVolumeLineItems = new ArrayList<>();
     private final KeyguardManager mKeyguard;
+    private final int mNormalTimeout;
+    private final int mHoveringTimeout;
+
     private Window mWindow;
     private CustomDialog mDialog;
     private RecyclerView mListView;
     private CarVolumeItemAdapter mVolumeItemsAdapter;
     private Car mCar;
     private CarAudioManager mCarAudioManager;
+    private boolean mHovering;
+    private int mCurrentlyDisplayingGroupId;
+    private int mPreviouslyDisplayingGroupId;
+    private boolean mShowing;
+    private boolean mDismissing;
+    private boolean mExpanded;
+    private View mExpandIcon;
+
     private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback =
             new CarAudioManager.CarVolumeCallback() {
                 @Override
@@ -129,6 +134,7 @@
                         volumeItem.progress = value;
                     }
                     if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+                        mPreviouslyDisplayingGroupId = mCurrentlyDisplayingGroupId;
                         mCurrentlyDisplayingGroupId = groupId;
                         mHandler.obtainMessage(H.SHOW,
                                 Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
@@ -140,80 +146,47 @@
                     // ignored
                 }
             };
-    private boolean mHovering;
-    private int mCurrentlyDisplayingGroupId;
-    private boolean mShowing;
-    private boolean mExpanded;
-    private View mExpandIcon;
-    private final ServiceConnection mServiceConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            try {
-                mExpanded = false;
-                mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
-                int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
-                // Populates volume slider items from volume groups to UI.
-                for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
-                    VolumeItem volumeItem = getVolumeItemForUsages(
-                            mCarAudioManager.getUsagesForVolumeGroupId(groupId));
-                    mAvailableVolumeItems.add(volumeItem);
-                    // The first one is the default item.
-                    if (groupId == 0) {
-                        setuptListItem(0);
-                    }
-                }
 
-                // If list is already initiated, update its content.
-                if (mVolumeItemsAdapter != null) {
-                    mVolumeItemsAdapter.notifyDataSetChanged();
-                }
-                mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
-            } catch (CarNotConnectedException e) {
-                Log.e(TAG, "Car is not connected!", e);
+    private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+        if (!ready) {
+            return;
+        }
+        mExpanded = false;
+        mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE);
+        int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
+        // Populates volume slider items from volume groups to UI.
+        for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
+            VolumeItem volumeItem = getVolumeItemForUsages(
+                    mCarAudioManager.getUsagesForVolumeGroupId(groupId));
+            mAvailableVolumeItems.add(volumeItem);
+            // The first one is the default item.
+            if (groupId == 0) {
+                clearAllAndSetupDefaultCarVolumeLineItem(0);
             }
         }
 
-        /**
-         * This does not get called when service is properly disconnected.
-         * So we need to also handle cleanups in destroy().
-         */
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            cleanupAudioManager();
+        // If list is already initiated, update its content.
+        if (mVolumeItemsAdapter != null) {
+            mVolumeItemsAdapter.notifyDataSetChanged();
         }
+        mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
     };
 
-    private void setuptListItem(int groupId) {
-        mCarVolumeLineItems.clear();
-        VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
-        volumeItem.defaultItem = true;
-        addCarVolumeListItem(volumeItem, /* volumeGroupId = */ groupId,
-                R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener()
-        );
-    }
-
     public CarVolumeDialogImpl(Context context) {
         mContext = context;
         mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        mCar = Car.createCar(mContext, mServiceConnection);
+        mNormalTimeout = mContext.getResources().getInteger(
+                R.integer.car_volume_dialog_display_normal_timeout);
+        mHoveringTimeout = mContext.getResources().getInteger(
+                R.integer.car_volume_dialog_display_hovering_timeout);
     }
 
     private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
-        try {
-            return carAudioManager.getGroupVolume(volumeGroupId);
-        } catch (CarNotConnectedException e) {
-            Log.e(TAG, "Car is not connected!", e);
-        }
-        return 0;
+        return carAudioManager.getGroupVolume(volumeGroupId);
     }
 
     private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
-        try {
-            return carAudioManager.getGroupMaxVolume(volumeGroupId);
-        } catch (CarNotConnectedException e) {
-            Log.e(TAG, "Car is not connected!", e);
-        }
-        return 0;
+        return carAudioManager.getGroupMaxVolume(volumeGroupId);
     }
 
     /**
@@ -223,18 +196,21 @@
     @Override
     public void init(int windowType, Callback callback) {
         initDialog();
-
-        mCar.connect();
+        mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+                mCarServiceLifecycleListener);
     }
 
     @Override
     public void destroy() {
-        mHandler.removeCallbacksAndMessages(null);
+        mHandler.removeCallbacksAndMessages(/* token= */ null);
 
         cleanupAudioManager();
         // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
         // audio manager beforehand.
-        mCar.disconnect();
+        if (mCar != null) {
+            mCar.disconnect();
+            mCar = null;
+        }
     }
 
     private void initDialog() {
@@ -244,6 +220,7 @@
 
         mHovering = false;
         mShowing = false;
+        mDismissing = false;
         mExpanded = false;
         mWindow = mDialog.getWindow();
         mWindow.requestFeature(Window.FEATURE_NO_TITLE);
@@ -301,19 +278,36 @@
 
         mHandler.removeMessages(H.SHOW);
         mHandler.removeMessages(H.DISMISS);
+
         rescheduleTimeoutH();
+
         // Refresh the data set before showing.
         mVolumeItemsAdapter.notifyDataSetChanged();
+
         if (mShowing) {
+            if (mPreviouslyDisplayingGroupId == mCurrentlyDisplayingGroupId || mExpanded) {
+                return;
+            }
+
+            clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId);
             return;
         }
+
         mShowing = true;
-        setuptListItem(mCurrentlyDisplayingGroupId);
+        clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId);
         mDialog.show();
         Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
     }
 
-    private void rescheduleTimeoutH() {
+    private void clearAllAndSetupDefaultCarVolumeLineItem(int groupId) {
+        mCarVolumeLineItems.clear();
+        VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+        volumeItem.defaultItem = true;
+        addCarVolumeListItem(volumeItem, /* volumeGroupId = */ groupId,
+                R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener());
+    }
+
+    protected void rescheduleTimeoutH() {
         mHandler.removeMessages(H.DISMISS);
         final int timeout = computeTimeoutH();
         mHandler.sendMessageDelayed(mHandler
@@ -325,7 +319,7 @@
     }
 
     private int computeTimeoutH() {
-        return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
+        return mHovering ? mHoveringTimeout : mNormalTimeout;
     }
 
     private void dismissH(int reason) {
@@ -335,14 +329,11 @@
 
         mHandler.removeMessages(H.DISMISS);
         mHandler.removeMessages(H.SHOW);
-        if (!mShowing) {
+        if (!mShowing || mDismissing) {
             return;
         }
 
-        mListView.animate().cancel();
-
-        mListView.setTranslationY(0);
-        mListView.setAlpha(1);
+        mDismissing = true;
         mListView.animate()
                 .alpha(0)
                 .translationY(-mListView.getHeight())
@@ -354,7 +345,7 @@
                     }
                     mDialog.dismiss();
                     mShowing = false;
-                    mShowing = false;
+                    mDismissing = false;
                     // if mExpandIcon is null that means user never clicked on the expanded arrow
                     // which implies that the dialog is still not expanded. In that case we do
                     // not want to reset the state
@@ -390,12 +381,13 @@
                 if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
                     TypedArray item = mContext.getResources().obtainAttributes(
                             attrs, R.styleable.carVolumeItems_item);
-                    int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
+                    int usage = item.getInt(R.styleable.carVolumeItems_item_usage,
+                            /* defValue= */ -1);
                     if (usage >= 0) {
                         VolumeItem volumeItem = new VolumeItem();
                         volumeItem.rank = rank;
-                        volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon,
-                                0);
+                        volumeItem.icon = item.getResourceId(
+                                R.styleable.carVolumeItems_item_icon, /* defValue= */ 0);
                         mVolumeItems.put(usage, volumeItem);
                         rank++;
                     }
@@ -420,22 +412,22 @@
         return result;
     }
 
-    private CarVolumeItem addCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId,
-            int supplementalIconId,
+    private CarVolumeItem createCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId,
+            Drawable supplementalIcon, int seekbarProgressValue,
             @Nullable View.OnClickListener supplementalIconOnClickListener) {
         CarVolumeItem carVolumeItem = new CarVolumeItem();
         carVolumeItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
-        int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
-        int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
-        carVolumeItem.setProgress(progress);
+        carVolumeItem.setProgress(seekbarProgressValue);
         carVolumeItem.setOnSeekBarChangeListener(
                 new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId,
                         mCarAudioManager));
-        Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
+        carVolumeItem.setGroupId(volumeGroupId);
+
+        int color = mContext.getColor(R.color.car_volume_dialog_tint);
+        Drawable primaryIcon = mContext.getDrawable(volumeItem.icon);
         primaryIcon.mutate().setTint(color);
         carVolumeItem.setPrimaryIcon(primaryIcon);
-        if (supplementalIconId != 0) {
-            Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
+        if (supplementalIcon != null) {
             supplementalIcon.mutate().setTint(color);
             carVolumeItem.setSupplementalIcon(supplementalIcon,
                     /* showSupplementalIconDivider= */ true);
@@ -444,21 +436,23 @@
             carVolumeItem.setSupplementalIcon(/* drawable= */ null,
                     /* showSupplementalIconDivider= */ false);
         }
-        carVolumeItem.setGroupId(volumeGroupId);
-        mCarVolumeLineItems.add(carVolumeItem);
+
         volumeItem.carVolumeItem = carVolumeItem;
-        volumeItem.progress = progress;
+        volumeItem.progress = seekbarProgressValue;
+
         return carVolumeItem;
     }
 
-    private VolumeItem findVolumeItem(CarVolumeItem targetItem) {
-        for (int i = 0; i < mVolumeItems.size(); ++i) {
-            VolumeItem volumeItem = mVolumeItems.valueAt(i);
-            if (volumeItem.carVolumeItem == targetItem) {
-                return volumeItem;
-            }
-        }
-        return null;
+    private CarVolumeItem addCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId,
+            int supplementalIconId,
+            @Nullable View.OnClickListener supplementalIconOnClickListener) {
+        int seekbarProgressValue = getSeekbarValue(mCarAudioManager, volumeGroupId);
+        Drawable supplementalIcon = supplementalIconId == 0 ? null : mContext.getDrawable(
+                supplementalIconId);
+        CarVolumeItem carVolumeItem = createCarVolumeListItem(volumeItem, volumeGroupId,
+                supplementalIcon, seekbarProgressValue, supplementalIconOnClickListener);
+        mCarVolumeLineItems.add(carVolumeItem);
+        return carVolumeItem;
     }
 
     private void cleanupAudioManager() {
@@ -554,21 +548,15 @@
             for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
                 if (groupId != mCurrentlyDisplayingGroupId) {
                     VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
-                    addCarVolumeListItem(volumeItem, groupId, 0, null);
+                    addCarVolumeListItem(volumeItem, groupId, /* supplementalIconId= */ 0,
+                            /* supplementalIconOnClickListener= */ null);
                 }
             }
             inAnimator = AnimatorInflater.loadAnimator(
                     mContext, R.anim.car_arrow_fade_in_rotate_up);
 
         } else {
-            // Only keeping the default stream if it is not expended.
-            Iterator itr = mCarVolumeLineItems.iterator();
-            while (itr.hasNext()) {
-                CarVolumeItem carVolumeItem = (CarVolumeItem) itr.next();
-                if (carVolumeItem.getGroupId() != mCurrentlyDisplayingGroupId) {
-                    itr.remove();
-                }
-            }
+            clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId);
             inAnimator = AnimatorInflater.loadAnimator(
                     mContext, R.anim.car_arrow_fade_in_rotate_down);
         }
@@ -606,18 +594,14 @@
                 // sent back down again.
                 return;
             }
-            try {
-                if (mCarAudioManager == null) {
-                    Log.w(TAG, "Ignoring volume change event because the car isn't connected");
-                    return;
-                }
-                mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
-                mAvailableVolumeItems.get(
-                        mVolumeGroupId).carVolumeItem.setProgress(progress);
-                mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
-            } catch (CarNotConnectedException e) {
-                Log.e(TAG, "Car is not connected!", e);
+            if (mCarAudioManager == null) {
+                Log.w(TAG, "Ignoring volume change event because the car isn't connected");
+                return;
             }
+            mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
+            mAvailableVolumeItems.get(
+                    mVolumeGroupId).carVolumeItem.setProgress(progress);
+            mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
         }
 
         @Override
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f8c9bcc..894ba9c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1969,6 +1969,12 @@
         dumpSetting(s, p,
                 Settings.Secure.SKIP_TOUCH_COUNT,
                 SecureSettingsProto.Gesture.SKIP_TOUCH_COUNT);
+        dumpSetting(s, p,
+                Settings.Secure.AWARE_TAP_PAUSE_GESTURE_COUNT,
+                SecureSettingsProto.Gesture.AWARE_TAP_PAUSE_GESTURE_COUNT);
+        dumpSetting(s, p,
+                Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT,
+                SecureSettingsProto.Gesture.AWARE_TAP_PAUSE_TOUCH_COUNT);
         p.end(gestureToken);
 
         dumpSetting(s, p,
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 9716a00..a9149300 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -44,7 +44,7 @@
     </com.android.systemui.statusbar.BackDropView>
 
     <com.android.systemui.statusbar.ScrimView
-        android:id="@+id/scrim_for_bubble"
+        android:id="@+id/scrim_behind"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:importantForAccessibility="no"
@@ -56,14 +56,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content" />
 
-    <com.android.systemui.statusbar.ScrimView
-        android:id="@+id/scrim_behind"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:importantForAccessibility="no"
-        sysui:ignoreRightInset="true"
-        />
-
     <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index bd91333..c0c14fb 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.HandlerThread;
+import android.os.Trace;
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
 import android.util.Size;
@@ -48,6 +49,7 @@
     private static final int DELAY_FINISH_RENDERING = 1000;
     private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
     private static final int PATIENCE_WAIT_FOR_RENDERING = 10;
+    private static final boolean DEBUG = true;
     private HandlerThread mWorker;
 
     @Override
@@ -125,6 +127,10 @@
         @Override
         public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
             if (!mNeedTransition) return;
+            if (DEBUG) {
+                Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode
+                        + ", duration=" + animationDuration);
+            }
             mWorker.getThreadHandler().post(
                     () -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration));
             if (inAmbientMode && animationDuration == 0) {
@@ -177,6 +183,10 @@
         @Override
         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
             mWorker.getThreadHandler().post(() -> {
+                if (DEBUG) {
+                    Log.d(TAG, "onSurfaceChanged: w=" + width + ", h=" + height);
+                }
+
                 mRenderer.onSurfaceChanged(width, height);
                 mNeedRedraw = true;
             });
@@ -185,16 +195,31 @@
         @Override
         public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
             mWorker.getThreadHandler().post(() -> {
+                if (DEBUG) {
+                    Log.d(TAG, "onSurfaceRedrawNeeded: mNeedRedraw=" + mNeedRedraw);
+                }
+
                 if (mNeedRedraw) {
-                    preRender();
-                    requestRender();
-                    postRender();
+                    drawFrame();
                     mNeedRedraw = false;
                 }
             });
         }
 
         @Override
+        public void onVisibilityChanged(boolean visible) {
+            if (DEBUG) {
+                Log.d(TAG, "wallpaper visibility changes to: " + visible);
+            }
+        }
+
+        private void drawFrame() {
+            preRender();
+            requestRender();
+            postRender();
+        }
+
+        @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) {
@@ -204,8 +229,18 @@
 
         @Override
         public void preRender() {
+            if (DEBUG) {
+                Log.d(TAG, "preRender start");
+            }
+
             // This method should only be invoked from worker thread.
+            Trace.beginSection("ImageWallpaper#preRender");
             preRenderInternal();
+            Trace.endSection();
+
+            if (DEBUG) {
+                Log.d(TAG, "preRender end");
+            }
         }
 
         private void preRenderInternal() {
@@ -240,7 +275,9 @@
         @Override
         public void requestRender() {
             // This method should only be invoked from worker thread.
+            Trace.beginSection("ImageWallpaper#requestRender");
             requestRenderInternal();
+            Trace.endSection();
         }
 
         private void requestRenderInternal() {
@@ -262,9 +299,19 @@
 
         @Override
         public void postRender() {
+            if (DEBUG) {
+                Log.d(TAG, "postRender start");
+            }
+
             // This method should only be invoked from worker thread.
+            Trace.beginSection("ImageWallpaper#postRender");
             notifyWaitingThread();
             scheduleFinishRendering();
+            Trace.endSection();
+
+            if (DEBUG) {
+                Log.d(TAG, "postRender end");
+            }
         }
 
         private void notifyWaitingThread() {
@@ -289,12 +336,18 @@
         }
 
         private void finishRendering() {
+            if (DEBUG) {
+                Log.d(TAG, "finishRendering, preserve=" + needPreserveEglContext());
+            }
+
+            Trace.beginSection("ImageWallpaper#finishRendering");
             if (mEglHelper != null) {
                 mEglHelper.destroyEglSurface();
                 if (!needPreserveEglContext()) {
                     mEglHelper.destroyEglContext();
                 }
             }
+            Trace.endSection();
         }
 
         private boolean needPreserveEglContext() {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index d151f7d..0899d95 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -123,12 +123,11 @@
     }
 
     public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
-            ScrimView scrimForBubble,
             LockscreenWallpaper lockscreenWallpaper,
             TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
             Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
             AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) {
-        return new ScrimController(scrimBehind, scrimInFront, scrimForBubble, scrimStateListener,
+        return new ScrimController(scrimBehind, scrimInFront, scrimStateListener,
                 scrimVisibleListener, dozeParameters, alarmManager, keyguardMonitor);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 1c5e800..150a40a 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui;
 
+import android.annotation.NonNull;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Build;
@@ -66,7 +67,13 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (args != null && args.length > 0 && args[0].equals("--config")) {
+            dumpConfig(pw);
+            return;
+        }
+
         dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args);
+        dumpConfig(pw);
     }
 
     static void dumpServices(
@@ -95,5 +102,29 @@
             }
         }
     }
+
+    private void dumpConfig(@NonNull PrintWriter pw) {
+        pw.println("SystemUiServiceComponents configuration:");
+
+        pw.print("vendor component: ");
+        pw.println(getResources().getString(R.string.config_systemUIVendorServiceComponent));
+
+        dumpConfig(pw, "global", R.array.config_systemUIServiceComponents);
+        dumpConfig(pw, "per-user", R.array.config_systemUIServiceComponentsPerUser);
+    }
+
+    private void dumpConfig(@NonNull PrintWriter pw, @NonNull String type, int resId) {
+        final String[] services = getResources().getStringArray(resId);
+        pw.print(type); pw.print(": ");
+        if (services == null) {
+            pw.println("N/A");
+            return;
+        }
+        pw.print(services.length);
+        pw.println(" services");
+        for (int i = 0; i < services.length; i++) {
+            pw.print("  "); pw.print(i); pw.print(": "); pw.println(services[i]);
+        }
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index ce67577..cbbd3a0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -499,6 +499,8 @@
         if (newState == STATE_PENDING_CONFIRMATION || newState == STATE_AUTHENTICATED) {
             mNegativeButton.setText(R.string.cancel);
             mNegativeButton.setContentDescription(getResources().getString(R.string.cancel));
+        } else {
+            mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
         }
 
         updateIcon(mState, newState);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 3cc91de..5559d29 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -316,9 +316,10 @@
         if (mStackView == null) {
             mStackView = new BubbleStackView(mContext, mBubbleData, mSurfaceSynchronizer);
             ViewGroup sbv = mStatusBarWindowController.getStatusBarView();
-            int bubbleScrimIndex = sbv.indexOfChild(sbv.findViewById(R.id.scrim_for_bubble));
-            int stackIndex = bubbleScrimIndex + 1;  // Show stack above bubble scrim.
-            sbv.addView(mStackView, stackIndex,
+            // TODO(b/130237686): When you expand the shade on top of expanded bubble, there is no
+            //  scrim between bubble and the shade
+            int bubblePosition = sbv.indexOfChild(sbv.findViewById(R.id.scrim_behind)) + 1;
+            sbv.addView(mStackView, bubblePosition,
                     new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
             if (mExpandListener != null) {
                 mStackView.setExpandListener(mExpandListener);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 90cb05a..0e124e4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -33,6 +33,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.AsyncSensorManager;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -64,7 +65,7 @@
                 params);
 
         DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock,
-                wakefulnessLifecycle);
+                wakefulnessLifecycle, Dependency.get(BatteryController.class));
         machine.setParts(new DozeMachine.Part[]{
                 new DozePauser(handler, machine, alarmManager, params.getPolicy()),
                 new DozeFalsingManagerAdapter(falsingManager),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 93a51cc..7f2d527 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -27,6 +27,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.wakelock.WakeLock;
 
@@ -121,6 +122,7 @@
     private final WakeLock mWakeLock;
     private final AmbientDisplayConfiguration mConfig;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
+    private final BatteryController mBatteryController;
     private Part[] mParts;
 
     private final ArrayList<State> mQueuedRequests = new ArrayList<>();
@@ -129,11 +131,13 @@
     private boolean mWakeLockHeldForCurrentState = false;
 
     public DozeMachine(Service service, AmbientDisplayConfiguration config,
-            WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle) {
+            WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle,
+            BatteryController batteryController) {
         mDozeService = service;
         mConfig = config;
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mWakeLock = wakeLock;
+        mBatteryController = batteryController;
     }
 
     /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */
@@ -316,6 +320,9 @@
             Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
             return mState;
         }
+        if (requestedState == State.DOZE_AOD && mBatteryController.isAodPowerSave()) {
+            return State.DOZE;
+        }
         if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
             Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
             return mState;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index bab64db..6199a0d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -345,7 +345,6 @@
 
     private void checkTriggersAtInit() {
         if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
-                || mDozeHost.isPowerSaveActive()
                 || mDozeHost.isBlockingDoze()
                 || !mDozeHost.isProvisioned()) {
             mMachine.requestState(DozeMachine.State.FINISH);
@@ -574,8 +573,8 @@
 
         @Override
         public void onPowerSaveChanged(boolean active) {
-            if (active) {
-                mMachine.requestState(DozeMachine.State.FINISH);
+            if (mDozeHost.isPowerSaveActive()) {
+                mMachine.requestState(DozeMachine.State.DOZE);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 1f33af8..3869e77 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -83,7 +83,8 @@
      */
     private void updateAnimateScreenOff() {
         if (mCanAnimateTransition) {
-            final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing;
+            final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing
+                    && !mHost.isPowerSaveActive();
             mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
             mHost.setAnimateScreenOff(controlScreenOff);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
index b154e66..39c9632 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.util.Log;
 
 import com.android.systemui.Interpolators;
 
@@ -30,6 +31,7 @@
     private static final String TAG = ImageRevealHelper.class.getSimpleName();
     private static final float MAX_REVEAL = 0f;
     private static final float MIN_REVEAL = 1f;
+    private static final boolean DEBUG = true;
 
     private final ValueAnimator mAnimator;
     private final RevealStateListener mRevealListener;
@@ -56,8 +58,13 @@
 
             @Override
             public void onAnimationEnd(Animator animation) {
-                if (!mIsCanceled && mRevealListener != null) {
-                    mRevealListener.onRevealEnd();
+                if (mRevealListener != null) {
+                    if (DEBUG) {
+                        Log.d(TAG, "transition end, cancel=" + mIsCanceled + ", reveal=" + mReveal);
+                    }
+                    if (!mIsCanceled) {
+                        mRevealListener.onRevealEnd();
+                    }
                 }
                 mIsCanceled = false;
             }
@@ -65,6 +72,9 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 if (mRevealListener != null) {
+                    if (DEBUG) {
+                        Log.d(TAG, "transition start");
+                    }
                     mRevealListener.onRevealStart(true /* animate */);
                 }
             }
@@ -82,6 +92,9 @@
     }
 
     void updateAwake(boolean awake, long duration) {
+        if (DEBUG) {
+            Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration);
+        }
         mAwake = awake;
         mAnimator.setDuration(duration);
         if (duration == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 2960634..be6f7bf 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -46,6 +46,7 @@
     private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
     private static final float SCALE_VIEWPORT_MIN = 1f;
     private static final float SCALE_VIEWPORT_MAX = 1.1f;
+    private static final boolean DEBUG = true;
 
     private final WallpaperManager mWallpaperManager;
     private final ImageGLProgram mProgram;
@@ -107,6 +108,9 @@
     }
 
     private boolean loadBitmap() {
+        if (DEBUG) {
+            Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap);
+        }
         if (mWallpaperManager != null && mBitmap == null) {
             mBitmap = mWallpaperManager.getBitmap();
             mWallpaperManager.forgetLoadedWallpaper();
@@ -119,6 +123,9 @@
                 mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight);
             }
         }
+        if (DEBUG) {
+            Log.d(TAG, "loadBitmap done, surface size=" + mSurfaceSize);
+        }
         return mBitmap != null;
     }
 
@@ -193,16 +200,28 @@
 
     @Override
     public void onRevealStart(boolean animate) {
+        if (DEBUG) {
+            Log.v(TAG, "onRevealStart: start, anim=" + animate);
+        }
+
         if (animate) {
             mScissorMode = true;
             // Use current display area of texture.
             mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset);
         }
         mProxy.preRender();
+
+        if (DEBUG) {
+            Log.v(TAG, "onRevealStart: done");
+        }
     }
 
     @Override
     public void onRevealEnd() {
+        if (DEBUG) {
+            Log.v(TAG, "onRevealEnd: start, mScissorMode=" + mScissorMode);
+        }
+
         if (mScissorMode) {
             mScissorMode = false;
             // reset texture coordinates to use full texture.
@@ -211,6 +230,10 @@
             mProxy.requestRender();
         }
         mProxy.postRender();
+
+        if (DEBUG) {
+            Log.v(TAG, "onRevealEnd: done");
+        }
     }
 
     @Override
@@ -223,6 +246,7 @@
         out.print(prefix); out.print("mXOffset="); out.print(mXOffset);
         out.print(prefix); out.print("mYOffset="); out.print(mYOffset);
         out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold());
+        out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal());
         mWallpaper.dump(prefix, fd, out, args);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 48f32cf..e408745 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -103,8 +103,8 @@
     protected final Uri mMediaUri;
     private final Date mCurrentTime = new Date();
     private final Handler mHandler;
+    private final Handler mMediaHandler;
     private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
-    private final Object mMediaToken = new Object();
     private DozeParameters mDozeParameters;
     @VisibleForTesting
     protected SettableWakeLock mMediaWakeLock;
@@ -169,17 +169,13 @@
                 }
             };
 
-    public KeyguardSliceProvider() {
-        this(new Handler());
-    }
-
     public static KeyguardSliceProvider getAttachedInstance() {
         return KeyguardSliceProvider.sInstance;
     }
 
-    @VisibleForTesting
-    KeyguardSliceProvider(Handler handler) {
-        mHandler = handler;
+    public KeyguardSliceProvider() {
+        mHandler = new Handler();
+        mMediaHandler = new Handler();
         mSliceUri = Uri.parse(KEYGUARD_SLICE_URI);
         mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI);
         mDateUri = Uri.parse(KEYGUARD_DATE_URI);
@@ -462,16 +458,18 @@
     public void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state) {
         synchronized (this) {
             boolean nextVisible = NotificationMediaManager.isPlayingState(state);
-            mHandler.removeCallbacksAndMessages(mMediaToken);
+            mMediaHandler.removeCallbacksAndMessages(null);
             if (mMediaIsVisible && !nextVisible && mStatusBarState != StatusBarState.SHADE) {
                 // We need to delay this event for a few millis when stopping to avoid jank in the
                 // animation. The media app might not send its update when buffering, and the slice
                 // would end up without a header for 0.5 second.
                 mMediaWakeLock.setAcquired(true);
-                mHandler.postDelayed(() -> {
-                    updateMediaStateLocked(metadata, state);
-                    mMediaWakeLock.setAcquired(false);
-                }, mMediaToken, 2000);
+                mMediaHandler.postDelayed(() -> {
+                    synchronized (this) {
+                        updateMediaStateLocked(metadata, state);
+                        mMediaWakeLock.setAcquired(false);
+                    }
+                }, 2000);
             } else {
                 mMediaWakeLock.setAcquired(false);
                 updateMediaStateLocked(metadata, state);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index 150667b..ef09434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import javax.inject.Inject;
@@ -63,6 +64,7 @@
     private final Context mContext;
     private final PowerManager mPowerManager;
     private final IDreamManager mDreamManager;
+    private final BatteryController mBatteryController;
 
     private NotificationPresenter mPresenter;
     private HeadsUpManager mHeadsUpManager;
@@ -75,13 +77,14 @@
 
     @Inject
     public NotificationInterruptionStateProvider(Context context, NotificationFilter filter,
-            StatusBarStateController stateController) {
+            StatusBarStateController stateController, BatteryController batteryController) {
         this(context,
                 (PowerManager) context.getSystemService(Context.POWER_SERVICE),
                 IDreamManager.Stub.asInterface(
                         ServiceManager.checkService(DreamService.DREAM_SERVICE)),
                 new AmbientDisplayConfiguration(context),
                 filter,
+                batteryController,
                 stateController);
     }
 
@@ -92,10 +95,12 @@
             IDreamManager dreamManager,
             AmbientDisplayConfiguration ambientDisplayConfiguration,
             NotificationFilter notificationFilter,
+            BatteryController batteryController,
             StatusBarStateController statusBarStateController) {
         mContext = context;
         mPowerManager = powerManager;
         mDreamManager = dreamManager;
+        mBatteryController = batteryController;
         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
         mNotificationFilter = notificationFilter;
         mStatusBarStateController = statusBarStateController;
@@ -293,6 +298,13 @@
             return false;
         }
 
+        if (mBatteryController.isAodPowerSave()) {
+            if (DEBUG_HEADS_UP) {
+                Log.d(TAG, "No pulsing: disabled by battery saver: " + sbn.getKey());
+            }
+            return false;
+        }
+
         if (!canAlertCommon(entry)) {
             if (DEBUG_HEADS_UP) {
                 Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index 4700baa..18d436f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -421,7 +421,7 @@
     }
 
     /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */
-    private static class AnimateCloseListener extends AnimatorListenerAdapter {
+    private class AnimateCloseListener extends AnimatorListenerAdapter {
         final View mView;
         private final GutsContent mGutsContent;
 
@@ -433,8 +433,10 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             super.onAnimationEnd(animation);
-            mView.setVisibility(View.GONE);
-            mGutsContent.onFinishedClosing();
+            if (!isExposed()) {
+                mView.setVisibility(View.GONE);
+                mGutsContent.onFinishedClosing();
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 8f7671a..719ec32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -100,6 +100,7 @@
     protected String mKeyToRemoveOnGutsClosed;
 
     private StatusBar mStatusBar;
+    private Runnable mOpenRunnable;
 
     @Inject
     public NotificationGutsManager(
@@ -343,6 +344,7 @@
     public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
             int x, int y, boolean resetMenu) {
         if (mNotificationGutsExposed != null) {
+            mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
             mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
         }
         if (resetMenu) {
@@ -445,7 +447,7 @@
         // ensure that it's laid but not visible until actually laid out
         guts.setVisibility(View.INVISIBLE);
         // Post to ensure the the guts are properly laid out.
-        guts.post(new Runnable() {
+        mOpenRunnable = new Runnable() {
             @Override
             public void run() {
                 if (row.getWindowToken() == null) {
@@ -470,7 +472,8 @@
                 mListContainer.onHeightChanged(row, true /* needsAnimation */);
                 mGutsMenuItem = menuItem;
             }
-        });
+        };
+        guts.post(mOpenRunnable);
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 179375e..4e91e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -240,7 +240,7 @@
      * @return Alpha from 0 to 1.
      */
     private float getClockAlpha(int y) {
-        float alphaKeyguard = Math.max(0, y / Math.max(1f, getExpandedPreferredClockY()));
+        float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f)));
         alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
         return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index f4635d1..f7b8a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -67,6 +67,9 @@
     }
 
     private fun updateListeningState() {
+        if (pickupSensor == null) {
+            return
+        }
         val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
                 !statusBarStateController.isDozing
 
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 ecfc45b..06a2225 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -604,7 +604,7 @@
      */
     public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) {
         if (mWakeAndUnlockRunning
-                && scrimsVisible == ScrimController.TRANSPARENT) {
+                && scrimsVisible == ScrimController.VISIBILITY_FULLY_TRANSPARENT) {
             mWakeAndUnlockRunning = false;
             update();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fd3c9526..9019e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -79,24 +79,23 @@
     /**
      * When both scrims have 0 alpha.
      */
-    public static final int TRANSPARENT = 0;
+    public static final int VISIBILITY_FULLY_TRANSPARENT = 0;
     /**
      * When scrims aren't transparent (alpha 0) but also not opaque (alpha 1.)
      */
-    public static final int SEMI_TRANSPARENT = 1;
+    public static final int VISIBILITY_SEMI_TRANSPARENT = 1;
     /**
      * When at least 1 scrim is fully opaque (alpha set to 1.)
      */
-    public static final int OPAQUE = 2;
+    public static final int VISIBILITY_FULLY_OPAQUE = 2;
 
-    @IntDef(prefix = {"VISIBILITY_"}, value = {
-            TRANSPARENT,
-            SEMI_TRANSPARENT,
-            OPAQUE
+    @IntDef(prefix = { "VISIBILITY_" }, value = {
+            VISIBILITY_FULLY_TRANSPARENT,
+            VISIBILITY_SEMI_TRANSPARENT,
+            VISIBILITY_FULLY_OPAQUE
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ScrimVisibility {
-    }
+    public @interface ScrimVisibility {}
 
     /**
      * Default alpha value for most scrims.
@@ -124,11 +123,8 @@
 
     private ScrimState mState = ScrimState.UNINITIALIZED;
     private final Context mContext;
-
-    protected final ScrimView mScrimInFront;
     protected final ScrimView mScrimBehind;
-    protected final ScrimView mScrimForBubble;
-
+    protected final ScrimView mScrimInFront;
     private final UnlockMethodCache mUnlockMethodCache;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final DozeParameters mDozeParameters;
@@ -157,15 +153,10 @@
     private Runnable mOnAnimationFinished;
     private boolean mDeferFinishedListener;
     private final Interpolator mInterpolator = new DecelerateInterpolator();
-
-    private float mInFrontAlpha = NOT_INITIALIZED;
-    private float mBehindAlpha = NOT_INITIALIZED;
-    private float mBubbleAlpha = NOT_INITIALIZED;
-
-    private int mInFrontTint;
-    private int mBehindTint;
-    private int mBubbleTint;
-
+    private float mCurrentInFrontAlpha  = NOT_INITIALIZED;
+    private float mCurrentBehindAlpha = NOT_INITIALIZED;
+    private int mCurrentInFrontTint;
+    private int mCurrentBehindTint;
     private boolean mWallpaperVisibilityTimedOut;
     private int mScrimsVisibility;
     private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener;
@@ -184,17 +175,14 @@
     private boolean mWakeLockHeld;
     private boolean mKeyguardOccluded;
 
-    public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, ScrimView scrimForBubble,
+    public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
             TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
             Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
             AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) {
         mScrimBehind = scrimBehind;
         mScrimInFront = scrimInFront;
-        mScrimForBubble = scrimForBubble;
-
         mScrimStateListener = scrimStateListener;
         mScrimVisibleListener = scrimVisibleListener;
-
         mContext = scrimBehind.getContext();
         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
         mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
@@ -225,13 +213,12 @@
 
         final ScrimState[] states = ScrimState.values();
         for (int i = 0; i < states.length; i++) {
-            states[i].init(mScrimInFront, mScrimBehind, mScrimForBubble, mDozeParameters);
+            states[i].init(mScrimInFront, mScrimBehind, mDozeParameters);
             states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
         }
 
         mScrimBehind.setDefaultFocusHighlightEnabled(false);
         mScrimInFront.setDefaultFocusHighlightEnabled(false);
-        mScrimForBubble.setDefaultFocusHighlightEnabled(false);
 
         updateScrims();
     }
@@ -270,14 +257,10 @@
         mBlankScreen = state.getBlanksScreen();
         mAnimateChange = state.getAnimateChange();
         mAnimationDuration = state.getAnimationDuration();
-
-        mInFrontTint = state.getFrontTint();
-        mBehindTint = state.getBehindTint();
-        mBubbleTint = state.getBubbleTint();
-
-        mInFrontAlpha = state.getFrontAlpha();
-        mBehindAlpha = state.getBehindAlpha();
-        mBubbleAlpha = state.getBubbleAlpha();
+        mCurrentInFrontTint = state.getFrontTint();
+        mCurrentBehindTint = state.getBehindTint();
+        mCurrentInFrontAlpha = state.getFrontAlpha();
+        mCurrentBehindAlpha = state.getBehindAlpha();
         applyExpansionToAlpha();
 
         // Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
@@ -410,20 +393,21 @@
         if (mExpansionFraction != fraction) {
             mExpansionFraction = fraction;
 
-            boolean relevantState = (mState == ScrimState.UNLOCKED
-                    || mState == ScrimState.KEYGUARD
-                    || mState == ScrimState.PULSING
-                    || mState == ScrimState.BUBBLE_EXPANDED);
-            if (!(relevantState && mExpansionAffectsAlpha)) {
+            final boolean keyguardOrUnlocked = mState == ScrimState.UNLOCKED
+                    || mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING;
+            if (!keyguardOrUnlocked || !mExpansionAffectsAlpha) {
                 return;
             }
+
             applyExpansionToAlpha();
+
             if (mUpdatePending) {
                 return;
             }
+
             setOrAdaptCurrentAnimation(mScrimBehind);
             setOrAdaptCurrentAnimation(mScrimInFront);
-            setOrAdaptCurrentAnimation(mScrimForBubble);
+
             dispatchScrimState(mScrimBehind.getViewAlpha());
 
             // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING
@@ -437,10 +421,11 @@
     }
 
     private void setOrAdaptCurrentAnimation(View scrim) {
-        float alpha = getCurrentScrimAlpha(scrim);
-        if (isAnimating(scrim)) {
-            // Adapt current animation.
+        if (!isAnimating(scrim)) {
+            updateScrimColor(scrim, getCurrentScrimAlpha(scrim), getCurrentScrimTint(scrim));
+        } else {
             ValueAnimator previousAnimator = (ValueAnimator) scrim.getTag(TAG_KEY_ANIM);
+            float alpha = getCurrentScrimAlpha(scrim);
             float previousEndValue = (Float) scrim.getTag(TAG_END_ALPHA);
             float previousStartValue = (Float) scrim.getTag(TAG_START_ALPHA);
             float relativeDiff = alpha - previousEndValue;
@@ -448,9 +433,6 @@
             scrim.setTag(TAG_START_ALPHA, newStartValue);
             scrim.setTag(TAG_END_ALPHA, alpha);
             previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-        } else {
-            // Set animation.
-            updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim));
         }
     }
 
@@ -459,27 +441,27 @@
             return;
         }
 
-        if (mState == ScrimState.UNLOCKED || mState == ScrimState.BUBBLE_EXPANDED) {
+        if (mState == ScrimState.UNLOCKED) {
             // Darken scrim as you pull down the shade when unlocked
             float behindFraction = getInterpolatedFraction();
             behindFraction = (float) Math.pow(behindFraction, 0.8f);
-            mBehindAlpha = behindFraction * GRADIENT_SCRIM_ALPHA_BUSY;
-            mInFrontAlpha = 0;
+            mCurrentBehindAlpha = behindFraction * GRADIENT_SCRIM_ALPHA_BUSY;
+            mCurrentInFrontAlpha = 0;
         } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING) {
             // Either darken of make the scrim transparent when you
             // pull down the shade
             float interpolatedFract = getInterpolatedFraction();
             float alphaBehind = mState.getBehindAlpha();
             if (mDarkenWhileDragging) {
-                mBehindAlpha = MathUtils.lerp(GRADIENT_SCRIM_ALPHA_BUSY, alphaBehind,
+                mCurrentBehindAlpha = MathUtils.lerp(GRADIENT_SCRIM_ALPHA_BUSY, alphaBehind,
                         interpolatedFract);
-                mInFrontAlpha = mState.getFrontAlpha();
+                mCurrentInFrontAlpha = mState.getFrontAlpha();
             } else {
-                mBehindAlpha = MathUtils.lerp(0 /* start */, alphaBehind,
+                mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, alphaBehind,
                         interpolatedFract);
-                mInFrontAlpha = mState.getFrontAlpha();
+                mCurrentInFrontAlpha = mState.getFrontAlpha();
             }
-            mBehindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
+            mCurrentBehindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
                     mState.getBehindTint(), interpolatedFract);
         }
     }
@@ -504,8 +486,8 @@
      */
     public void setAodFrontScrimAlpha(float alpha) {
         if (((mState == ScrimState.AOD && mDozeParameters.getAlwaysOn())
-                || mState == ScrimState.PULSING) && mInFrontAlpha != alpha) {
-            mInFrontAlpha = alpha;
+                || mState == ScrimState.PULSING) && mCurrentInFrontAlpha != alpha) {
+            mCurrentInFrontAlpha = alpha;
             updateScrims();
         }
 
@@ -519,9 +501,9 @@
      */
     public void prepareForGentleWakeUp() {
         if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) {
-            mInFrontAlpha = 1f;
-            mInFrontTint = Color.BLACK;
-            mBehindTint = Color.BLACK;
+            mCurrentInFrontAlpha = 1f;
+            mCurrentInFrontTint = Color.BLACK;
+            mCurrentBehindTint = Color.BLACK;
             mAnimateChange = false;
             updateScrims();
             mAnimateChange = true;
@@ -539,8 +521,8 @@
 
         if (mState == ScrimState.PULSING) {
             float newBehindAlpha = mState.getBehindAlpha();
-            if (mBehindAlpha != newBehindAlpha) {
-                mBehindAlpha = newBehindAlpha;
+            if (mCurrentBehindAlpha != newBehindAlpha) {
+                mCurrentBehindAlpha = newBehindAlpha;
                 updateScrims();
             }
         }
@@ -562,11 +544,8 @@
             // Only animate scrim color if the scrim view is actually visible
             boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen;
             boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen;
-            boolean animateScrimForBubble = mScrimForBubble.getViewAlpha() != 0 && !mBlankScreen;
-
             mScrimInFront.setColors(mColors, animateScrimInFront);
             mScrimBehind.setColors(mColors, animateScrimBehind);
-            mScrimForBubble.setColors(mColors, animateScrimForBubble);
 
             // Calculate minimum scrim opacity for white or black text.
             int textColor = mColors.supportsDarkText() ? Color.BLACK : Color.WHITE;
@@ -585,11 +564,12 @@
         boolean occludedKeyguard = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
                 && mKeyguardOccluded;
         if (aodWallpaperTimeout || occludedKeyguard) {
-            mBehindAlpha = 1;
+            mCurrentBehindAlpha = 1;
         }
-        setScrimAlpha(mScrimInFront, mInFrontAlpha);
-        setScrimAlpha(mScrimBehind, mBehindAlpha);
-        setScrimAlpha(mScrimForBubble, mBubbleAlpha);
+
+        setScrimInFrontAlpha(mCurrentInFrontAlpha);
+        setScrimBehindAlpha(mCurrentBehindAlpha);
+
         dispatchScrimsVisible();
     }
 
@@ -600,11 +580,11 @@
     private void dispatchScrimsVisible() {
         final int currentScrimVisibility;
         if (mScrimInFront.getViewAlpha() == 1 || mScrimBehind.getViewAlpha() == 1) {
-            currentScrimVisibility = OPAQUE;
+            currentScrimVisibility = VISIBILITY_FULLY_OPAQUE;
         } else if (mScrimInFront.getViewAlpha() == 0 && mScrimBehind.getViewAlpha() == 0) {
-            currentScrimVisibility = TRANSPARENT;
+            currentScrimVisibility = VISIBILITY_FULLY_TRANSPARENT;
         } else {
-            currentScrimVisibility = SEMI_TRANSPARENT;
+            currentScrimVisibility = VISIBILITY_SEMI_TRANSPARENT;
         }
 
         if (mScrimsVisibility != currentScrimVisibility) {
@@ -621,10 +601,18 @@
             return 0;
         } else {
             // woo, special effects
-            return (float) (1f - 0.5f * (1f - Math.cos(3.14159f * Math.pow(1f - frac, 2f))));
+            return (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
         }
     }
 
+    private void setScrimBehindAlpha(float alpha) {
+        setScrimAlpha(mScrimBehind, alpha);
+    }
+
+    private void setScrimInFrontAlpha(float alpha) {
+        setScrimAlpha(mScrimInFront, alpha);
+    }
+
     private void setScrimAlpha(ScrimView scrim, float alpha) {
         if (alpha == 0f) {
             scrim.setClickable(false);
@@ -635,26 +623,17 @@
         updateScrim(scrim, alpha);
     }
 
-    private String getScrimName(ScrimView scrim) {
-        if (scrim == mScrimInFront) {
-            return "front_scrim";
-        } else if (scrim == mScrimBehind) {
-            return "back_scrim";
-        } else if (scrim == mScrimForBubble) {
-            return "bubble_scrim";
-        }
-        return "unknown_scrim";
-    }
-
     private void updateScrimColor(View scrim, float alpha, int tint) {
         alpha = Math.max(0, Math.min(1.0f, alpha));
         if (scrim instanceof ScrimView) {
             ScrimView scrimView = (ScrimView) scrim;
 
-            Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_alpha",
+            Trace.traceCounter(Trace.TRACE_TAG_APP,
+                    scrim == mScrimInFront ? "front_scrim_alpha" : "back_scrim_alpha",
                     (int) (alpha * 255));
 
-            Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint",
+            Trace.traceCounter(Trace.TRACE_TAG_APP,
+                    scrim == mScrimInFront ? "front_scrim_tint" : "back_scrim_tint",
                     Color.alpha(tint));
 
             scrimView.setTint(tint);
@@ -711,11 +690,9 @@
 
     private float getCurrentScrimAlpha(View scrim) {
         if (scrim == mScrimInFront) {
-            return mInFrontAlpha;
+            return mCurrentInFrontAlpha;
         } else if (scrim == mScrimBehind) {
-            return mBehindAlpha;
-        } else if (scrim == mScrimForBubble) {
-            return mBubbleAlpha;
+            return mCurrentBehindAlpha;
         } else {
             throw new IllegalArgumentException("Unknown scrim view");
         }
@@ -723,11 +700,9 @@
 
     private int getCurrentScrimTint(View scrim) {
         if (scrim == mScrimInFront) {
-            return mInFrontTint;
+            return mCurrentInFrontTint;
         } else if (scrim == mScrimBehind) {
-            return mBehindTint;
-        } else if (scrim == mScrimForBubble) {
-            return mBubbleTint;
+            return mCurrentBehindTint;
         } else {
             throw new IllegalArgumentException("Unknown scrim view");
         }
@@ -754,16 +729,6 @@
     }
 
     private void onFinished(Callback callback) {
-        if (!hasReachedFinalState(mScrimBehind)
-            || !hasReachedFinalState(mScrimInFront)
-            || !hasReachedFinalState(mScrimForBubble)) {
-            if (callback != null && callback != mCallback) {
-                // Since we only notify the callback that we're finished once everything has
-                // finished, we need to make sure that any changing callbacks are also invoked
-                callback.onFinished();
-            }
-            return;
-        }
         if (mWakeLockHeld) {
             mWakeLock.release(TAG);
             mWakeLockHeld = false;
@@ -780,20 +745,11 @@
         // When unlocking with fingerprint, we'll fade the scrims from black to transparent.
         // At the end of the animation we need to remove the tint.
         if (mState == ScrimState.UNLOCKED) {
-            mInFrontTint = Color.TRANSPARENT;
-            mBehindTint = Color.TRANSPARENT;
-            mBubbleTint = Color.TRANSPARENT;
-            updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint);
-            updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint);
-            updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint);
+            mCurrentInFrontTint = Color.TRANSPARENT;
+            mCurrentBehindTint = Color.TRANSPARENT;
         }
     }
 
-    private boolean hasReachedFinalState(ScrimView scrim) {
-        return scrim.getViewAlpha() == getCurrentScrimAlpha(scrim)
-                && scrim.getTint() == getCurrentScrimTint(scrim);
-    }
-
     private boolean isAnimating(View scrim) {
         return scrim.getTag(TAG_KEY_ANIM) != null;
     }
@@ -895,7 +851,6 @@
 
     /**
      * Executes a callback after the frame has hit the display.
-     *
      * @param callback What to run.
      */
     @VisibleForTesting
@@ -939,35 +894,16 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println(" ScrimController: ");
-        pw.print("  state: ");
-        pw.println(mState);
+        pw.print("  state: "); pw.println(mState);
+        pw.print("  frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha());
+        pw.print(" alpha="); pw.print(mCurrentInFrontAlpha);
+        pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimInFront.getTint()));
 
-        pw.print("  frontScrim:");
-        pw.print(" viewAlpha=");
-        pw.print(mScrimInFront.getViewAlpha());
-        pw.print(" alpha=");
-        pw.print(mInFrontAlpha);
-        pw.print(" tint=0x");
-        pw.println(Integer.toHexString(mScrimInFront.getTint()));
+        pw.print("  backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha());
+        pw.print(" alpha="); pw.print(mCurrentBehindAlpha);
+        pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimBehind.getTint()));
 
-        pw.print("  backScrim:");
-        pw.print(" viewAlpha=");
-        pw.print(mScrimBehind.getViewAlpha());
-        pw.print(" alpha=");
-        pw.print(mBehindAlpha);
-        pw.print(" tint=0x");
-        pw.println(Integer.toHexString(mScrimBehind.getTint()));
-
-        pw.print("  bubbleScrim:");
-        pw.print(" viewAlpha=");
-        pw.print(mScrimForBubble.getViewAlpha());
-        pw.print(" alpha=");
-        pw.print(mBubbleAlpha);
-        pw.print(" tint=0x");
-        pw.println(Integer.toHexString(mScrimForBubble.getTint()));
-
-        pw.print("   mTracking=");
-        pw.println(mTracking);
+        pw.print("   mTracking="); pw.println(mTracking);
     }
 
     public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
@@ -1014,8 +950,8 @@
         // in this case, back-scrim needs to be re-evaluated
         if (mState == ScrimState.AOD || mState == ScrimState.PULSING) {
             float newBehindAlpha = mState.getBehindAlpha();
-            if (mBehindAlpha != newBehindAlpha) {
-                mBehindAlpha = newBehindAlpha;
+            if (mCurrentBehindAlpha != newBehindAlpha) {
+                mCurrentBehindAlpha = newBehindAlpha;
                 updateScrims();
             }
         }
@@ -1036,13 +972,10 @@
     public interface Callback {
         default void onStart() {
         }
-
         default void onDisplayBlanked() {
         }
-
         default void onFinished() {
         }
-
         default void onCancelled() {
         }
         /** Returns whether to timeout wallpaper or not. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 7463c7c..5fa861c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -36,6 +36,7 @@
      * On the lock screen.
      */
     KEYGUARD(0) {
+
         @Override
         public void prepare(ScrimState previousState) {
             mBlankScreen = false;
@@ -52,13 +53,10 @@
             } else {
                 mAnimationDuration = ScrimController.ANIMATION_DURATION;
             }
-            mFrontTint = Color.BLACK;
-            mBehindTint = Color.BLACK;
-            mBubbleTint = Color.TRANSPARENT;
-
-            mFrontAlpha = 0;
-            mBehindAlpha = mScrimBehindAlphaKeyguard;
-            mBubbleAlpha = 0;
+            mCurrentInFrontTint = Color.BLACK;
+            mCurrentBehindTint = Color.BLACK;
+            mCurrentBehindAlpha = mScrimBehindAlphaKeyguard;
+            mCurrentInFrontAlpha = 0;
         }
     },
 
@@ -68,9 +66,8 @@
     BOUNCER(1) {
         @Override
         public void prepare(ScrimState previousState) {
-            mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
-            mFrontAlpha = 0f;
-            mBubbleAlpha = 0f;
+            mCurrentBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
+            mCurrentInFrontAlpha = 0f;
         }
     },
 
@@ -80,9 +77,8 @@
     BOUNCER_SCRIMMED(2) {
         @Override
         public void prepare(ScrimState previousState) {
-            mBehindAlpha = 0;
-            mBubbleAlpha = 0f;
-            mFrontAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
+            mCurrentBehindAlpha = 0;
+            mCurrentInFrontAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
         }
     },
 
@@ -92,9 +88,8 @@
     BRIGHTNESS_MIRROR(3) {
         @Override
         public void prepare(ScrimState previousState) {
-            mBehindAlpha = 0;
-            mFrontAlpha = 0;
-            mBubbleAlpha = 0;
+            mCurrentBehindAlpha = 0;
+            mCurrentInFrontAlpha = 0;
         }
     },
 
@@ -106,16 +101,9 @@
         public void prepare(ScrimState previousState) {
             final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
             mBlankScreen = mDisplayRequiresBlanking;
-
-            mFrontTint = Color.BLACK;
-            mFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f;
-
-            mBehindTint = Color.BLACK;
-            mBehindAlpha = ScrimController.TRANSPARENT;
-
-            mBubbleTint = Color.TRANSPARENT;
-            mBubbleAlpha = ScrimController.TRANSPARENT;
-
+            mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f;
+            mCurrentInFrontTint = Color.BLACK;
+            mCurrentBehindTint = Color.BLACK;
             mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
             // DisplayPowerManager may blank the screen for us,
             // in this case we just need to set our state.
@@ -139,10 +127,9 @@
     PULSING(5) {
         @Override
         public void prepare(ScrimState previousState) {
-            mFrontAlpha = mAodFrontScrimAlpha;
-            mBubbleAlpha = 0f;
-            mBehindTint = Color.BLACK;
-            mFrontTint = Color.BLACK;
+            mCurrentInFrontAlpha = mAodFrontScrimAlpha;
+            mCurrentBehindTint = Color.BLACK;
+            mCurrentInFrontTint = Color.BLACK;
             mBlankScreen = mDisplayRequiresBlanking;
             mAnimationDuration = mWakeLockScreenSensorActive
                     ? ScrimController.ANIMATION_DURATION_LONG : ScrimController.ANIMATION_DURATION;
@@ -167,33 +154,26 @@
     UNLOCKED(6) {
         @Override
         public void prepare(ScrimState previousState) {
-            // State that UI will sync to.
-            mBehindAlpha = 0;
-            mFrontAlpha = 0;
-            mBubbleAlpha = 0;
-
+            mCurrentBehindAlpha = 0;
+            mCurrentInFrontAlpha = 0;
             mAnimationDuration = mKeyguardFadingAway
                     ? mKeyguardFadingAwayDuration
                     : StatusBar.FADE_KEYGUARD_DURATION;
 
             mAnimateChange = !mLaunchingAffordanceWithPreview;
 
-            mFrontTint = Color.TRANSPARENT;
-            mBehindTint = Color.TRANSPARENT;
-            mBubbleTint = Color.TRANSPARENT;
-            mBlankScreen = false;
-
             if (previousState == ScrimState.AOD) {
-                // Set all scrims black, before they fade transparent.
-                updateScrimColor(mScrimInFront, 1f /* alpha */, Color.BLACK /* tint */);
-                updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK /* tint */);
-                updateScrimColor(mScrimForBubble, 1f /* alpha */, Color.BLACK /* tint */);
-
+                // Fade from black to transparent when coming directly from AOD
+                updateScrimColor(mScrimInFront, 1, Color.BLACK);
+                updateScrimColor(mScrimBehind, 1, Color.BLACK);
                 // Scrims should still be black at the end of the transition.
-                mFrontTint = Color.BLACK;
-                mBehindTint = Color.BLACK;
-                mBubbleTint = Color.BLACK;
+                mCurrentInFrontTint = Color.BLACK;
+                mCurrentBehindTint = Color.BLACK;
                 mBlankScreen = true;
+            } else {
+                mCurrentInFrontTint = Color.TRANSPARENT;
+                mCurrentBehindTint = Color.TRANSPARENT;
+                mBlankScreen = false;
             }
         }
     },
@@ -204,36 +184,25 @@
     BUBBLE_EXPANDED(7) {
         @Override
         public void prepare(ScrimState previousState) {
-            mFrontTint = Color.TRANSPARENT;
-            mBehindTint = Color.TRANSPARENT;
-            mBubbleTint = Color.TRANSPARENT;
-
-            mFrontAlpha = ScrimController.TRANSPARENT;
-            mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
-            mBubbleAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
-
+            mCurrentInFrontTint = Color.TRANSPARENT;
+            mCurrentBehindTint = Color.TRANSPARENT;
             mAnimationDuration = ScrimController.ANIMATION_DURATION;
+            mCurrentBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
             mBlankScreen = false;
         }
     };
 
     boolean mBlankScreen = false;
     long mAnimationDuration = ScrimController.ANIMATION_DURATION;
-    int mFrontTint = Color.TRANSPARENT;
-    int mBehindTint = Color.TRANSPARENT;
-    int mBubbleTint = Color.TRANSPARENT;
-
+    int mCurrentInFrontTint = Color.TRANSPARENT;
+    int mCurrentBehindTint = Color.TRANSPARENT;
     boolean mAnimateChange = true;
+    float mCurrentInFrontAlpha;
+    float mCurrentBehindAlpha;
     float mAodFrontScrimAlpha;
-    float mFrontAlpha;
-    float mBehindAlpha;
-    float mBubbleAlpha;
-
     float mScrimBehindAlphaKeyguard;
     ScrimView mScrimInFront;
     ScrimView mScrimBehind;
-    ScrimView mScrimForBubble;
-
     DozeParameters mDozeParameters;
     boolean mDisplayRequiresBlanking;
     boolean mWallpaperSupportsAmbientMode;
@@ -248,17 +217,13 @@
         mIndex = index;
     }
 
-    public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble,
-            DozeParameters dozeParameters) {
+    public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters) {
         mScrimInFront = scrimInFront;
         mScrimBehind = scrimBehind;
-        mScrimForBubble = scrimForBubble;
-
         mDozeParameters = dozeParameters;
         mDisplayRequiresBlanking = dozeParameters.getDisplayNeedsBlanking();
     }
 
-    /** Prepare state for transition. */
     public void prepare(ScrimState previousState) {
     }
 
@@ -267,27 +232,19 @@
     }
 
     public float getFrontAlpha() {
-        return mFrontAlpha;
+        return mCurrentInFrontAlpha;
     }
 
     public float getBehindAlpha() {
-        return mBehindAlpha;
-    }
-
-    public float getBubbleAlpha() {
-        return mBubbleAlpha;
+        return mCurrentBehindAlpha;
     }
 
     public int getFrontTint() {
-        return mFrontTint;
+        return mCurrentInFrontTint;
     }
 
     public int getBehindTint() {
-        return mBehindTint;
-    }
-
-    public int getBubbleTint() {
-        return mBubbleTint;
+        return mCurrentBehindTint;
     }
 
     public long getAnimationDuration() {
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 fc20d83..559df18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -229,6 +229,7 @@
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.InjectionInflationController;
 import com.android.systemui.volume.VolumeComponent;
 
@@ -811,7 +812,6 @@
         // TODO: Deal with the ugliness that comes from having some of the statusbar broken out
         // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
         mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
-
         mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
         mZenController.addCallback(this);
         NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller;
@@ -932,10 +932,8 @@
 
         ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
         ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
-        ScrimView scrimForBubble = mStatusBarWindow.findViewById(R.id.scrim_for_bubble);
-
         mScrimController = SystemUIFactory.getInstance().createScrimController(
-                scrimBehind, scrimInFront, scrimForBubble, mLockscreenWallpaper,
+                scrimBehind, scrimInFront, mLockscreenWallpaper,
                 (state, alpha, color) -> mLightBarController.setScrimState(state, alpha, color),
                 scrimsVisible -> {
                     if (mStatusBarWindowController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0c47d14..68ee8bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -62,6 +62,8 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
+import androidx.annotation.VisibleForTesting;
+
 /**
  * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
  * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
@@ -161,6 +163,7 @@
     private boolean mLastLockVisible;
 
     private OnDismissAction mAfterKeyguardGoneAction;
+    private Runnable mKeyguardGoneCancelAction;
     private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
 
     // Dismiss action to be launched when we stop dozing or the keyguard is gone.
@@ -328,10 +331,20 @@
         return false;
     }
 
-    private void hideBouncer(boolean destroyView) {
+    @VisibleForTesting
+    void hideBouncer(boolean destroyView) {
         if (mBouncer == null) {
             return;
         }
+        if (mShowing) {
+            // If we were showing the bouncer and then aborting, we need to also clear out any
+            // potential actions unless we actually unlocked.
+            mAfterKeyguardGoneAction = null;
+            if (mKeyguardGoneCancelAction != null) {
+                mKeyguardGoneCancelAction.run();
+                mKeyguardGoneCancelAction = null;
+            }
+        }
         mBouncer.hide(destroyView);
         cancelPendingWakeupAction();
     }
@@ -364,6 +377,7 @@
                 mBouncer.showWithDismissAction(r, cancelAction);
             } else {
                 mAfterKeyguardGoneAction = r;
+                mKeyguardGoneCancelAction = cancelAction;
                 mBouncer.show(false /* resetSecuritySelection */);
             }
         }
@@ -671,6 +685,7 @@
             mAfterKeyguardGoneAction.onDismiss();
             mAfterKeyguardGoneAction = null;
         }
+        mKeyguardGoneCancelAction = null;
         for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) {
             mAfterKeyguardGoneRunnables.get(i).run();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index d3ae5cf..946fe0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -222,7 +222,7 @@
         }
 
         final boolean scrimsOccludingWallpaper =
-                state.scrimsVisibility == ScrimController.OPAQUE;
+                state.scrimsVisibility == ScrimController.VISIBILITY_FULLY_OPAQUE;
         final boolean keyguardOrAod = state.keyguardShowing
                 || (state.dozing && mDozeParameters.getAlwaysOn());
         if (keyguardOrAod && !state.backdropShowing && !scrimsOccludingWallpaper) {
@@ -308,7 +308,7 @@
         return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
                 || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
                 || state.headsUpShowing || state.bubblesShowing
-                || state.scrimsVisibility != ScrimController.TRANSPARENT);
+                || state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_TRANSPARENT);
     }
 
     private void applyFitsSystemWindows(State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 111cdd2..738d076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -45,9 +45,7 @@
     /**
      * Returns {@code true} if AOD was disabled by power saving policies.
      */
-    default boolean isAodPowerSave() {
-        return isPowerSave();
-    }
+    boolean isAodPowerSave();
 
     /**
      * A listener that will be notified whenever a change in battery level or power save mode has
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index ba434d4..5a4e6c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -74,6 +74,7 @@
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -173,7 +174,8 @@
         TestableNotificationInterruptionStateProvider interruptionStateProvider =
                 new TestableNotificationInterruptionStateProvider(mContext,
                         mock(NotificationFilter.class),
-                        mock(StatusBarStateController.class));
+                        mock(StatusBarStateController.class),
+                        mock(BatteryController.class));
         interruptionStateProvider.setUpWithPresenter(
                 mock(NotificationPresenter.class),
                 mock(HeadsUpManager.class),
@@ -659,8 +661,9 @@
             NotificationInterruptionStateProvider {
 
         TestableNotificationInterruptionStateProvider(Context context,
-                NotificationFilter filter, StatusBarStateController controller) {
-            super(context, filter, controller);
+                NotificationFilter filter, StatusBarStateController controller,
+                BatteryController batteryController) {
+            super(context, filter, controller, batteryController);
             mUseHeadsUp = true;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 1e18e51..1eb75aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -46,6 +46,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
 import org.junit.Before;
@@ -76,8 +77,8 @@
         mConfigMock = mock(AmbientDisplayConfiguration.class);
         mPartMock = mock(DozeMachine.Part.class);
 
-        mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake, mWakefulnessLifecycle);
-
+        mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake,
+                mWakefulnessLifecycle, mock(BatteryController.class));
         mMachine.setParts(new DozeMachine.Part[]{mPartMock});
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
index a66cf84..0db1f68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
@@ -53,6 +53,7 @@
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
@@ -85,6 +86,8 @@
     HeadsUpManager mHeadsUpManager;
     @Mock
     NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
+    @Mock
+    BatteryController mBatteryController;
 
     private NotificationInterruptionStateProvider mNotifInterruptionStateProvider;
 
@@ -98,7 +101,8 @@
                         mDreamManager,
                         mAmbientDisplayConfiguration,
                         mNotificationFilter,
-                        mStatusBarStateController);
+                        mStatusBarStateController,
+                        mBatteryController);
 
         mNotifInterruptionStateProvider.setUpWithPresenter(
                 mPresenter,
@@ -573,17 +577,17 @@
     /**
      * Testable class overriding constructor.
      */
-    public class TestableNotificationInterruptionStateProvider extends
+    public static class TestableNotificationInterruptionStateProvider extends
             NotificationInterruptionStateProvider {
 
         TestableNotificationInterruptionStateProvider(Context context,
                 PowerManager powerManager, IDreamManager dreamManager,
                 AmbientDisplayConfiguration ambientDisplayConfiguration,
                 NotificationFilter notificationFilter,
-                StatusBarStateController statusBarStateController) {
+                StatusBarStateController statusBarStateController,
+                BatteryController batteryController) {
             super(context, powerManager, dreamManager, ambientDisplayConfiguration,
-                    notificationFilter,
-                    statusBarStateController);
+                    notificationFilter, batteryController, statusBarStateController);
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index d4ad4b6..205312c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -16,9 +16,9 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.statusbar.phone.ScrimController.OPAQUE;
-import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT;
+import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_FULLY_OPAQUE;
+import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_FULLY_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_SEMI_TRANSPARENT;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -69,7 +69,6 @@
     private SynchronousScrimController mScrimController;
     private ScrimView mScrimBehind;
     private ScrimView mScrimInFront;
-    private ScrimView mScrimForBubble;
     private ScrimState mScrimState;
     private float mScrimBehindAlpha;
     private GradientColors mScrimInFrontColor;
@@ -85,7 +84,6 @@
     public void setup() {
         mScrimBehind = spy(new ScrimView(getContext()));
         mScrimInFront = new ScrimView(getContext());
-        mScrimForBubble = new ScrimView(getContext());
         mWakeLock = mock(WakeLock.class);
         mAlarmManager = mock(AlarmManager.class);
         mAlwaysOnEnabled = true;
@@ -94,7 +92,6 @@
         when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
         when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
         mScrimController = new SynchronousScrimController(mScrimBehind, mScrimInFront,
-                mScrimForBubble,
                 (scrimState, scrimBehindAlpha, scrimInFrontColor) -> {
                     mScrimState = scrimState;
                     mScrimBehindAlpha = scrimBehindAlpha;
@@ -117,28 +114,21 @@
     public void transitionToKeyguard() {
         mScrimController.transitionTo(ScrimState.KEYGUARD);
         mScrimController.finishAnimationsImmediately();
-
-        assertScrimAlpha(TRANSPARENT /* front */,
-                SEMI_TRANSPARENT /* back */,
-                TRANSPARENT /* bubble */);
-
-        assertScrimTint(true /* front */,
-                true /* behind */,
-                false /* bubble */);
+        // Front scrim should be transparent
+        // Back scrim should be visible without tint
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
+        assertScrimTint(mScrimBehind, true /* tinted */);
     }
 
     @Test
     public void transitionToAod_withRegularWallpaper() {
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
-
-        assertScrimAlpha(TRANSPARENT /* front */,
-                OPAQUE /* back */,
-                TRANSPARENT /* bubble */);
-
-        assertScrimTint(true /* front */,
-                true /* behind */,
-                false /* bubble */);
+        // Front scrim should be transparent
+        // Back scrim should be visible with tint
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimTint(mScrimBehind, true /* tinted */);
+        assertScrimTint(mScrimInFront, true /* tinted */);
     }
 
     @Test
@@ -146,18 +136,14 @@
         mScrimController.setWallpaperSupportsAmbientMode(true);
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
-
-        assertScrimAlpha(TRANSPARENT /* front */,
-                TRANSPARENT /* back */,
-                TRANSPARENT /* bubble */);
+        // Front scrim should be transparent
+        // Back scrim should be transparent
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
 
         // Pulsing notification should conserve AOD wallpaper.
         mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
-
-        assertScrimAlpha(TRANSPARENT /* front */,
-                TRANSPARENT /* back */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
     }
 
     @Test
@@ -166,14 +152,11 @@
         mScrimController.setWallpaperSupportsAmbientMode(true);
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
-
-        assertScrimAlpha(TRANSPARENT /* front */,
-                OPAQUE /* back */,
-                TRANSPARENT /* bubble */);
-
-        assertScrimTint(true /* front */,
-                true /* behind */,
-                false /* bubble */);
+        // Front scrim should be transparent
+        // Back scrim should be visible with tint
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimTint(mScrimBehind, true /* tinted */);
+        assertScrimTint(mScrimInFront, true /* tinted */);
     }
 
     @Test
@@ -183,14 +166,11 @@
         mScrimController.finishAnimationsImmediately();
         mScrimController.setHasBackdrop(true);
         mScrimController.finishAnimationsImmediately();
-
-        assertScrimAlpha(TRANSPARENT /* front */,
-                OPAQUE /* back */,
-                TRANSPARENT /* bubble */);
-
-        assertScrimTint(true /* front */,
-                true /* behind */,
-                false /* bubble */);
+        // Front scrim should be transparent
+        // Back scrim should be visible with tint
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimTint(mScrimBehind, true /* tinted */);
+        assertScrimTint(mScrimInFront, true /* tinted */);
     }
 
     @Test
@@ -199,32 +179,27 @@
         mScrimController.transitionTo(ScrimState.KEYGUARD);
         mScrimController.setAodFrontScrimAlpha(0.5f);
         mScrimController.finishAnimationsImmediately();
-
-        assertScrimAlpha(TRANSPARENT /* front */,
-                SEMI_TRANSPARENT /* back */,
-                TRANSPARENT /* bubble */);
+        // Front scrim should be transparent
+        // Back scrim should be visible without tint
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
 
         // ... but that it does take effect once we enter the AOD state.
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
-        assertScrimAlpha(SEMI_TRANSPARENT /* front */,
-                OPAQUE /* back */,
-                TRANSPARENT /* bubble */);
+        // Front scrim should be semi-transparent
+        // Back scrim should be visible
+        assertScrimVisibility(VISIBILITY_SEMI_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
 
         // ... and that if we set it while we're in AOD, it does take immediate effect.
         mScrimController.setAodFrontScrimAlpha(1f);
-        assertScrimAlpha(OPAQUE /* front */,
-                OPAQUE /* back */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_OPAQUE, VISIBILITY_FULLY_OPAQUE);
 
         // ... and make sure we recall the previous front scrim alpha even if we transition away
         // for a bit.
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
-        assertScrimAlpha(OPAQUE /* front */,
-                OPAQUE /* back */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_OPAQUE, VISIBILITY_FULLY_OPAQUE);
 
         // ... and alpha updates should be completely ignored if always_on is off.
         // Passing it forward would mess up the wake-up transition.
@@ -248,36 +223,27 @@
         mScrimController.setWallpaperSupportsAmbientMode(false);
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
-        assertScrimAlpha(TRANSPARENT /* front */,
-                OPAQUE /* back */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
 
         mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be transparent, but tinted
         // Back scrim should be semi-transparent so the user can see the wallpaper
         // Pulse callback should have been invoked
-        assertScrimAlpha(TRANSPARENT /* front */,
-                OPAQUE /* back */,
-                TRANSPARENT /* bubble */);
-
-        assertScrimTint(true /* front */,
-                true /* behind */,
-                false /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimTint(mScrimBehind, true /* tinted */);
 
         // ... and when ambient goes dark, front scrim should be semi-transparent
         mScrimController.setAodFrontScrimAlpha(0.5f);
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be semi-transparent
-        assertScrimAlpha(SEMI_TRANSPARENT /* front */,
-                OPAQUE /* back */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_SEMI_TRANSPARENT /* front */,
+                VISIBILITY_FULLY_OPAQUE /* back */);
 
         mScrimController.setWakeLockScreenSensorActive(true);
         mScrimController.finishAnimationsImmediately();
-        assertScrimAlpha(SEMI_TRANSPARENT /* front */,
-                SEMI_TRANSPARENT /* back */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_SEMI_TRANSPARENT /* front */,
+                VISIBILITY_SEMI_TRANSPARENT /* back */);
 
         // Reset value since enums are static.
         mScrimController.setAodFrontScrimAlpha(0f);
@@ -289,13 +255,8 @@
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be transparent
         // Back scrim should be visible without tint
-        assertScrimAlpha(TRANSPARENT /* front */,
-                SEMI_TRANSPARENT /* back */,
-                TRANSPARENT /* bubble */);
-
-        assertScrimTint(false /* front */,
-                false /* behind */,
-                false /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
+        assertScrimTint(mScrimBehind, false /* tinted */);
     }
 
     @Test
@@ -304,12 +265,8 @@
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be transparent
         // Back scrim should be visible without tint
-        assertScrimAlpha(SEMI_TRANSPARENT /* front */,
-                TRANSPARENT /* back */,
-                TRANSPARENT /* bubble */);
-        assertScrimTint(false /* front */,
-                false /* behind */,
-                false /* bubble */);
+        assertScrimVisibility(VISIBILITY_SEMI_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
+        assertScrimTint(mScrimBehind, false /* tinted */);
     }
 
     @Test
@@ -317,19 +274,15 @@
         mScrimController.setPanelExpansion(0f);
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         mScrimController.finishAnimationsImmediately();
-        assertScrimAlpha(TRANSPARENT /* front */,
-                TRANSPARENT /* back */,
-                TRANSPARENT /* bubble */);
-
-        assertScrimTint(false /* front */,
-                false /* behind */,
-                false /* bubble */);
+        // Front scrim should be transparent
+        // Back scrim should be transparent
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
+        assertScrimTint(mScrimBehind, false /* tinted */);
+        assertScrimTint(mScrimInFront, false /* tinted */);
 
         // Back scrim should be visible after start dragging
         mScrimController.setPanelExpansion(0.5f);
-        assertScrimAlpha(TRANSPARENT /* front */,
-                SEMI_TRANSPARENT /* back */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
     }
 
     @Test
@@ -337,19 +290,12 @@
         mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED);
         mScrimController.finishAnimationsImmediately();
 
-        assertScrimTint(false /* front */,
-                false /* behind */,
-                false /* bubble */);
-
         // Front scrim should be transparent
-        Assert.assertEquals(ScrimController.TRANSPARENT,
+        Assert.assertEquals(ScrimController.VISIBILITY_FULLY_TRANSPARENT,
                 mScrimInFront.getViewAlpha(), 0.0f);
         // Back scrim should be visible
         Assert.assertEquals(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
                 mScrimBehind.getViewAlpha(), 0.0f);
-        // Bubble scrim should be visible
-        Assert.assertEquals(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
-                mScrimBehind.getViewAlpha(), 0.0f);
     }
 
     @Test
@@ -418,23 +364,16 @@
         mScrimController.setPanelExpansion(0f);
         mScrimController.finishAnimationsImmediately();
         mScrimController.transitionTo(ScrimState.UNLOCKED);
-
-        // Immediately tinted black after the transition starts
-        assertScrimTint(true /* front */,
-                true /* behind */,
-                true  /* bubble */);
-
+        // Immediately tinted after the transition starts
+        assertScrimTint(mScrimInFront, true /* tinted */);
+        assertScrimTint(mScrimBehind, true /* tinted */);
         mScrimController.finishAnimationsImmediately();
-
-        // All scrims should be transparent at the end of fade transition.
-        assertScrimAlpha(TRANSPARENT /* front */,
-                TRANSPARENT /* behind */,
-                TRANSPARENT /* bubble */);
-
-        // Make sure at the very end of the animation, we're reset to transparent
-        assertScrimTint(false /* front */,
-                false /* behind */,
-                false  /* bubble */);
+        // Front scrim should be transparent
+        // Back scrim should be transparent
+        // Neither scrims should be tinted anymore after the animation.
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
+        assertScrimTint(mScrimInFront, false /* tinted */);
+        assertScrimTint(mScrimBehind, false /* tinted */);
     }
 
     @Test
@@ -449,11 +388,9 @@
                         // Front scrim should be black in the middle of the transition
                         Assert.assertTrue("Scrim should be visible during transition. Alpha: "
                                 + mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
-                        assertScrimTint(true /* front */,
-                                true /* behind */,
-                                true /* bubble */);
+                        assertScrimTint(mScrimInFront, true /* tinted */);
                         Assert.assertSame("Scrim should be visible during transition.",
-                                mScrimVisibility, OPAQUE);
+                                mScrimVisibility, VISIBILITY_FULLY_OPAQUE);
                     }
                 });
         mScrimController.finishAnimationsImmediately();
@@ -661,15 +598,11 @@
         mScrimController.setKeyguardOccluded(true);
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
-        assertScrimAlpha(TRANSPARENT /* front */,
-                OPAQUE /* behind */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
 
         mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
-        assertScrimAlpha(TRANSPARENT /* front */,
-                OPAQUE /* behind */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
     }
 
     @Test
@@ -677,15 +610,11 @@
         mScrimController.setWallpaperSupportsAmbientMode(true);
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
-        assertScrimAlpha(TRANSPARENT /* front */,
-                TRANSPARENT /* behind */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
 
         mScrimController.setKeyguardOccluded(true);
         mScrimController.finishAnimationsImmediately();
-        assertScrimAlpha(TRANSPARENT /* front */,
-                OPAQUE /* behind */,
-                TRANSPARENT /* bubble */);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
     }
 
     @Test
@@ -724,68 +653,33 @@
                 mScrimInFront.getDefaultFocusHighlightEnabled());
         Assert.assertFalse("Scrim shouldn't have focus highlight",
                 mScrimBehind.getDefaultFocusHighlightEnabled());
-        Assert.assertFalse("Scrim shouldn't have focus highlight",
-                mScrimForBubble.getDefaultFocusHighlightEnabled());
     }
 
-    private void assertScrimTint(boolean front, boolean behind, boolean bubble) {
+    private void assertScrimTint(ScrimView scrimView, boolean tinted) {
+        final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
+        final String name = scrimView == mScrimInFront ? "front" : "back";
         Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
-                        + " with scrim: " + getScrimName(mScrimInFront) + " and tint: "
-                        + Integer.toHexString(mScrimInFront.getTint()),
-                front, mScrimInFront.getTint() != Color.TRANSPARENT);
-
-        Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
-                        + " with scrim: " + getScrimName(mScrimBehind) + " and tint: "
-                        + Integer.toHexString(mScrimBehind.getTint()),
-                behind, mScrimBehind.getTint() != Color.TRANSPARENT);
-
-        Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
-                        + " with scrim: " + getScrimName(mScrimForBubble) + " and tint: "
-                        + Integer.toHexString(mScrimForBubble.getTint()),
-                bubble, mScrimForBubble.getTint() != Color.TRANSPARENT);
+                +" with scrim: " + name + " and tint: " + Integer.toHexString(scrimView.getTint()),
+                tinted, viewIsTinted);
     }
 
-    private String getScrimName(ScrimView scrim) {
-        if (scrim == mScrimInFront) {
-            return "front";
-        } else if (scrim == mScrimBehind) {
-            return "back";
-        } else if (scrim == mScrimForBubble) {
-            return "bubble";
-        }
-        return "unknown_scrim";
-    }
+    private void assertScrimVisibility(int inFront, int behind) {
+        boolean inFrontVisible = inFront != ScrimController.VISIBILITY_FULLY_TRANSPARENT;
+        boolean behindVisible = behind != ScrimController.VISIBILITY_FULLY_TRANSPARENT;
+        Assert.assertEquals("Unexpected front scrim visibility. Alpha is "
+                + mScrimInFront.getViewAlpha(), inFrontVisible, mScrimInFront.getViewAlpha() > 0);
+        Assert.assertEquals("Unexpected back scrim visibility. Alpha is "
+                + mScrimBehind.getViewAlpha(), behindVisible, mScrimBehind.getViewAlpha() > 0);
 
-    private void assertScrimAlpha(int front, int behind, int bubble) {
-        // Check single scrim visibility.
-        Assert.assertEquals("Unexpected front scrim alpha: "
-                        + mScrimInFront.getViewAlpha(),
-                front != TRANSPARENT /* expected */,
-                mScrimInFront.getViewAlpha() > TRANSPARENT /* actual */);
-
-        Assert.assertEquals("Unexpected back scrim alpha: "
-                        + mScrimBehind.getViewAlpha(),
-                behind != TRANSPARENT /* expected */,
-                mScrimBehind.getViewAlpha() > TRANSPARENT /* actual */);
-
-        Assert.assertEquals(
-                "Unexpected bubble scrim alpha: "
-                        + mScrimForBubble.getViewAlpha(), /* message */
-                bubble != TRANSPARENT /* expected */,
-                mScrimForBubble.getViewAlpha() > TRANSPARENT /* actual */);
-
-        // Check combined scrim visibility.
         final int visibility;
-        if (front == OPAQUE || behind == OPAQUE || bubble == OPAQUE) {
-            visibility = OPAQUE;
-        } else if (front > TRANSPARENT || behind > TRANSPARENT || bubble > TRANSPARENT) {
-            visibility = SEMI_TRANSPARENT;
+        if (inFront == VISIBILITY_FULLY_OPAQUE || behind == VISIBILITY_FULLY_OPAQUE) {
+            visibility = VISIBILITY_FULLY_OPAQUE;
+        } else if (inFront > VISIBILITY_FULLY_TRANSPARENT || behind > VISIBILITY_FULLY_TRANSPARENT) {
+            visibility = VISIBILITY_SEMI_TRANSPARENT;
         } else {
-            visibility = TRANSPARENT;
+            visibility = VISIBILITY_FULLY_TRANSPARENT;
         }
-        Assert.assertEquals("Invalid visibility.",
-                visibility /* expected */,
-                mScrimVisibility);
+        Assert.assertEquals("Invalid visibility.", visibility, mScrimVisibility);
     }
 
     /**
@@ -797,12 +691,11 @@
         boolean mOnPreDrawCalled;
 
         SynchronousScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
-                ScrimView scrimForBubble,
                 TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
                 Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
                 AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) {
-            super(scrimBehind, scrimInFront, scrimForBubble, scrimStateListener,
-                    scrimVisibleListener, dozeParameters, alarmManager, keyguardMonitor);
+            super(scrimBehind, scrimInFront, scrimStateListener, scrimVisibleListener,
+                    dozeParameters, alarmManager, keyguardMonitor);
         }
 
         @Override
@@ -813,14 +706,13 @@
 
         void finishAnimationsImmediately() {
             boolean[] animationFinished = {false};
-            setOnAnimationFinished(() -> animationFinished[0] = true);
+            setOnAnimationFinished(()-> animationFinished[0] = true);
             // Execute code that will trigger animations.
             onPreDraw();
             // Force finish all animations.
             mLooper.processAllMessages();
             endAnimation(mScrimBehind, TAG_KEY_ANIM);
             endAnimation(mScrimInFront, TAG_KEY_ANIM);
-            endAnimation(mScrimForBubble, TAG_KEY_ANIM);
 
             if (!animationFinished[0]) {
                 throw new IllegalStateException("Animation never finished");
@@ -858,7 +750,6 @@
 
         /**
          * Do not wait for a frame since we're in a test environment.
-         *
          * @param callback What to execute.
          */
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 63f653b..0da0e76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -221,6 +221,31 @@
         verify(mStatusBar, never()).animateKeyguardUnoccluding();
     }
 
+    @Test
+    public void testHiding_cancelsGoneRunnable() {
+        OnDismissAction action = mock(OnDismissAction.class);
+        Runnable cancelAction = mock(Runnable.class);
+        mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
+                true /* afterKeyguardGone */);
+
+        mStatusBarKeyguardViewManager.hideBouncer(true);
+        mStatusBarKeyguardViewManager.hide(0, 30);
+        verify(action, never()).onDismiss();
+        verify(cancelAction).run();
+    }
+
+    @Test
+    public void testHiding_doesntCancelWhenShowing() {
+        OnDismissAction action = mock(OnDismissAction.class);
+        Runnable cancelAction = mock(Runnable.class);
+        mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
+                true /* afterKeyguardGone */);
+
+        mStatusBarKeyguardViewManager.hide(0, 30);
+        verify(action).onDismiss();
+        verify(cancelAction, never()).run();
+    }
+
     private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
 
         public TestableStatusBarKeyguardViewManager(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 178ff22..02215a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -110,6 +110,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -156,6 +157,7 @@
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
     @Mock private StatusBarStateControllerImpl mStatusBarStateController;
+    @Mock private BatteryController mBatteryController;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private StatusBarNotificationPresenter mNotificationPresenter;
     @Mock
@@ -209,7 +211,7 @@
         mNotificationInterruptionStateProvider =
                 new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
                         mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter,
-                        mStatusBarStateController);
+                        mStatusBarStateController, mBatteryController);
         mDependency.injectTestDependency(NotificationInterruptionStateProvider.class,
                 mNotificationInterruptionStateProvider);
         mDependency.injectMockDependency(NavigationBarController.class);
@@ -873,9 +875,10 @@
                 IDreamManager dreamManager,
                 AmbientDisplayConfiguration ambientDisplayConfiguration,
                 NotificationFilter filter,
-                StatusBarStateController controller) {
+                StatusBarStateController controller,
+                BatteryController batteryController) {
             super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter,
-                    controller);
+                    batteryController, controller);
             mUseHeadsUp = true;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index a843cca..df76f01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -48,4 +48,9 @@
     public boolean isPowerSave() {
         return false;
     }
+
+    @Override
+    public boolean isAodPowerSave() {
+        return false;
+    }
 }
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index fbf6ca5..353a187 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1798,12 +1798,12 @@
   optional int32 data_stall_duration_ms = 5;
 
   // Threshold of Tx throughput below which to trigger a data stall
-  // measured in Kbps
-  optional int32 data_stall_tx_tput_thr_kbps = 6;
+  // measured in Mbps
+  optional int32 data_stall_tx_tput_thr_mbps = 6;
 
   // Threshold of Rx throughput below which to trigger a data stall
-  // measured in Kbps
-  optional int32 data_stall_rx_tput_thr_kbps = 7;
+  // measured in Mbps
+  optional int32 data_stall_rx_tput_thr_mbps = 7;
 
   // Threshold of Tx packet error rate above which to trigger a data stall
   // in percentage
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 2ab46e6..4aaf0b7 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -291,6 +291,7 @@
     private boolean mLightEnabled;
     private boolean mDeepEnabled;
     private boolean mQuickDozeActivated;
+    private boolean mQuickDozeActivatedWhileIdling;
     private boolean mForceIdle;
     private boolean mNetworkConnected;
     private boolean mScreenOn;
@@ -302,6 +303,10 @@
     private boolean mHasNetworkLocation;
     private Location mLastGenericLocation;
     private Location mLastGpsLocation;
+
+    /** Time in the elapsed realtime timebase when this listener last received a motion event. */
+    private long mLastMotionEventElapsed;
+
     // Current locked state of the screen
     private boolean mScreenLocked;
     private int mNumBlockingConstraints = 0;
@@ -547,6 +552,9 @@
      */
     private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>();
 
+    private final ArraySet<StationaryListener> mStationaryListeners =
+            new ArraySet<>();
+
     private static final int EVENT_NULL = 0;
     private static final int EVENT_NORMAL = 1;
     private static final int EVENT_LIGHT_IDLE = 2;
@@ -605,6 +613,21 @@
         }
     };
 
+    private final AlarmManager.OnAlarmListener mMotionTimeoutAlarmListener = () -> {
+        synchronized (DeviceIdleController.this) {
+            if (!isStationaryLocked()) {
+                // If the device keeps registering motion, then the alarm should be
+                // rescheduled, so this shouldn't go off until the device is stationary.
+                // This case may happen in a race condition (alarm goes off right before
+                // motion is detected, but handleMotionDetectedLocked is called before
+                // we enter this block).
+                Slog.w(TAG, "motion timeout went off and device isn't stationary");
+                return;
+            }
+        }
+        postStationaryStatusUpdated();
+    };
+
     private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener
             = new AlarmManager.OnAlarmListener() {
         @Override
@@ -654,12 +677,70 @@
         }
     };
 
+    /** Post stationary status only to this listener. */
+    private void postStationaryStatus(StationaryListener listener) {
+        mHandler.obtainMessage(MSG_REPORT_STATIONARY_STATUS, listener).sendToTarget();
+    }
+
+    /** Post stationary status to all registered listeners. */
+    private void postStationaryStatusUpdated() {
+        mHandler.sendEmptyMessage(MSG_REPORT_STATIONARY_STATUS);
+    }
+
+    private boolean isStationaryLocked() {
+        final long now = mInjector.getElapsedRealtime();
+        return mMotionListener.active
+                // Listening for motion for long enough and last motion was long enough ago.
+                && now - Math.max(mMotionListener.activatedTimeElapsed, mLastMotionEventElapsed)
+                >= mConstants.MOTION_INACTIVE_TIMEOUT;
+    }
+
+    @VisibleForTesting
+    void registerStationaryListener(StationaryListener listener) {
+        synchronized (this) {
+            if (!mStationaryListeners.add(listener)) {
+                // Listener already registered.
+                return;
+            }
+            postStationaryStatus(listener);
+            if (mMotionListener.active) {
+                if (!isStationaryLocked() && mStationaryListeners.size() == 1) {
+                    // First listener to be registered and the device isn't stationary, so we
+                    // need to register the alarm to report the device is stationary.
+                    scheduleMotionTimeoutAlarmLocked();
+                }
+            } else {
+                startMonitoringMotionLocked();
+                scheduleMotionTimeoutAlarmLocked();
+            }
+        }
+    }
+
+    private void unregisterStationaryListener(StationaryListener listener) {
+        synchronized (this) {
+            if (mStationaryListeners.remove(listener) && mStationaryListeners.size() == 0
+                    // Motion detection is started when transitioning from INACTIVE to IDLE_PENDING
+                    // and so doesn't need to be on for ACTIVE or INACTIVE states.
+                    // Motion detection isn't needed when idling due to Quick Doze.
+                    && (mState == STATE_ACTIVE || mState == STATE_INACTIVE
+                    || mQuickDozeActivated)) {
+                maybeStopMonitoringMotionLocked();
+            }
+        }
+    }
+
     @VisibleForTesting
     final class MotionListener extends TriggerEventListener
             implements SensorEventListener {
 
         boolean active = false;
 
+        /**
+         * Time in the elapsed realtime timebase when this listener was activated. Only valid if
+         * {@link #active} is true.
+         */
+        long activatedTimeElapsed;
+
         public boolean isActive() {
             return active;
         }
@@ -667,7 +748,6 @@
         @Override
         public void onTrigger(TriggerEvent event) {
             synchronized (DeviceIdleController.this) {
-                active = false;
                 motionLocked();
             }
         }
@@ -675,8 +755,6 @@
         @Override
         public void onSensorChanged(SensorEvent event) {
             synchronized (DeviceIdleController.this) {
-                mSensorManager.unregisterListener(this, mMotionSensor);
-                active = false;
                 motionLocked();
             }
         }
@@ -694,6 +772,7 @@
             }
             if (success) {
                 active = true;
+                activatedTimeElapsed = mInjector.getElapsedRealtime();
             } else {
                 Slog.e(TAG, "Unable to register for " + mMotionSensor);
             }
@@ -1307,6 +1386,8 @@
     private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
     private static final int MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11;
     private static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12;
+    @VisibleForTesting
+    static final int MSG_REPORT_STATIONARY_STATUS = 13;
 
     final class MyHandler extends Handler {
         MyHandler(Looper looper) {
@@ -1443,6 +1524,30 @@
                     updatePreIdleFactor();
                     maybeDoImmediateMaintenance();
                 } break;
+                case MSG_REPORT_STATIONARY_STATUS: {
+                    final StationaryListener newListener = (StationaryListener) msg.obj;
+                    final StationaryListener[] listeners;
+                    final boolean isStationary;
+                    synchronized (DeviceIdleController.this) {
+                        isStationary = isStationaryLocked();
+                        if (newListener == null) {
+                            // Only notify all listeners if we aren't directing to one listener.
+                            listeners = mStationaryListeners.toArray(
+                                    new StationaryListener[mStationaryListeners.size()]);
+                        } else {
+                            listeners = null;
+                        }
+                    }
+                    if (listeners != null) {
+                        for (StationaryListener listener : listeners) {
+                            listener.onDeviceStationaryChanged(isStationary);
+                        }
+                    }
+                    if (newListener != null) {
+                        newListener.onDeviceStationaryChanged(isStationary);
+                    }
+                }
+                break;
             }
         }
     }
@@ -1628,6 +1733,19 @@
         }
     }
 
+    /**
+     * Listener to be notified when DeviceIdleController determines that the device has
+     * moved or is stationary.
+     */
+    public interface StationaryListener {
+        /**
+         * Called when DeviceIdleController has determined that the device is stationary or moving.
+         *
+         * @param isStationary true if the device is stationary, false otherwise
+         */
+        void onDeviceStationaryChanged(boolean isStationary);
+    }
+
     public class LocalService {
         public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) {
             synchronized (DeviceIdleController.this) {
@@ -1693,6 +1811,24 @@
         public int[] getPowerSaveTempWhitelistAppIds() {
             return DeviceIdleController.this.getAppIdTempWhitelistInternal();
         }
+
+        /**
+         * Registers a listener that will be notified when the system has detected that the device
+         * is
+         * stationary or in motion.
+         */
+        public void registerStationaryListener(StationaryListener listener) {
+            DeviceIdleController.this.registerStationaryListener(listener);
+        }
+
+        /**
+         * Unregisters a registered stationary listener from being notified when the system has
+         * detected
+         * that the device is stationary or in motion.
+         */
+        public void unregisterStationaryListener(StationaryListener listener) {
+            DeviceIdleController.this.unregisterStationaryListener(listener);
+        }
     }
 
     static class Injector {
@@ -1734,6 +1870,11 @@
             return mConstants;
         }
 
+        /** Returns the current elapsed realtime in milliseconds. */
+        long getElapsedRealtime() {
+            return SystemClock.elapsedRealtime();
+        }
+
         LocationManager getLocationManager() {
             if (mLocationManager == null) {
                 mLocationManager = mContext.getSystemService(LocationManager.class);
@@ -2601,6 +2742,8 @@
     void updateQuickDozeFlagLocked(boolean enabled) {
         if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled);
         mQuickDozeActivated = enabled;
+        mQuickDozeActivatedWhileIdling =
+                mQuickDozeActivated && (mState == STATE_IDLE || mState == STATE_IDLE_MAINTENANCE);
         if (enabled) {
             // If Quick Doze is enabled, see if we should go straight into it.
             becomeInactiveIfAppropriateLocked();
@@ -2767,10 +2910,11 @@
         mNextIdleDelay = 0;
         mNextLightIdleDelay = 0;
         mIdleStartTime = 0;
+        mQuickDozeActivatedWhileIdling = false;
         cancelAlarmLocked();
         cancelSensingTimeoutAlarmLocked();
         cancelLocatingLocked();
-        stopMonitoringMotionLocked();
+        maybeStopMonitoringMotionLocked();
         mAnyMotionDetector.stop();
         updateActiveConstraintsLocked();
     }
@@ -3270,11 +3414,23 @@
 
     void motionLocked() {
         if (DEBUG) Slog.d(TAG, "motionLocked()");
-        // The motion sensor will have been disabled at this point
+        mLastMotionEventElapsed = mInjector.getElapsedRealtime();
         handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
     }
 
     void handleMotionDetectedLocked(long timeout, String type) {
+        if (mStationaryListeners.size() > 0) {
+            postStationaryStatusUpdated();
+            scheduleMotionTimeoutAlarmLocked();
+        }
+        if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) {
+            // Don't exit idle due to motion if quick doze is enabled.
+            // However, if the device started idling due to the normal progression (going through
+            // all the states) and then had quick doze activated, come out briefly on motion so the
+            // user can get slightly fresher content.
+            return;
+        }
+        maybeStopMonitoringMotionLocked();
         // The device is not yet active, so we want to go back to the pending idle
         // state to wait again for no motion.  Note that we only monitor for motion
         // after moving out of the inactive state, so no need to worry about that.
@@ -3326,10 +3482,15 @@
         }
     }
 
-    void stopMonitoringMotionLocked() {
-        if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
-        if (mMotionSensor != null && mMotionListener.active) {
+    /**
+     * Stops motion monitoring. Will not stop monitoring if there are registered stationary
+     * listeners.
+     */
+    private void maybeStopMonitoringMotionLocked() {
+        if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()");
+        if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) {
             mMotionListener.unregisterLocked();
+            cancelMotionTimeoutAlarmLocked();
         }
     }
 
@@ -3356,6 +3517,10 @@
         }
     }
 
+    private void cancelMotionTimeoutAlarmLocked() {
+        mAlarmManager.cancel(mMotionTimeoutAlarmListener);
+    }
+
     void cancelSensingTimeoutAlarmLocked() {
         if (mNextSensingTimeoutAlarmTime != 0) {
             mNextSensingTimeoutAlarmTime = 0;
@@ -3402,6 +3567,14 @@
                 mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
     }
 
+    private void scheduleMotionTimeoutAlarmLocked() {
+        if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked");
+        long nextMotionTimeoutAlarmTime =
+                mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT;
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
+                "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+    }
+
     void scheduleSensingTimeoutAlarmLocked(long delay) {
         if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
         mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
@@ -4322,9 +4495,14 @@
                 }
                 pw.println("  }");
             }
-            if (mUseMotionSensor) {
+            if (mUseMotionSensor || mStationaryListeners.size() > 0) {
                 pw.print("  mMotionActive="); pw.println(mMotionListener.active);
                 pw.print("  mNotMoving="); pw.println(mNotMoving);
+                pw.print("  mMotionListener.activatedTimeElapsed=");
+                pw.println(mMotionListener.activatedTimeElapsed);
+                pw.print("  mLastMotionEventElapsed="); pw.println(mLastMotionEventElapsed);
+                pw.print("  "); pw.print(mStationaryListeners.size());
+                pw.println(" stationary listeners registered");
             }
             pw.print("  mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
                     pw.print(mHasGps); pw.print(" mHasNetwork=");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3d2ddce..28c506a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18018,7 +18018,7 @@
 
         @Override
         public int getCurrentUserId() {
-            return mUserController.getCurrentUserIdLU();
+            return mUserController.getCurrentUserId();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b311233..598a68e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -134,12 +134,12 @@
     static final int CONTINUE_USER_SWITCH_MSG = 20;
     static final int USER_SWITCH_TIMEOUT_MSG = 30;
     static final int START_PROFILES_MSG = 40;
-    static final int SYSTEM_USER_START_MSG = 50;
-    static final int SYSTEM_USER_CURRENT_MSG = 60;
+    static final int USER_START_MSG = 50;
+    static final int USER_CURRENT_MSG = 60;
     static final int FOREGROUND_PROFILE_CHANGED_MSG = 70;
     static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80;
     static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90;
-    static final int SYSTEM_USER_UNLOCK_MSG = 100;
+    static final int USER_UNLOCK_MSG = 100;
     static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
     static final int START_USER_SWITCH_FG_MSG = 120;
 
@@ -369,16 +369,18 @@
                 }
             }
 
-            mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
-                    userId, 0));
-            Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
-            intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-            intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
-                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-            mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
-                    new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
-                    AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
-                    Binder.getCallingUid(), Binder.getCallingPid(), userId);
+            if (!mInjector.getUserManager().isPreCreated(userId)) {
+                mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
+                        userId, 0));
+                Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
+                intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+                intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+                mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
+                        new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+                        AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
+                        Binder.getCallingUid(), Binder.getCallingPid(), userId);
+            }
         }
 
         // We need to delay unlocking managed profiles until the parent user
@@ -439,8 +441,7 @@
 
             // Dispatch unlocked to system services; when fully dispatched,
             // that calls through to the next "unlocked" phase
-            mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
-                    .sendToTarget();
+            mHandler.obtainMessage(USER_UNLOCK_MSG, userId, 0, uss).sendToTarget();
         });
         return true;
     }
@@ -556,6 +557,17 @@
             }
         }
 
+        if (userInfo.preCreated) {
+            Slog.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
+            // Pre-created user was started right after creation so services could properly
+            // intialize it; it should be stopped right away as it's not really a "real" user.
+            // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
+            // on SystemService instead.
+            stopUser(userInfo.id, /* force= */ true, /* stopUserCallback= */ null,
+                    /* keyEvictedCallback= */ null);
+            return;
+        }
+
         // Spin up app widgets prior to boot-complete, so they can be ready promptly
         mInjector.startUserWidgets(userId);
 
@@ -808,7 +820,8 @@
             mInjector.systemServiceManagerCleanupUser(userId);
             mInjector.stackSupervisorRemoveUser(userId);
             // Remove the user if it is ephemeral.
-            if (getUserInfo(userId).isEphemeral()) {
+            UserInfo userInfo = getUserInfo(userId);
+            if (userInfo.isEphemeral() && !userInfo.preCreated) {
                 mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
             }
 
@@ -985,11 +998,13 @@
      * <ul>
      *     <li>{@link Intent#ACTION_USER_STARTED} - sent to registered receivers of the new user
      *     <li>{@link Intent#ACTION_USER_BACKGROUND} - sent to registered receivers of the outgoing
-     *     user and all profiles of this user. Sent only if {@code foreground} parameter is true
+     *     user and all profiles of this user. Sent only if {@code foreground} parameter is
+     *     {@code false}
      *     <li>{@link Intent#ACTION_USER_FOREGROUND} - sent to registered receivers of the new
-     *     user and all profiles of this user. Sent only if {@code foreground} parameter is true
+     *     user and all profiles of this user. Sent only if {@code foreground} parameter is
+     *     {@code true}
      *     <li>{@link Intent#ACTION_USER_SWITCHED} - sent to registered receivers of the new user.
-     *     Sent only if {@code foreground} parameter is true
+     *     Sent only if {@code foreground} parameter is {@code true}
      *     <li>{@link Intent#ACTION_USER_STARTING} - ordered broadcast sent to registered receivers
      *     of the new fg user
      *     <li>{@link Intent#ACTION_LOCKED_BOOT_COMPLETED} - ordered broadcast sent to receivers of
@@ -1063,6 +1078,11 @@
                 return false;
             }
 
+            if (foreground && userInfo.preCreated) {
+                Slog.w(TAG, "Cannot start pre-created user #" + userId + " as foreground");
+                return false;
+            }
+
             if (foreground && mUserSwitchUiEnabled) {
                 mInjector.getWindowManager().startFreezingScreen(
                         R.anim.screen_user_exit, R.anim.screen_user_enter);
@@ -1157,13 +1177,11 @@
                 // Booting up a new user, need to tell system services about it.
                 // Note that this is on the same handler as scheduling of broadcasts,
                 // which is important because it needs to go first.
-                mHandler.sendMessage(
-                        mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
+                mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, 0));
             }
 
             if (foreground) {
-                mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
-                        oldUserId));
+                mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));
                 mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
                 mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
                 mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
@@ -1172,6 +1190,10 @@
                         oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS);
             }
 
+            if (userInfo.preCreated) {
+                needStart = false;
+            }
+
             if (needStart) {
                 // Send USER_STARTED broadcast
                 Intent intent = new Intent(Intent.ACTION_USER_STARTED);
@@ -2129,13 +2151,13 @@
             case START_PROFILES_MSG:
                 startProfiles();
                 break;
-            case SYSTEM_USER_START_MSG:
+            case USER_START_MSG:
                 mInjector.batteryStatsServiceNoteEvent(
                         BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
                         Integer.toString(msg.arg1), msg.arg1);
                 mInjector.getSystemServiceManager().startUser(msg.arg1);
                 break;
-            case SYSTEM_USER_UNLOCK_MSG:
+            case USER_UNLOCK_MSG:
                 final int userId = msg.arg1;
                 mInjector.getSystemServiceManager().unlockUser(userId);
                 // Loads recents on a worker thread that allows disk I/O
@@ -2144,7 +2166,7 @@
                 });
                 finishUserUnlocked((UserState) msg.obj);
                 break;
-            case SYSTEM_USER_CURRENT_MSG:
+            case USER_CURRENT_MSG:
                 mInjector.batteryStatsServiceNoteEvent(
                         BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
                         Integer.toString(msg.arg2), msg.arg2);
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 0bf43b6..2dc2cf0 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -589,16 +589,18 @@
         if (immediate) {
             dtm.setColorMatrix(tintController.getLevel(), to);
         } else {
-            tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
-                    from == null ? MATRIX_IDENTITY : from, to));
-            tintController.getAnimator().setDuration(TRANSITION_DURATION);
-            tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
+            TintValueAnimator valueAnimator = TintValueAnimator.ofMatrix(COLOR_MATRIX_EVALUATOR,
+                    from == null ? MATRIX_IDENTITY : from, to);
+            tintController.setAnimator(valueAnimator);
+            valueAnimator.setDuration(TRANSITION_DURATION);
+            valueAnimator.setInterpolator(AnimationUtils.loadInterpolator(
                     getContext(), android.R.interpolator.fast_out_slow_in));
-            tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
+            valueAnimator.addUpdateListener((ValueAnimator animator) -> {
                 final float[] value = (float[]) animator.getAnimatedValue();
                 dtm.setColorMatrix(tintController.getLevel(), value);
+                ((TintValueAnimator) animator).updateMinMaxComponents();
             });
-            tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
+            valueAnimator.addListener(new AnimatorListenerAdapter() {
 
                 private boolean mIsCancelled;
 
@@ -609,9 +611,14 @@
 
                 @Override
                 public void onAnimationEnd(Animator animator) {
+                    TintValueAnimator t = (TintValueAnimator) animator;
                     Slog.d(TAG, tintController.getClass().getSimpleName()
                             + " Animation cancelled: " + mIsCancelled
-                            + " to matrix: " + TintController.matrixToString(to, 16));
+                            + " to matrix: " + TintController.matrixToString(to, 16)
+                            + " min matrix coefficients: "
+                            + TintController.matrixToString(t.getMin(), 16)
+                            + " max matrix coefficients: "
+                            + TintController.matrixToString(t.getMax(), 16));
                     if (!mIsCancelled) {
                         // Ensure final color matrix is set at the end of the animation. If the
                         // animation is cancelled then don't set the final color matrix so the new
@@ -621,7 +628,7 @@
                     tintController.setAnimator(null);
                 }
             });
-            tintController.getAnimator().start();
+            valueAnimator.start();
         }
     }
 
@@ -1109,6 +1116,51 @@
     }
 
     /**
+     * Only animates matrices and saves min and max coefficients for logging.
+     */
+    static class TintValueAnimator extends ValueAnimator {
+        private float[] min;
+        private float[] max;
+
+        public static TintValueAnimator ofMatrix(ColorMatrixEvaluator evaluator,
+                Object... values) {
+            TintValueAnimator anim = new TintValueAnimator();
+            anim.setObjectValues(values);
+            anim.setEvaluator(evaluator);
+            if (values == null || values.length == 0) {
+                return null;
+            }
+            float[] m = (float[]) values[0];
+            anim.min = new float[m.length];
+            anim.max = new float[m.length];
+            for (int i = 0; i < m.length; ++i) {
+                anim.min[i] = Float.MAX_VALUE;
+                anim.max[i] = Float.MIN_VALUE;
+            }
+            return anim;
+        }
+
+        public void updateMinMaxComponents() {
+            float[] value = (float[]) getAnimatedValue();
+            if (value == null) {
+                return;
+            }
+            for (int i = 0; i < value.length; ++i) {
+                min[i] = Math.min(min[i], value[i]);
+                max[i] = Math.max(max[i], value[i]);
+            }
+        }
+
+        public float[] getMin() {
+            return min;
+        }
+
+        public float[] getMax() {
+            return max;
+        }
+    }
+
+    /**
      * Interpolates between two 4x4 color transform matrices (in column-major order).
      */
     private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java
index 8d8b9b2..422dd32 100644
--- a/services/core/java/com/android/server/display/color/TintController.java
+++ b/services/core/java/com/android/server/display/color/TintController.java
@@ -24,14 +24,14 @@
 
 abstract class TintController {
 
-    private ValueAnimator mAnimator;
+    private ColorDisplayService.TintValueAnimator mAnimator;
     private Boolean mIsActivated;
 
-    public ValueAnimator getAnimator() {
+    public ColorDisplayService.TintValueAnimator getAnimator() {
         return mAnimator;
     }
 
-    public void setAnimator(ValueAnimator animator) {
+    public void setAnimator(ColorDisplayService.TintValueAnimator animator) {
         mAnimator = animator;
     }
 
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 181a435..4b79677 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -75,6 +75,8 @@
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.location.gnssmetrics.GnssMetrics;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
 import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
 import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
 
@@ -178,6 +180,7 @@
     private static final int AGPS_SUPL_MODE_MSA = 0x02;
     private static final int AGPS_SUPL_MODE_MSB = 0x01;
 
+    private static final int UPDATE_LOW_POWER_MODE = 1;
     private static final int SET_REQUEST = 3;
     private static final int INJECT_NTP_TIME = 5;
     // PSDS stands for Predicted Satellite Data Service
@@ -371,6 +374,12 @@
     private boolean mDisableGpsForPowerManager = false;
 
     /**
+     * True if the device idle controller has determined that the device is stationary. This is only
+     * updated when the device enters idle mode.
+     */
+    private volatile boolean mIsDeviceStationary = false;
+
+    /**
      * Properties loaded from PROPERTIES_FILE.
      * It must be accessed only inside {@link #mHandler}.
      */
@@ -462,6 +471,15 @@
     public GnssNavigationMessageProvider getGnssNavigationMessageProvider() {
         return mGnssNavigationMessageProvider;
     }
+
+    private final DeviceIdleController.StationaryListener mDeviceIdleStationaryListener =
+            isStationary -> {
+                mIsDeviceStationary = isStationary;
+                // Call updateLowPowerMode on handler thread so it's always called from the same
+                // thread.
+                mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
+            };
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -478,11 +496,22 @@
                 case ALARM_TIMEOUT:
                     hibernate();
                     break;
-                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
                 case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+                    DeviceIdleController.LocalService deviceIdleService = LocalServices.getService(
+                            DeviceIdleController.LocalService.class);
+                    if (mPowerManager.isDeviceIdleMode()) {
+                        deviceIdleService.registerStationaryListener(mDeviceIdleStationaryListener);
+                    } else {
+                        deviceIdleService.unregisterStationaryListener(
+                                mDeviceIdleStationaryListener);
+                    }
+                    // Intentional fall-through.
+                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
                 case Intent.ACTION_SCREEN_OFF:
                 case Intent.ACTION_SCREEN_ON:
-                    updateLowPowerMode();
+                    // Call updateLowPowerMode on handler thread so it's always called from the
+                    // same thread.
+                    mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
                     break;
                 case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
                 case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
@@ -540,10 +569,9 @@
     }
 
     private void updateLowPowerMode() {
-        // Disable GPS if we are in device idle mode.
-        boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode();
-        final PowerSaveState result =
-                mPowerManager.getPowerSaveState(ServiceType.LOCATION);
+        // Disable GPS if we are in device idle mode and the device is stationary.
+        boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode() && mIsDeviceStationary;
+        final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION);
         switch (result.locationMode) {
             case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
             case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
@@ -2048,6 +2076,9 @@
                 case REPORT_SV_STATUS:
                     handleReportSvStatus((SvStatusInfo) msg.obj);
                     break;
+                case UPDATE_LOW_POWER_MODE:
+                    updateLowPowerMode();
+                    break;
             }
             if (msg.arg2 == 1) {
                 // wakelock was taken for this message, release it
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index aba7ebb..a213d48 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1463,7 +1463,7 @@
             } else {
                 // Secure => Global
                 Settings.Secure.putInt(resolver,
-                        Settings.Secure.NOTIFICATION_BADGING,
+                        Settings.Secure.NOTIFICATION_BUBBLES,
                         globalSettingValue);
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index e75f545..0329e2c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -491,6 +491,7 @@
 
             params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
             params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
+            params.installFlags &= ~PackageManager.INSTALL_ALLOW_TEST;
             params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
             if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
                     && !mPm.isCallerVerifier(callingUid)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e040e8b..871cefd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -19057,10 +19057,11 @@
      * Tries to delete system package.
      */
     private void deleteSystemPackageLIF(DeletePackageAction action, PackageSetting deletedPs,
-            int[] allUserHandles, int flags, PackageRemovedInfo outInfo, boolean writeSettings)
+            int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
+            boolean writeSettings)
             throws SystemDeleteException {
-        final boolean applyUserRestrictions
-                = (allUserHandles != null) && (outInfo.origUsers != null);
+        final boolean applyUserRestrictions =
+                (allUserHandles != null) && outInfo != null && (outInfo.origUsers != null);
         final PackageParser.Package deletedPkg = deletedPs.pkg;
         // Confirm if the system package has been updated
         // An updated system app can be deleted. This will also have to restore
@@ -19081,19 +19082,21 @@
             }
         }
 
-        // Delete the updated package
-        outInfo.isRemovedPackageSystemUpdate = true;
-        if (outInfo.removedChildPackages != null) {
-            final int childCount = (deletedPs.childPackageNames != null)
-                    ? deletedPs.childPackageNames.size() : 0;
-            for (int i = 0; i < childCount; i++) {
-                String childPackageName = deletedPs.childPackageNames.get(i);
-                if (disabledPs.childPackageNames != null && disabledPs.childPackageNames
-                        .contains(childPackageName)) {
-                    PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
-                            childPackageName);
-                    if (childInfo != null) {
-                        childInfo.isRemovedPackageSystemUpdate = true;
+        if (outInfo != null) {
+            // Delete the updated package
+            outInfo.isRemovedPackageSystemUpdate = true;
+            if (outInfo.removedChildPackages != null) {
+                final int childCount = (deletedPs.childPackageNames != null)
+                        ? deletedPs.childPackageNames.size() : 0;
+                for (int i = 0; i < childCount; i++) {
+                    String childPackageName = deletedPs.childPackageNames.get(i);
+                    if (disabledPs.childPackageNames != null && disabledPs.childPackageNames
+                            .contains(childPackageName)) {
+                        PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
+                                childPackageName);
+                        if (childInfo != null) {
+                            childInfo.isRemovedPackageSystemUpdate = true;
+                        }
                     }
                 }
             }
@@ -19126,7 +19129,8 @@
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
         try {
             installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles,
-                    outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings);
+                    outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(),
+                    writeSettings);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
                     + e.getMessage());
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 9091168..a9a6bd2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2376,6 +2376,7 @@
         int userId = -1;
         int flags = 0;
         String opt;
+        boolean preCreateOnly = false;
         while ((opt = getNextOption()) != null) {
             if ("--profileOf".equals(opt)) {
                 userId = UserHandle.parseUserArg(getNextArgRequired());
@@ -2389,16 +2390,22 @@
                 flags |= UserInfo.FLAG_GUEST;
             } else if ("--demo".equals(opt)) {
                 flags |= UserInfo.FLAG_DEMO;
+            } else if ("--pre-create-only".equals(opt)) {
+                preCreateOnly = true;
             } else {
                 getErrPrintWriter().println("Error: unknown option " + opt);
                 return 1;
             }
         }
         String arg = getNextArg();
-        if (arg == null) {
+        if (arg == null && !preCreateOnly) {
             getErrPrintWriter().println("Error: no user name specified.");
             return 1;
         }
+        if (arg != null && preCreateOnly) {
+            getErrPrintWriter().println("Warning: name is ignored for pre-created users");
+        }
+
         name = arg;
         UserInfo info;
         IUserManager um = IUserManager.Stub.asInterface(
@@ -2412,7 +2419,7 @@
             accm.addSharedAccountsFromParentUser(parentUserId, userId,
                     (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
         } else if (userId < 0) {
-            info = um.createUser(name, flags);
+            info = preCreateOnly ? um.preCreateUser(flags) : um.createUser(name, flags);
         } else {
             info = um.createProfileForUser(name, flags, userId, null);
         }
@@ -3308,8 +3315,11 @@
         pw.println("  trim-caches DESIRED_FREE_SPACE [internal|UUID]");
         pw.println("    Trim cache files to reach the given free space.");
         pw.println("");
+        pw.println("  list users");
+        pw.println("    Lists the current users.");
+        pw.println("");
         pw.println("  create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
-        pw.println("      [--guest] USER_NAME");
+        pw.println("      [--guest] [--pre-create-only] USER_NAME");
         pw.println("    Create a new user with the given USER_NAME, printing the new user identifier");
         pw.println("    of the user.");
         pw.println("");
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 204f186..e23ce9e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -41,6 +41,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.os.Binder;
@@ -66,6 +67,7 @@
 import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManager.EnforcingUser;
@@ -83,6 +85,7 @@
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
+import android.util.TimingsTraceLog;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -155,6 +158,7 @@
     private static final String ATTR_SERIAL_NO = "serialNumber";
     private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
     private static final String ATTR_PARTIAL = "partial";
+    private static final String ATTR_PRE_CREATED = "preCreated";
     private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove";
     private static final String ATTR_USER_VERSION = "version";
     private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId";
@@ -582,7 +586,8 @@
             final int userSize = mUsers.size();
             for (int i = 0; i < userSize; i++) {
                 UserInfo ui = mUsers.valueAt(i).info;
-                if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
+                if ((ui.partial || ui.guestToRemove || (ui.isEphemeral() && !ui.preCreated))
+                        && i != 0) {
                     partials.add(ui);
                     addRemovingUserIdLocked(ui.id);
                     ui.partial = true;
@@ -645,20 +650,25 @@
         return null;
     }
 
-    @Override
     public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
+        return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true);
+    }
+
+    @Override
+    public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+            boolean excludePreCreated) {
         checkManageOrCreateUsersPermission("query users");
         synchronized (mUsersLock) {
             ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
             final int userSize = mUsers.size();
             for (int i = 0; i < userSize; i++) {
                 UserInfo ui = mUsers.valueAt(i).info;
-                if (ui.partial) {
+                if ((excludePartial && ui.partial)
+                        || (excludeDying && mRemovingUserIds.get(ui.id))
+                        || (excludePreCreated && ui.preCreated)) {
                     continue;
                 }
-                if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
-                    users.add(userWithName(ui));
-                }
+                users.add(userWithName(ui));
             }
             return users;
         }
@@ -1179,8 +1189,9 @@
         }
     }
 
-    private void checkManageOrInteractPermIfCallerInOtherProfileGroup(int userId, String name) {
-        int callingUserId = UserHandle.getCallingUserId();
+    private void checkManageOrInteractPermIfCallerInOtherProfileGroup(@UserIdInt int userId,
+            String name) {
+        final int callingUserId = UserHandle.getCallingUserId();
         if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
                 hasManageUsersPermission()) {
             return;
@@ -1193,8 +1204,8 @@
     }
 
     @Override
-    public boolean isDemoUser(int userId) {
-        int callingUserId = UserHandle.getCallingUserId();
+    public boolean isDemoUser(@UserIdInt int userId) {
+        final int callingUserId = UserHandle.getCallingUserId();
         if (callingUserId != userId && !hasManageUsersPermission()) {
             throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
                     + " is a demo user");
@@ -1206,6 +1217,19 @@
     }
 
     @Override
+    public boolean isPreCreated(@UserIdInt int userId) {
+        final int callingUserId = UserHandle.getCallingUserId();
+        if (callingUserId != userId && !hasManageUsersPermission()) {
+            throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
+                    + " is pre-created");
+        }
+        synchronized (mUsersLock) {
+            UserInfo userInfo = getUserInfoLU(userId);
+            return userInfo != null && userInfo.preCreated;
+        }
+    }
+
+    @Override
     public boolean isRestricted() {
         synchronized (mUsersLock) {
             return getUserInfoLU(UserHandle.getCallingUserId()).isRestricted();
@@ -1859,7 +1883,7 @@
         // Skip over users being removed
         for (int i = 0; i < totalUserCount; i++) {
             UserInfo user = mUsers.valueAt(i).info;
-            if (!mRemovingUserIds.get(user.id) && !user.isGuest()) {
+            if (!mRemovingUserIds.get(user.id) && !user.isGuest() && !user.preCreated) {
                 aliveUserCount++;
             }
         }
@@ -2320,6 +2344,9 @@
         if (userInfo.partial) {
             serializer.attribute(null, ATTR_PARTIAL, "true");
         }
+        if (userInfo.preCreated) {
+            serializer.attribute(null, ATTR_PRE_CREATED, "true");
+        }
         if (userInfo.guestToRemove) {
             serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true");
         }
@@ -2476,6 +2503,7 @@
         int profileBadge = 0;
         int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID;
         boolean partial = false;
+        boolean preCreated = false;
         boolean guestToRemove = false;
         boolean persistSeedData = false;
         String seedAccountName = null;
@@ -2520,6 +2548,10 @@
             if ("true".equals(valueString)) {
                 partial = true;
             }
+            valueString = parser.getAttributeValue(null, ATTR_PRE_CREATED);
+            if ("true".equals(valueString)) {
+                preCreated = true;
+            }
             valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE);
             if ("true".equals(valueString)) {
                 guestToRemove = true;
@@ -2573,6 +2605,7 @@
         userInfo.lastLoggedInTime = lastLoggedInTime;
         userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint;
         userInfo.partial = partial;
+        userInfo.preCreated = preCreated;
         userInfo.guestToRemove = guestToRemove;
         userInfo.profileGroupId = profileGroupId;
         userInfo.profileBadge = profileBadge;
@@ -2644,7 +2677,8 @@
     public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags, int userId,
             String[] disallowedPackages) {
         checkManageOrCreateUsersPermission(flags);
-        return createUserInternalUnchecked(name, flags, userId, disallowedPackages);
+        return createUserInternalUnchecked(name, flags, userId, /* preCreate= */ false,
+                disallowedPackages);
     }
 
     @Override
@@ -2659,12 +2693,27 @@
         return createUserInternal(name, flags, UserHandle.USER_NULL);
     }
 
-    private UserInfo createUserInternal(String name, int flags, int parentId) {
+    @Override
+    public UserInfo preCreateUser(int flags) {
+        checkManageOrCreateUsersPermission(flags);
+
+        Preconditions.checkArgument(!UserInfo.isManagedProfile(flags),
+                "cannot pre-create managed profiles");
+
+        Slog.i(LOG_TAG, "Pre-creating user with flags " + UserInfo.flagsToString(flags));
+
+        return createUserInternalUnchecked(/* name= */ null, flags,
+                /* parentId= */ UserHandle.USER_NULL, /* preCreate= */ true,
+                /* disallowedPackages= */ null);
+    }
+
+    private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
+            @UserIdInt int parentId) {
         return createUserInternal(name, flags, parentId, null);
     }
 
-    private UserInfo createUserInternal(String name, int flags, int parentId,
-            String[] disallowedPackages) {
+    private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
+            @UserIdInt int parentId, @Nullable String[] disallowedPackages) {
         String restriction = ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0)
                 ? UserManager.DISALLOW_ADD_MANAGED_PROFILE
                 : UserManager.DISALLOW_ADD_USER;
@@ -2672,19 +2721,64 @@
             Log.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled.");
             return null;
         }
-        return createUserInternalUnchecked(name, flags, parentId, disallowedPackages);
+        return createUserInternalUnchecked(name, flags, parentId, /* preCreate= */ false,
+                disallowedPackages);
     }
 
-    private UserInfo createUserInternalUnchecked(String name, int flags, int parentId,
-            String[] disallowedPackages) {
+    private UserInfo createUserInternalUnchecked(@Nullable String name, @UserInfoFlag int flags,
+            @UserIdInt int parentId, boolean preCreate,
+            @Nullable String[] disallowedPackages) {
+        final TimingsTraceLog t = new TimingsTraceLog(LOG_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+        t.traceBegin("createUser-" + flags);
+        try {
+            return createUserInternalUncheckedNoTracing(name, flags, parentId, preCreate,
+                disallowedPackages, t);
+        } finally {
+            t.traceEnd();
+        }
+    }
+
+    private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
+            @UserInfoFlag int flags, @UserIdInt int parentId, boolean preCreate,
+            @Nullable String[] disallowedPackages, @NonNull TimingsTraceLog t) {
+
+        // First try to use a pre-created user (if available).
+        // NOTE: currently we don't support pre-created managed profiles
+        if (!preCreate && (parentId < 0 && !UserInfo.isManagedProfile(flags))) {
+            final UserData preCreatedUserData;
+            synchronized (mUsersLock) {
+                preCreatedUserData = getPreCreatedUserLU(flags);
+            }
+            if (preCreatedUserData != null) {
+                final UserInfo preCreatedUser = preCreatedUserData.info;
+                Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " for flags + "
+                        + UserInfo.flagsToString(flags));
+                if (DBG) {
+                    Log.d(LOG_TAG, "pre-created user flags: "
+                            + UserInfo.flagsToString(preCreatedUser.flags)
+                            + " new-user flags: " + UserInfo.flagsToString(flags));
+                }
+                preCreatedUser.name = name;
+                preCreatedUser.preCreated = false;
+                preCreatedUser.creationTime = getCreationTime();
+
+                dispatchUserAddedIntent(preCreatedUser);
+
+                writeUserLP(preCreatedUserData);
+                writeUserListLP();
+
+                return preCreatedUser;
+            }
+        }
+
         DeviceStorageMonitorInternal dsm = LocalServices
                 .getService(DeviceStorageMonitorInternal.class);
         if (dsm.isMemoryLow()) {
             Log.w(LOG_TAG, "Cannot add user. Not enough space on disk.");
             return null;
         }
-        final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
-        final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
+        final boolean isGuest = UserInfo.isGuest(flags);
+        final boolean isManagedProfile = UserInfo.isManagedProfile(flags);
         final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
         final boolean isDemo = (flags & UserInfo.FLAG_DEMO) != 0;
         final long ident = Binder.clearCallingIdentity();
@@ -2705,13 +2799,13 @@
                     return null;
                 }
                 if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
-                    // If we're not adding a guest/demo user or a managed profile and the limit has
-                    // been reached, cannot add a user.
+                    // If we're not adding a guest/demo user or a managed profile,
+                    // and the limit has been reached, cannot add a user.
                     Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached.");
                     return null;
                 }
                 // If we're adding a guest and there already exists one, bail.
-                if (isGuest && findCurrentGuestUser() != null) {
+                if (isGuest && !preCreate && findCurrentGuestUser() != null) {
                     Log.e(LOG_TAG, "Cannot add guest user. Guest user already exists.");
                     return null;
                 }
@@ -2747,8 +2841,7 @@
 
                 userId = getNextAvailableId();
                 Environment.getUserSystemDirectory(userId).mkdirs();
-                boolean ephemeralGuests = Resources.getSystem()
-                        .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
+                boolean ephemeralGuests = areGuestUsersEphemeral();
 
                 synchronized (mUsersLock) {
                     // Add ephemeral flag to guests/users if required. Also inherit it from parent.
@@ -2759,9 +2852,9 @@
 
                     userInfo = new UserInfo(userId, name, null, flags);
                     userInfo.serialNumber = mNextSerialNumber++;
-                    long now = System.currentTimeMillis();
-                    userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
+                    userInfo.creationTime = getCreationTime();
                     userInfo.partial = true;
+                    userInfo.preCreated = preCreate;
                     userInfo.lastLoggedInFingerprint = Build.FINGERPRINT;
                     if (isManagedProfile && parentId != UserHandle.USER_NULL) {
                         userInfo.profileBadge = getFreeProfileBadgeLU(parentId);
@@ -2808,18 +2901,97 @@
                 mBaseUserRestrictions.append(userId, restrictions);
             }
             mPm.onNewUserCreated(userId);
-            Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
-            addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-            mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
-                    android.Manifest.permission.MANAGE_USERS);
-            MetricsLogger.count(mContext, isGuest ? TRON_GUEST_CREATED
-                    : (isDemo ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
+            if (preCreate) {
+                // Must start user (which will be stopped right away, through
+                // UserController.finishUserUnlockedCompleted) so services can properly
+                // intialize it.
+                // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
+                // on SystemService instead.
+                Slog.i(LOG_TAG, "starting pre-created user " + userInfo.toFullString());
+                final IActivityManager am = ActivityManager.getService();
+                try {
+                    am.startUserInBackground(userId);
+                } catch (RemoteException e) {
+                    Slog.w(LOG_TAG, "could not start pre-created user " + userId, e);
+                }
+            } else {
+                dispatchUserAddedIntent(userInfo);
+            }
+
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
+
+        // TODO(b/140750212): it's possible to reach "max users overflow" when the user is created
+        // "from scratch" (i.e., not from a pre-created user) and reaches the maximum number of
+        // users without counting the pre-created one. Then when the pre-created is converted, the
+        // "effective" number of max users is exceeds. Example:
+        // Max: 3 Current: 2 full (u0 and u10) + 1 pre-created (u11)
+        // Step 1: create(/* flags doesn't match u11 */): u12 is created, "effective max" is now 3
+        //         (u0, u10, u12) but "real" max is 4 (u0, u10, u11, u12)
+        // Step 2: create(/* flags match u11 */): u11 is converted, now "effective max" is also 4
+        //         (u0, u10, u11, u12)
+        // One way to avoid this issue is by removing a pre-created user from the pool when the
+        // "real" max exceeds the max here.
+
         return userInfo;
     }
 
+    private long getCreationTime() {
+        final long now = System.currentTimeMillis();
+        return (now > EPOCH_PLUS_30_YEARS) ? now : 0;
+    }
+
+    private void dispatchUserAddedIntent(@NonNull UserInfo userInfo) {
+        Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
+        addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
+        mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
+                android.Manifest.permission.MANAGE_USERS);
+        MetricsLogger.count(mContext, userInfo.isGuest() ? TRON_GUEST_CREATED
+                : (userInfo.isDemo() ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
+    }
+
+    private boolean areGuestUsersEphemeral() {
+        return Resources.getSystem()
+                .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
+    }
+
+    /**
+     * Gets a pre-created user for the given flag.
+     *
+     * <p>Should be used only during user creation, so the pre-created user can be used (instead of
+     * creating and initializing a new user from scratch).
+     */
+    // TODO(b/140750212): add unit test
+    @GuardedBy("mUsersLock")
+    private @Nullable UserData getPreCreatedUserLU(@UserInfoFlag int flags) {
+        if (DBG) {
+            Slog.d(LOG_TAG, "getPreCreatedUser(): initialFlags= " + UserInfo.flagsToString(flags));
+        }
+        if (UserInfo.isGuest(flags) && areGuestUsersEphemeral()) {
+            flags |= UserInfo.FLAG_EPHEMERAL;
+        }
+        if (DBG) {
+            Slog.d(LOG_TAG, "getPreCreatedUser(): targetFlags= " + UserInfo.flagsToString(flags));
+        }
+        final int userSize = mUsers.size();
+        for (int i = 0; i < userSize; i++) {
+            final UserData user = mUsers.valueAt(i);
+            if (DBG) Slog.d(LOG_TAG, i + ":" + user.info.toFullString());
+            if (user.info.preCreated
+                    && (user.info.flags & ~UserInfo.FLAG_INITIALIZED) == flags) {
+                if (!user.info.isInitialized()) {
+                    Slog.w(LOG_TAG, "found pre-created user for flags "
+                            + "" + UserInfo.flagsToString(flags)
+                            + ", but it's not initialized yet: " + user.info.toFullString());
+                    continue;
+                }
+                return user;
+            }
+        }
+        return null;
+    }
+
     @VisibleForTesting
     UserData putUserInfo(UserInfo userInfo) {
         final UserData userData = new UserData();
@@ -2872,7 +3044,8 @@
             final int size = mUsers.size();
             for (int i = 0; i < size; i++) {
                 final UserInfo user = mUsers.valueAt(i).info;
-                if (user.isGuest() && !user.guestToRemove && !mRemovingUserIds.get(user.id)) {
+                if (user.isGuest() && !user.guestToRemove && !user.preCreated
+                        && !mRemovingUserIds.get(user.id)) {
                     return user;
                 }
             }
@@ -3651,7 +3824,7 @@
         try {
             switch(cmd) {
                 case "list":
-                    return runList(pw);
+                    return runList(pw, shell);
                 default:
                     return shell.handleDefaultCommands(cmd);
             }
@@ -3661,17 +3834,58 @@
         return -1;
     }
 
-    private int runList(PrintWriter pw) throws RemoteException {
+    private int runList(PrintWriter pw, Shell shell) throws RemoteException {
+        boolean all = false;
+        boolean verbose = false;
+        String opt;
+        while ((opt = shell.getNextOption()) != null) {
+            switch (opt) {
+                case "-v":
+                    verbose = true;
+                    break;
+                case "--all":
+                    all = true;
+                    break;
+                default:
+                    pw.println("Invalid option: " + opt);
+                    return -1;
+            }
+        }
         final IActivityManager am = ActivityManager.getService();
-        final List<UserInfo> users = getUsers(false);
+        final List<UserInfo> users = getUsers(/* excludePartial= */ !all,
+                /* excludingDying=*/ false, /* excludePreCreated= */ !all);
         if (users == null) {
             pw.println("Error: couldn't get users");
             return 1;
         } else {
-            pw.println("Users:");
-            for (int i = 0; i < users.size(); i++) {
-                String running = am.isUserRunning(users.get(i).id, 0) ? " running" : "";
-                pw.println("\t" + users.get(i).toString() + running);
+            final int size = users.size();
+            int currentUser = UserHandle.USER_NULL;
+            if (verbose) {
+                pw.printf("%d users:\n\n", size);
+                currentUser = am.getCurrentUser().id;
+            } else {
+                // NOTE: the standard "list users" command is used by integration tests and
+                // hence should not be changed. If you need to add more info, use the
+                // verbose option.
+                pw.println("Users:");
+            }
+            for (int i = 0; i < size; i++) {
+                final UserInfo user = users.get(i);
+                final boolean running = am.isUserRunning(user.id, 0);
+                final boolean current = user.id == currentUser;
+                if (verbose) {
+                    pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s\n", i, user.id, user.name,
+                            UserInfo.flagsToString(user.flags),
+                            running ? " (running)" : "",
+                            user.partial ? " (partial)" : "",
+                            user.preCreated ? " (pre-created)" : "",
+                            current ? " (current)" : "");
+                } else {
+                    // NOTE: the standard "list users" command is used by integration tests and
+                    // hence should not be changed. If you need to add more info, use the
+                    // verbose option.
+                    pw.printf("\t%s%s\n", user, running ? " running" : "");
+                }
             }
             return 0;
         }
@@ -3683,6 +3897,16 @@
 
         long now = System.currentTimeMillis();
         final long nowRealtime = SystemClock.elapsedRealtime();
+
+        final ActivityManagerInternal amInternal = LocalServices
+                .getService(ActivityManagerInternal.class);
+        pw.print("Current user: ");
+        if (amInternal != null) {
+            pw.println(amInternal.getCurrentUserId());
+        } else {
+            pw.println("N/A");
+        }
+
         StringBuilder sb = new StringBuilder();
         synchronized (mPackagesLock) {
             synchronized (mUsersLock) {
@@ -3696,12 +3920,16 @@
                     final int userId = userInfo.id;
                     pw.print("  "); pw.print(userInfo);
                     pw.print(" serialNo="); pw.print(userInfo.serialNumber);
+                    pw.print(" isPrimary="); pw.print(userInfo.isPrimary());
                     if (mRemovingUserIds.get(userId)) {
                         pw.print(" <removing> ");
                     }
                     if (userInfo.partial) {
                         pw.print(" <partial>");
                     }
+                    if (userInfo.preCreated) {
+                        pw.print(" <pre-created>");
+                    }
                     pw.println();
                     pw.print("    State: ");
                     final int state;
@@ -3778,13 +4006,16 @@
             synchronized (mUserStates) {
                 pw.println("  Started users state: " + mUserStates);
             }
-            // Dump some capabilities
-            pw.println();
-            pw.println("  Max users: " + UserManager.getMaxSupportedUsers());
-            pw.println("  Supports switchable users: " + UserManager.supportsMultipleUsers());
-            pw.println("  All guests ephemeral: " + Resources.getSystem().getBoolean(
-                    com.android.internal.R.bool.config_guestUserEphemeral));
-        }
+        } // synchronized (mPackagesLock)
+
+        // Dump some capabilities
+        pw.println();
+        pw.print("  Max users: " + UserManager.getMaxSupportedUsers());
+        pw.println(" (limit reached: " + isUserLimitReached() + ")");
+        pw.println("  Supports switchable users: " + UserManager.supportsMultipleUsers());
+        pw.println("  All guests ephemeral: " + areGuestUsersEphemeral());
+        pw.println("  Is split-system user: " + UserManager.isSplitSystemUser());
+        pw.println("  User version: " + mUserVersion);
     }
 
     private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
@@ -3969,7 +4200,7 @@
         public UserInfo createUserEvenWhenDisallowed(String name, int flags,
                 String[] disallowedPackages) {
             UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL,
-                    disallowedPackages);
+                    /* preCreated= */ false, disallowedPackages);
             // Keep this in sync with UserManager.createUser
             if (user != null && !user.isAdmin() && !user.isDemo()) {
                 setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
@@ -4134,7 +4365,7 @@
             pw.println("  help");
             pw.println("    Print this help text.");
             pw.println("");
-            pw.println("  list");
+            pw.println("  list [-v] [-all]");
             pw.println("    Prints all users on the system.");
         }
     }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 48056b4..7270616 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -23,6 +23,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.Context.CONTEXT_RESTRICTED;
 import static android.content.Context.WINDOW_SERVICE;
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
 import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
@@ -384,6 +385,7 @@
     BurnInProtectionHelper mBurnInProtectionHelper;
     private DisplayFoldController mDisplayFoldController;
     AppOpsManager mAppOpsManager;
+    private boolean mHasFeatureAuto;
     private boolean mHasFeatureWatch;
     private boolean mHasFeatureLeanback;
     private boolean mHasFeatureHdmiCec;
@@ -1752,6 +1754,7 @@
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
         mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK);
+        mHasFeatureAuto = mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE);
         mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC);
         mAccessibilityShortcutController =
                 new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
@@ -5215,7 +5218,7 @@
             awakenDreams();
         }
 
-        if (!isUserSetupComplete()) {
+        if (!mHasFeatureAuto && !isUserSetupComplete()) {
             Slog.i(TAG, "Not going home because user setup is in progress.");
             return;
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index e51ee94..88273b7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -17,6 +17,7 @@
 
 import static androidx.test.InstrumentationRegistry.getContext;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
@@ -31,6 +32,7 @@
 import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
 import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
 import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
+import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
 import static com.android.server.DeviceIdleController.STATE_ACTIVE;
 import static com.android.server.DeviceIdleController.STATE_IDLE;
 import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
@@ -51,6 +53,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -70,6 +73,7 @@
 import android.net.NetworkInfo;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
@@ -85,11 +89,13 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
 import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
 
 /**
  * Tests for {@link com.android.server.DeviceIdleController}.
@@ -97,6 +103,7 @@
 @RunWith(AndroidJUnit4.class)
 public class DeviceIdleControllerTest {
     private DeviceIdleController mDeviceIdleController;
+    private DeviceIdleController.MyHandler mHandler;
     private AnyMotionDetectorForTest mAnyMotionDetector;
     private AppStateTrackerForTest mAppStateTracker;
     private DeviceIdleController.Constants mConstants;
@@ -110,8 +117,6 @@
     @Mock
     private ContentResolver mContentResolver;
     @Mock
-    private DeviceIdleController.MyHandler mHandler;
-    @Mock
     private IActivityManager mIActivityManager;
     @Mock
     private LocationManager mLocationManager;
@@ -128,6 +133,8 @@
         ConnectivityService connectivityService;
         LocationManager locationManager;
         ConstraintController constraintController;
+        // Freeze time for testing.
+        long nowElapsed;
 
         InjectorForTest(Context ctx) {
             super(ctx);
@@ -155,12 +162,34 @@
         }
 
         @Override
+        long getElapsedRealtime() {
+            return nowElapsed;
+        }
+
+        @Override
         LocationManager getLocationManager() {
             return locationManager;
         }
 
         @Override
         DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) {
+            if (mHandler == null) {
+                mHandler = controller.new MyHandler(getContext().getMainLooper());
+                spyOn(mHandler);
+                doNothing().when(mHandler).handleMessage(argThat((message) ->
+                        message.what != MSG_REPORT_STATIONARY_STATUS));
+                doAnswer(new Answer<Boolean>() {
+                    @Override
+                    public Boolean answer(InvocationOnMock invocation) throws Throwable {
+                        Message msg = invocation.getArgument(0);
+                        mHandler.handleMessage(msg);
+                        return true;
+                    }
+                }).when(mHandler).sendMessageDelayed(
+                        argThat((message) -> message.what == MSG_REPORT_STATIONARY_STATUS),
+                        anyLong());
+            }
+
             return mHandler;
         }
 
@@ -226,6 +255,19 @@
         }
     }
 
+    private class StationaryListenerForTest implements DeviceIdleController.StationaryListener {
+        boolean motionExpected = false;
+        boolean isStationary = false;
+
+        @Override
+        public void onDeviceStationaryChanged(boolean isStationary) {
+            if (isStationary == motionExpected) {
+                fail("Unexpected device stationary status: " + isStationary);
+            }
+            this.isStationary = isStationary;
+        }
+    }
+
     @Before
     public void setUp() {
         mMockingSession = mockitoSession()
@@ -255,8 +297,6 @@
         doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
         mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
         mAnyMotionDetector = new AnyMotionDetectorForTest();
-        mHandler = mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
-        doNothing().when(mHandler).handleMessage(any());
         mInjector = new InjectorForTest(getContext());
         doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
 
@@ -1607,6 +1647,86 @@
                 1.0f, curfactor, delta);
     }
 
+    @Test
+    public void testStationaryDetection_QuickDozeOff() {
+        setQuickDozeEnabled(false);
+        enterDeepState(STATE_IDLE);
+        // Regular progression through states, so time should have increased appropriately.
+        mInjector.nowElapsed += mConstants.IDLE_AFTER_INACTIVE_TIMEOUT + mConstants.SENSING_TIMEOUT
+                + mConstants.LOCATING_TIMEOUT;
+
+        StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+
+        mDeviceIdleController.registerStationaryListener(stationaryListener);
+
+        // Go to IDLE_MAINTENANCE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+
+        // Back to IDLE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        assertTrue(stationaryListener.isStationary);
+
+        // Test motion
+        stationaryListener.motionExpected = true;
+        mDeviceIdleController.mMotionListener.onTrigger(null);
+        assertFalse(stationaryListener.isStationary);
+    }
+
+    @Test
+    public void testStationaryDetection_QuickDozeOn() {
+        setAlarmSoon(false);
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE);
+        // Quick doze progression through states, so time should have increased appropriately.
+        mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
+        final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
+                .forClass(AlarmManager.OnAlarmListener.class);
+        doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"),
+                alarmListener.capture(), any());
+
+        StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+
+        stationaryListener.motionExpected = true;
+        mDeviceIdleController.registerStationaryListener(stationaryListener);
+        assertFalse(stationaryListener.isStationary);
+
+        // Go to IDLE_MAINTENANCE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+
+        mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+
+        // Back to IDLE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+
+        // Now enough time has passed.
+        mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+        stationaryListener.motionExpected = false;
+        alarmListener.getValue().onAlarm();
+        assertTrue(stationaryListener.isStationary);
+
+        stationaryListener.motionExpected = true;
+        mDeviceIdleController.mMotionListener.onSensorChanged(null);
+        assertFalse(stationaryListener.isStationary);
+
+        // Since we're in quick doze, the device shouldn't stop idling.
+        verifyStateConditions(STATE_IDLE);
+
+        // Go to IDLE_MAINTENANCE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+
+        mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+
+        // Back to IDLE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+
+        // Now enough time has passed.
+        mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+        stationaryListener.motionExpected = false;
+        alarmListener.getValue().onAlarm();
+        assertTrue(stationaryListener.isStationary);
+    }
+
     private void enterDeepState(int state) {
         switch (state) {
             case STATE_ACTIVE:
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 231025c..a7c943e 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -25,8 +25,8 @@
 import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
 import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
 import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
+import static com.android.server.am.UserController.USER_CURRENT_MSG;
+import static com.android.server.am.UserController.USER_START_MSG;
 import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
 
 import static com.google.android.collect.Lists.newArrayList;
@@ -53,11 +53,13 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.UserIdInt;
 import android.app.IUserSwitchObserver;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -107,6 +109,10 @@
     private static final int TEST_USER_ID1 = 101;
     private static final int TEST_USER_ID2 = 102;
     private static final int NONEXIST_USER_ID = 2;
+    private static final int TEST_PRE_CREATED_USER_ID = 103;
+
+    private static final int NO_USERINFO_FLAGS = 0;
+
     private static final String TAG = UserControllerTest.class.getSimpleName();
 
     private static final long HANDLER_WAIT_TIME_MS = 100;
@@ -128,11 +134,11 @@
     private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
             REPORT_USER_SWITCH_MSG,
             USER_SWITCH_TIMEOUT_MSG,
-            SYSTEM_USER_START_MSG,
-            SYSTEM_USER_CURRENT_MSG);
+            USER_START_MSG,
+            USER_CURRENT_MSG);
 
     private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
-            SYSTEM_USER_START_MSG,
+            USER_START_MSG,
             REPORT_LOCKED_BOOT_COMPLETE_MSG);
 
     @Before
@@ -149,7 +155,8 @@
             doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
             doNothing().when(mInjector).stackSupervisorRemoveUser(anyInt());
             mUserController = new UserController(mInjector);
-            setUpUser(TEST_USER_ID, 0);
+            setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
+            setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true);
         });
     }
 
@@ -190,6 +197,31 @@
         startForegroundUserAssertions();
     }
 
+    @Test
+    public void testStartPreCreatedUser_foreground() {
+        assertFalse(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ true));
+    }
+
+    @Test
+    public void testStartPreCreatedUser_background() throws Exception {
+        assertTrue(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ false));
+
+        verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+        verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+        verify(mInjector, never()).clearAllLockedTasks(anyString());
+
+        assertWithMessage("should not have received intents")
+                .that(getActions(mInjector.mSentIntents)).isEmpty();
+        // TODO(b/140868593): should have received a USER_UNLOCK_MSG message as well, but it doesn't
+        // because StorageManager.isUserKeyUnlocked(TEST_PRE_CREATED_USER_ID) returns false - to
+        // properly fix it, we'd need to move this class to FrameworksMockingServicesTests so we can
+        // mock static methods (but moving this class would involve changing the presubmit tests,
+        // and the cascade effect goes on...). In fact, a better approach would to not assert the
+        // binder calls, but their side effects (in this case, that the user is stopped right away)
+        assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes())
+                .containsExactly(USER_START_MSG);
+    }
+
     private void startUserAssertions(
             List<String> expectedActions, Set<Integer> expectedMessageCodes) {
         assertEquals(expectedActions, getActions(mInjector.mSentIntents));
@@ -469,9 +501,15 @@
         continueUserSwitchAssertions(newUserId, expectOldUserStopping);
     }
 
-    private void setUpUser(int userId, int flags) {
+    private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) {
+        setUpUser(userId, flags, /* preCreated= */ false);
+    }
+
+    private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags, boolean preCreated) {
         UserInfo userInfo = new UserInfo(userId, "User" + userId, flags);
+        userInfo.preCreated = preCreated;
         when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
+        when(mInjector.mUserManagerMock.isPreCreated(userId)).thenReturn(preCreated);
     }
 
     private static List<String> getActions(List<Intent> intents) {
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index a985a6b..51048c0 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -987,6 +987,9 @@
             case RIL_RADIO_TECHNOLOGY_LTE_CA:
                 rtString = "LTE_CA";
                 break;
+            case RIL_RADIO_TECHNOLOGY_NR:
+                rtString = "LTE_NR";
+                break;
             default:
                 rtString = "Unexpected";
                 Rlog.w(LOG_TAG, "Unexpected radioTechnology=" + rt);
@@ -1553,6 +1556,7 @@
                 return AccessNetworkType.CDMA2000;
             case RIL_RADIO_TECHNOLOGY_LTE:
             case RIL_RADIO_TECHNOLOGY_LTE_CA:
+            case RIL_RADIO_TECHNOLOGY_NR:
                 return AccessNetworkType.EUTRAN;
             case RIL_RADIO_TECHNOLOGY_IWLAN:
                 return AccessNetworkType.IWLAN;
@@ -1601,6 +1605,8 @@
                 return ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
             case TelephonyManager.NETWORK_TYPE_LTE_CA:
                 return ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
+            case TelephonyManager.NETWORK_TYPE_NR:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_NR;
             default:
                 return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
         }
@@ -1691,7 +1697,8 @@
                 || radioTechnology == RIL_RADIO_TECHNOLOGY_GSM
                 || radioTechnology == RIL_RADIO_TECHNOLOGY_TD_SCDMA
                 || radioTechnology == RIL_RADIO_TECHNOLOGY_IWLAN
-                || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA;
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_NR;
 
     }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d2559e7..c8ac160 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2795,6 +2795,8 @@
     /** Class of broadly defined "4G" networks. {@hide} */
     @UnsupportedAppUsage
     public static final int NETWORK_CLASS_4_G = 3;
+    /** Class of broadly defined "5G" networks. {@hide} */
+    public static final int NETWORK_CLASS_5_G = 4;
 
     /**
      * Return general class of network type, such as "3G" or "4G". In cases
@@ -2827,6 +2829,8 @@
             case NETWORK_TYPE_IWLAN:
             case NETWORK_TYPE_LTE_CA:
                 return NETWORK_CLASS_4_G;
+            case NETWORK_TYPE_NR:
+                return NETWORK_CLASS_5_G;
             default:
                 return NETWORK_CLASS_UNKNOWN;
         }
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 1e0d9a78..10251d7 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -885,6 +885,12 @@
      */
     public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
 
+    /**
+     * The dialed RTT call should be retried without RTT
+     * @hide
+     */
+    public static final int CODE_RETRY_ON_IMS_WITHOUT_RTT = 3001;
+
     /*
      * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
      * would be replaced by ERROR_UNSPECIFIED.
@@ -1065,6 +1071,7 @@
             CODE_REJECT_VT_AVPF_NOT_ALLOWED,
             CODE_REJECT_ONGOING_ENCRYPTED_CALL,
             CODE_REJECT_ONGOING_CS_CALL,
+            CODE_RETRY_ON_IMS_WITHOUT_RTT,
             CODE_OEM_CAUSE_1,
             CODE_OEM_CAUSE_2,
             CODE_OEM_CAUSE_3,
diff --git a/tests/testables/src/android/testing/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java
index b158476..fd92c65 100644
--- a/tests/testables/src/android/testing/TestableSettingsProvider.java
+++ b/tests/testables/src/android/testing/TestableSettingsProvider.java
@@ -14,6 +14,8 @@
 
 package android.testing;
 
+import static org.junit.Assert.assertEquals;
+
 import android.content.ContentProviderClient;
 import android.content.Context;
 import android.os.Bundle;
@@ -25,8 +27,6 @@
 
 import java.util.HashMap;
 
-import static org.junit.Assert.*;
-
 /**
  * Allows calls to android.provider.Settings to be tested easier.
  *
@@ -71,7 +71,7 @@
 
     public Bundle call(String method, String arg, Bundle extras) {
         // Methods are "GET_system", "GET_global", "PUT_secure", etc.
-        final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, 0);
+        final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.myUserId());
         final String[] commands = method.split("_", 2);
         final String op = commands[0];
         final String table = commands[1];
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 77e02df..a2aadb5 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1752,12 +1752,13 @@
     }
 
     /**
-     * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
+     * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name) added
+     * by the caller.
      *
-     * @param fqdn The FQDN of the Passpoint configuration to be removed
+     * @param fqdn The FQDN of the Passpoint configuration added by the caller to be removed
      * @throws IllegalArgumentException if no configuration is associated with the given FQDN or
      *                                  Passpoint is not enabled on the device.
-     * @deprecated This is no longer supported.
+     * @deprecated This will be non-functional in a future release.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
@@ -1772,12 +1773,12 @@
     }
 
     /**
-     * Return the list of installed Passpoint configurations.
+     * Return the list of installed Passpoint configurations added by the caller.
      *
      * An empty list will be returned when no configurations are installed.
      *
-     * @return A list of {@link PasspointConfiguration}
-     * @deprecated This is no longer supported.
+     * @return A list of {@link PasspointConfiguration} added by the caller
+     * @deprecated This will be non-functional in a future release.
      */
     @Deprecated
     @RequiresPermission(anyOf = {
@@ -2044,69 +2045,69 @@
     }
 
     /** @hide */
-    public static final int WIFI_FEATURE_INFRA            = 0x0001;  // Basic infrastructure mode
+    public static final long WIFI_FEATURE_INFRA            = 0x0001L;  // Basic infrastructure mode
     /** @hide */
-    public static final int WIFI_FEATURE_INFRA_5G         = 0x0002;  // Support for 5 GHz Band
+    public static final long WIFI_FEATURE_INFRA_5G         = 0x0002L;  // Support for 5 GHz Band
     /** @hide */
-    public static final int WIFI_FEATURE_PASSPOINT        = 0x0004;  // Support for GAS/ANQP
+    public static final long WIFI_FEATURE_PASSPOINT        = 0x0004L;  // Support for GAS/ANQP
     /** @hide */
-    public static final int WIFI_FEATURE_P2P              = 0x0008;  // Wifi-Direct
+    public static final long WIFI_FEATURE_P2P              = 0x0008L;  // Wifi-Direct
     /** @hide */
-    public static final int WIFI_FEATURE_MOBILE_HOTSPOT   = 0x0010;  // Soft AP
+    public static final long WIFI_FEATURE_MOBILE_HOTSPOT   = 0x0010L;  // Soft AP
     /** @hide */
-    public static final int WIFI_FEATURE_SCANNER          = 0x0020;  // WifiScanner APIs
+    public static final long WIFI_FEATURE_SCANNER          = 0x0020L;  // WifiScanner APIs
     /** @hide */
-    public static final int WIFI_FEATURE_AWARE            = 0x0040;  // Wi-Fi AWare networking
+    public static final long WIFI_FEATURE_AWARE            = 0x0040L;  // Wi-Fi AWare networking
     /** @hide */
-    public static final int WIFI_FEATURE_D2D_RTT          = 0x0080;  // Device-to-device RTT
+    public static final long WIFI_FEATURE_D2D_RTT          = 0x0080L;  // Device-to-device RTT
     /** @hide */
-    public static final int WIFI_FEATURE_D2AP_RTT         = 0x0100;  // Device-to-AP RTT
+    public static final long WIFI_FEATURE_D2AP_RTT         = 0x0100L;  // Device-to-AP RTT
     /** @hide */
-    public static final int WIFI_FEATURE_BATCH_SCAN       = 0x0200;  // Batched Scan (deprecated)
+    public static final long WIFI_FEATURE_BATCH_SCAN       = 0x0200L;  // Batched Scan (deprecated)
     /** @hide */
-    public static final int WIFI_FEATURE_PNO              = 0x0400;  // Preferred network offload
+    public static final long WIFI_FEATURE_PNO              = 0x0400L;  // Preferred network offload
     /** @hide */
-    public static final int WIFI_FEATURE_ADDITIONAL_STA   = 0x0800;  // Support for two STAs
+    public static final long WIFI_FEATURE_ADDITIONAL_STA   = 0x0800L;  // Support for two STAs
     /** @hide */
-    public static final int WIFI_FEATURE_TDLS             = 0x1000;  // Tunnel directed link setup
+    public static final long WIFI_FEATURE_TDLS             = 0x1000L;  // Tunnel directed link setup
     /** @hide */
-    public static final int WIFI_FEATURE_TDLS_OFFCHANNEL  = 0x2000;  // Support for TDLS off channel
+    public static final long WIFI_FEATURE_TDLS_OFFCHANNEL  = 0x2000L;  // TDLS off channel
     /** @hide */
-    public static final int WIFI_FEATURE_EPR              = 0x4000;  // Enhanced power reporting
+    public static final long WIFI_FEATURE_EPR              = 0x4000L;  // Enhanced power reporting
     /** @hide */
-    public static final int WIFI_FEATURE_AP_STA           = 0x8000;  // AP STA Concurrency
+    public static final long WIFI_FEATURE_AP_STA           = 0x8000L;  // AP STA Concurrency
     /** @hide */
-    public static final int WIFI_FEATURE_LINK_LAYER_STATS = 0x10000; // Link layer stats collection
+    public static final long WIFI_FEATURE_LINK_LAYER_STATS = 0x10000L; // Link layer stats
     /** @hide */
-    public static final int WIFI_FEATURE_LOGGER           = 0x20000; // WiFi Logger
+    public static final long WIFI_FEATURE_LOGGER           = 0x20000L; // WiFi Logger
     /** @hide */
-    public static final int WIFI_FEATURE_HAL_EPNO         = 0x40000; // Enhanced PNO
+    public static final long WIFI_FEATURE_HAL_EPNO         = 0x40000L; // Enhanced PNO
     /** @hide */
-    public static final int WIFI_FEATURE_RSSI_MONITOR     = 0x80000; // RSSI Monitor
+    public static final long WIFI_FEATURE_RSSI_MONITOR     = 0x80000L; // RSSI Monitor
     /** @hide */
-    public static final int WIFI_FEATURE_MKEEP_ALIVE      = 0x100000; // mkeep_alive
+    public static final long WIFI_FEATURE_MKEEP_ALIVE      = 0x100000L; // mkeep_alive
     /** @hide */
-    public static final int WIFI_FEATURE_CONFIG_NDO       = 0x200000; // ND offload
+    public static final long WIFI_FEATURE_CONFIG_NDO       = 0x200000L; // ND offload
     /** @hide */
-    public static final int WIFI_FEATURE_TRANSMIT_POWER   = 0x400000; // Capture transmit power
+    public static final long WIFI_FEATURE_TRANSMIT_POWER   = 0x400000L; // Capture transmit power
     /** @hide */
-    public static final int WIFI_FEATURE_CONTROL_ROAMING  = 0x800000; // Control firmware roaming
+    public static final long WIFI_FEATURE_CONTROL_ROAMING  = 0x800000L; // Control firmware roaming
     /** @hide */
-    public static final int WIFI_FEATURE_IE_WHITELIST     = 0x1000000; // Probe IE white listing
+    public static final long WIFI_FEATURE_IE_WHITELIST     = 0x1000000L; // Probe IE white listing
     /** @hide */
-    public static final int WIFI_FEATURE_SCAN_RAND        = 0x2000000; // Random MAC & Probe seq
+    public static final long WIFI_FEATURE_SCAN_RAND        = 0x2000000L; // Random MAC & Probe seq
     /** @hide */
-    public static final int WIFI_FEATURE_TX_POWER_LIMIT   = 0x4000000; // Set Tx power limit
+    public static final long WIFI_FEATURE_TX_POWER_LIMIT   = 0x4000000L; // Set Tx power limit
     /** @hide */
-    public static final int WIFI_FEATURE_WPA3_SAE         = 0x8000000; // WPA3-Personal SAE
+    public static final long WIFI_FEATURE_WPA3_SAE         = 0x8000000L; // WPA3-Personal SAE
     /** @hide */
-    public static final int WIFI_FEATURE_WPA3_SUITE_B     = 0x10000000; // WPA3-Enterprise Suite-B
+    public static final long WIFI_FEATURE_WPA3_SUITE_B     = 0x10000000L; // WPA3-Enterprise Suite-B
     /** @hide */
-    public static final int WIFI_FEATURE_OWE              = 0x20000000; // Enhanced Open
+    public static final long WIFI_FEATURE_OWE              = 0x20000000L; // Enhanced Open
     /** @hide */
-    public static final int WIFI_FEATURE_LOW_LATENCY      = 0x40000000; // Low Latency modes
+    public static final long WIFI_FEATURE_LOW_LATENCY      = 0x40000000L; // Low Latency modes
     /** @hide */
-    public static final int WIFI_FEATURE_DPP              = 0x80000000; // DPP (Easy-Connect)
+    public static final long WIFI_FEATURE_DPP              = 0x80000000L; // DPP (Easy-Connect)
     /** @hide */
     public static final long WIFI_FEATURE_P2P_RAND_MAC    = 0x100000000L; // Random P2P MAC