Merge changes from topic "permz"

* changes:
  Flesh out remaining CR.wrap() methods.
  Apps using storage must have runtime permission.
diff --git a/api/current.txt b/api/current.txt
index 23cf2a3..db17542 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -291,6 +291,7 @@
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowEmbedded = 16843765; // 0x10103f5
+    field public static final int allowExternalStorageSandbox = 16844201; // 0x10105a9
     field public static final int allowParallelSyncs = 16843570; // 0x1010332
     field public static final int allowSingleTap = 16843353; // 0x1010259
     field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -34659,9 +34660,11 @@
     method @NonNull public static java.io.File getRootDirectory();
     method @Deprecated public static String getStorageState(java.io.File);
     method public static boolean isExternalStorageEmulated();
-    method public static boolean isExternalStorageEmulated(java.io.File);
+    method public static boolean isExternalStorageEmulated(@NonNull java.io.File);
     method public static boolean isExternalStorageRemovable();
-    method public static boolean isExternalStorageRemovable(java.io.File);
+    method public static boolean isExternalStorageRemovable(@NonNull java.io.File);
+    method public static boolean isExternalStorageSandboxed();
+    method public static boolean isExternalStorageSandboxed(@NonNull java.io.File);
     field public static String DIRECTORY_ALARMS;
     field public static String DIRECTORY_AUDIOBOOKS;
     field public static String DIRECTORY_DCIM;
diff --git a/cmds/bmgr/Android.bp b/cmds/bmgr/Android.bp
new file mode 100644
index 0000000..b64923b
--- /dev/null
+++ b/cmds/bmgr/Android.bp
@@ -0,0 +1,8 @@
+// Copyright 2007 The Android Open Source Project
+//
+
+java_binary {
+    name: "bmgr",
+    wrapper: "bmgr",
+    srcs: ["**/*.java"],
+}
diff --git a/cmds/bmgr/Android.mk b/cmds/bmgr/Android.mk
deleted file mode 100644
index d520cf2..0000000
--- a/cmds/bmgr/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2007 The Android Open Source Project
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := bmgrlib
-LOCAL_MODULE_STEM := bmgr
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := bmgr
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_SRC_FILES := bmgr
-LOCAL_REQUIRED_MODULES := bmgrlib
-include $(BUILD_PREBUILT)
diff --git a/cmds/media/Android.bp b/cmds/media/Android.bp
new file mode 100644
index 0000000..7879c53
--- /dev/null
+++ b/cmds/media/Android.bp
@@ -0,0 +1,8 @@
+// Copyright 2013 The Android Open Source Project
+//
+
+java_binary {
+    name: "media",
+    wrapper: "media",
+    srcs: ["**/*.java"],
+}
diff --git a/cmds/media/Android.mk b/cmds/media/Android.mk
deleted file mode 100644
index b9451c5..0000000
--- a/cmds/media/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := media_cmd
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := media
-LOCAL_SRC_FILES := media
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_PREBUILT)
diff --git a/cmds/media/media b/cmds/media/media
index 5c0eb2f..8ada914 100755
--- a/cmds/media/media
+++ b/cmds/media/media
@@ -3,5 +3,5 @@
 # shell.
 #
 base=/system
-export CLASSPATH=$base/framework/media_cmd.jar
+export CLASSPATH=$base/framework/media.jar
 exec app_process $base/bin com.android.commands.media.Media "$@"
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 023371d..80b6349 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4037,7 +4037,7 @@
      * continues running even if the process is killed and restarted.  To remove the watch,
      * use {@link #clearWatchHeapLimit()}.
      *
-     * <p>This API only work if the calling process has been marked as
+     * <p>This API only works if the calling process has been marked as
      * {@link ApplicationInfo#FLAG_DEBUGGABLE} or this is running on a debuggable
      * (userdebug or eng) build.</p>
      *
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 08239a1..2441672 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2100,6 +2100,16 @@
     public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
             int flags, int userId) {
         final boolean differentUser = (UserHandle.myUserId() != userId);
+        ApplicationInfo ai;
+        try {
+            ai = getPackageManager().getApplicationInfo(packageName,
+                    PackageManager.GET_SHARED_LIBRARY_FILES
+                            | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                    (userId < 0) ? UserHandle.myUserId() : userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
         synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
             if (differentUser) {
@@ -2112,11 +2122,7 @@
             }
 
             LoadedApk packageInfo = ref != null ? ref.get() : null;
-            //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
-            //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
-            //        + ": " + packageInfo.mResources.getAssets().isUpToDate());
-            if (packageInfo != null && (packageInfo.mResources == null
-                    || packageInfo.mResources.getAssets().isUpToDate())) {
+            if (ai != null && packageInfo != null && isLoadedApkUpToDate(packageInfo, ai)) {
                 if (packageInfo.isSecurityViolation()
                         && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
                     throw new SecurityException(
@@ -2129,16 +2135,6 @@
             }
         }
 
-        ApplicationInfo ai = null;
-        try {
-            ai = getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_SHARED_LIBRARY_FILES
-                            | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
-                    (userId < 0) ? UserHandle.myUserId() : userId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
         if (ai != null) {
             return getPackageInfo(ai, compatInfo, flags);
         }
@@ -2209,37 +2205,59 @@
             }
 
             LoadedApk packageInfo = ref != null ? ref.get() : null;
-            if (packageInfo == null || (packageInfo.mResources != null
-                    && !packageInfo.mResources.getAssets().isUpToDate())) {
-                if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
+
+            boolean isUpToDate = packageInfo != null && isLoadedApkUpToDate(packageInfo, aInfo);
+
+            if (isUpToDate) {
+                return packageInfo;
+            }
+
+            if (localLOGV) {
+                Slog.v(TAG, (includeCode ? "Loading code package "
                         : "Loading resource-only package ") + aInfo.packageName
                         + " (in " + (mBoundApplication != null
-                                ? mBoundApplication.processName : null)
+                        ? mBoundApplication.processName : null)
                         + ")");
-                packageInfo =
-                    new LoadedApk(this, aInfo, compatInfo, baseLoader,
-                            securityViolation, includeCode &&
-                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
-
-                if (mSystemThread && "android".equals(aInfo.packageName)) {
-                    packageInfo.installSystemApplicationInfo(aInfo,
-                            getSystemContext().mPackageInfo.getClassLoader());
-                }
-
-                if (differentUser) {
-                    // Caching not supported across users
-                } else if (includeCode) {
-                    mPackages.put(aInfo.packageName,
-                            new WeakReference<LoadedApk>(packageInfo));
-                } else {
-                    mResourcePackages.put(aInfo.packageName,
-                            new WeakReference<LoadedApk>(packageInfo));
-                }
             }
+
+            packageInfo =
+                    new LoadedApk(this, aInfo, compatInfo, baseLoader,
+                            securityViolation, includeCode
+                            && (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
+
+            if (mSystemThread && "android".equals(aInfo.packageName)) {
+                packageInfo.installSystemApplicationInfo(aInfo,
+                        getSystemContext().mPackageInfo.getClassLoader());
+            }
+
+            if (differentUser) {
+                // Caching not supported across users
+            } else if (includeCode) {
+                mPackages.put(aInfo.packageName,
+                        new WeakReference<LoadedApk>(packageInfo));
+            } else {
+                mResourcePackages.put(aInfo.packageName,
+                        new WeakReference<LoadedApk>(packageInfo));
+            }
+
             return packageInfo;
         }
     }
 
+    /**
+     * Compares overlay/resource directories for a LoadedApk to determine if it's up to date
+     * with the given ApplicationInfo.
+     */
+    private boolean isLoadedApkUpToDate(LoadedApk loadedApk, ApplicationInfo appInfo) {
+        Resources packageResources = loadedApk.mResources;
+        String[] overlayDirs = ArrayUtils.defeatNullable(loadedApk.getOverlayDirs());
+        String[] resourceDirs = ArrayUtils.defeatNullable(appInfo.resourceDirs);
+
+        return (packageResources == null || packageResources.getAssets().isUpToDate())
+                && overlayDirs.length == resourceDirs.length
+                && ArrayUtils.containsAll(overlayDirs, resourceDirs);
+    }
+
     @UnsupportedAppUsage
     ActivityThread() {
         mResourcesManager = ResourcesManager.getInstance();
@@ -5434,19 +5452,25 @@
             ref = mResourcePackages.get(ai.packageName);
             resApk = ref != null ? ref.get() : null;
         }
+
+        final String[] oldResDirs = new String[2];
+
         if (apk != null) {
+            oldResDirs[0] = apk.getResDir();
             final ArrayList<String> oldPaths = new ArrayList<>();
             LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths);
             apk.updateApplicationInfo(ai, oldPaths);
         }
         if (resApk != null) {
+            oldResDirs[1] = resApk.getResDir();
             final ArrayList<String> oldPaths = new ArrayList<>();
             LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths);
             resApk.updateApplicationInfo(ai, oldPaths);
         }
+
         synchronized (mResourcesManager) {
             // Update all affected Resources objects to use new ResourcesImpl
-            mResourcesManager.applyNewResourceDirsLocked(ai.sourceDir, ai.resourceDirs);
+            mResourcesManager.applyNewResourceDirsLocked(ai, oldResDirs);
         }
 
         ApplicationPackageManager.configurationChanged();
@@ -5699,9 +5723,17 @@
                                         }
                                     }
                                 }
+
+                                final String[] oldResDirs = { pkgInfo.getResDir() };
+
                                 final ArrayList<String> oldPaths = new ArrayList<>();
                                 LoadedApk.makePaths(this, pkgInfo.getApplicationInfo(), oldPaths);
                                 pkgInfo.updateApplicationInfo(aInfo, oldPaths);
+
+                                synchronized (mResourcesManager) {
+                                    // Update affected Resources objects to use new ResourcesImpl
+                                    mResourcesManager.applyNewResourceDirsLocked(aInfo, oldResDirs);
+                                }
                             } catch (RemoteException e) {
                             }
                         }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 404e520..a906790 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3049,6 +3049,15 @@
     }
 
     @Override
+    public String getAttentionServicePackageName() {
+        try {
+            return mPM.getAttentionServicePackageName();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
     public String getWellbeingPackageName() {
         try {
             return mPM.getWellbeingPackageName();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 11000df..41a4fba 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2122,8 +2122,7 @@
     }
 
     private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
-            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo,
-            String[] overlayDirs) {
+            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
         final String[] splitResDirs;
         final ClassLoader classLoader;
         try {
@@ -2135,7 +2134,7 @@
         return ResourcesManager.getInstance().getResources(activityToken,
                 pi.getResDir(),
                 splitResDirs,
-                overlayDirs,
+                pi.getOverlayDirs(),
                 pi.getApplicationInfo().sharedLibraryFiles,
                 displayId,
                 overrideConfig,
@@ -2153,11 +2152,9 @@
                     new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
 
             final int displayId = getDisplayId();
-            // overlayDirs is retrieved directly from ApplicationInfo since ActivityThread may have
-            // a LoadedApk containing Resources with stale overlays for a remote application.
-            final String[] overlayDirs = application.resourceDirs;
+
             c.setResources(createResources(mActivityToken, pi, null, displayId, null,
-                    getDisplayAdjustments(displayId).getCompatibilityInfo(), overlayDirs));
+                    getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
             }
@@ -2192,7 +2189,7 @@
             final int displayId = getDisplayId();
 
             c.setResources(createResources(mActivityToken, pi, null, displayId, null,
-                    getDisplayAdjustments(displayId).getCompatibilityInfo(), pi.getOverlayDirs()));
+                    getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
             }
@@ -2242,8 +2239,7 @@
 
         final int displayId = getDisplayId();
         context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
-                overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
-                mPackageInfo.getOverlayDirs()));
+                overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         return context;
     }
 
@@ -2258,8 +2254,7 @@
 
         final int displayId = display.getDisplayId();
         context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
-                null, getDisplayAdjustments(displayId).getCompatibilityInfo(),
-                mPackageInfo.getOverlayDirs()));
+                null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         context.mDisplay = display;
         return context;
     }
@@ -2441,7 +2436,7 @@
         ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
                 null, null, 0, null, null);
         context.setResources(createResources(null, packageInfo, null, displayId, null,
-                packageInfo.getCompatibilityInfo(), packageInfo.getOverlayDirs()));
+                packageInfo.getCompatibilityInfo()));
         context.updateDisplay(displayId);
         return context;
     }
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 35658fb..b93aaa2 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.content.res.CompatResources;
@@ -32,6 +33,7 @@
 import android.content.res.ResourcesKey;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
@@ -1136,27 +1138,46 @@
     }
 
     // TODO(adamlesinski): Make this accept more than just overlay directories.
-    final void applyNewResourceDirsLocked(@NonNull final String baseCodePath,
-            @Nullable final String[] newResourceDirs) {
+    final void applyNewResourceDirsLocked(@NonNull final ApplicationInfo appInfo,
+            @Nullable final String[] oldPaths) {
         try {
             Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
                     "ResourcesManager#applyNewResourceDirsLocked");
 
+            String baseCodePath = appInfo.getBaseCodePath();
+
+            final int myUid = Process.myUid();
+            String[] newSplitDirs = appInfo.uid == myUid
+                    ? appInfo.splitSourceDirs
+                    : appInfo.splitPublicSourceDirs;
+
+            // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
+            String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
+            String[] copiedResourceDirs = ArrayUtils.cloneOrNull(appInfo.resourceDirs);
+
             final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
             final int implCount = mResourceImpls.size();
             for (int i = 0; i < implCount; i++) {
                 final ResourcesKey key = mResourceImpls.keyAt(i);
                 final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
                 final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
-                if (impl != null && (key.mResDir == null || key.mResDir.equals(baseCodePath))) {
+
+                if (impl == null) {
+                    continue;
+                }
+
+                if (key.mResDir == null
+                        || key.mResDir.equals(baseCodePath)
+                        || ArrayUtils.contains(oldPaths, key.mResDir)) {
                     updatedResourceKeys.put(impl, new ResourcesKey(
-                            key.mResDir,
-                            key.mSplitResDirs,
-                            newResourceDirs,
+                            baseCodePath,
+                            copiedSplitDirs,
+                            copiedResourceDirs,
                             key.mLibDirs,
                             key.mDisplayId,
                             key.mOverrideConfiguration,
-                            key.mCompatInfo));
+                            key.mCompatInfo
+                    ));
                 }
             }
 
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index aabe59d..b8c6d87 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -44,7 +44,7 @@
             STATE_DISABLED,
             STATE_ENABLED,
             STATE_ENABLED_STATIC,
-            STATE_TARGET_UPGRADING,
+            // @Deprecated STATE_TARGET_UPGRADING,
             STATE_OVERLAY_UPGRADING,
     })
     /** @hide */
@@ -96,7 +96,14 @@
      * The target package is currently being upgraded; the state will change
      * once the package installation has finished.
      * @hide
+     *
+     * @deprecated No longer used. Caused invalid transitions from enabled -> upgrading -> enabled,
+     * where an update is propagated when nothing has changed. Can occur during --dont-kill
+     * installs when code and resources are hot swapped and the Activity should not be relaunched.
+     * In all other cases, the process and therefore Activity is killed, so the state loop is
+     * irrelevant.
      */
+    @Deprecated
     public static final int STATE_TARGET_UPGRADING = 4;
 
     /**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 068a93a..5328dda 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -678,6 +678,14 @@
      */
     public static final int PRIVATE_FLAG_IS_RESOURCE_OVERLAY = 1 << 28;
 
+    /**
+     * Value for {@link #privateFlags}: If {@code true} this app allows
+     * shared/external storage media to be a sandboxed view that only contains
+     * files owned by the app.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX = 1 << 29;
 
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
@@ -707,7 +715,8 @@
             PRIVATE_FLAG_VIRTUAL_PRELOAD,
             PRIVATE_FLAG_HAS_FRAGILE_USER_DATA,
             PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE,
-            PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE
+            PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE,
+            PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApplicationInfoPrivateFlags {}
@@ -1822,6 +1831,16 @@
         return (privateFlags & PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) != 0;
     }
 
+    /**
+     * If {@code true} this app allows shared/external storage media to be a
+     * sandboxed view that only contains files owned by the app.
+     *
+     * @hide
+     */
+    public boolean isExternalStorageSandboxAllowed() {
+        return (privateFlags & PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX) != 0;
+    }
+
     private boolean isAllowedToUseHiddenApis() {
         if (isSignedWithPlatformKey()) {
             return true;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c798270..fb22187 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -740,6 +740,8 @@
 
     String getSystemTextClassifierPackageName();
 
+    String getAttentionServicePackageName();
+
     String getWellbeingPackageName();
 
     String getAppPredictionServicePackageName();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 1e6cea3..5d6867e 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1211,6 +1211,9 @@
          * Adds a session ID to the set of sessions that will be committed atomically
          * when this session is committed.
          *
+         * <p>If the parent is staged or has rollback enabled, all children must have
+         * the same properties.
+         *
          * @param sessionId the session ID to add to this multi-package session.
          */
         public void addChildSessionId(int sessionId) {
@@ -1480,6 +1483,9 @@
         /**
          * Request that rollbacks be enabled or disabled for the given upgrade.
          *
+         * <p>If the parent session is staged or has rollback enabled, all children sessions
+         * must have the same properties.
+         *
          * @param enable set to {@code true} to enable, {@code false} to disable
          * @hide
          */
@@ -1607,6 +1613,9 @@
          * multi-package. In that case, if any of the children sessions fail to install at reboot,
          * all the other children sessions are aborted as well.
          *
+         * <p>If the parent session is staged or has rollback enabled, all children sessions
+         * must have the same properties.
+         *
          * {@hide}
          */
         @SystemApi @TestApi
@@ -1626,6 +1635,11 @@
             installFlags |= PackageManager.INSTALL_APEX;
         }
 
+        /** @hide */
+        public boolean getEnableRollback() {
+            return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0;
+        }
+
         /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fe7fb0f..de10bb0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1397,6 +1397,14 @@
      */
     public static final int INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS = -119;
 
+    /**
+     * Installation failed return code: one of the child sessions does not match the parent session
+     * in respect to staged or rollback enabled parameters.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DELETE_" }, value = {
             DELETE_KEEP_DATA,
@@ -6991,6 +6999,16 @@
     }
 
     /**
+     * @return  attention service package name, or null if there's none.
+     *
+     * @hide
+     */
+    public String getAttentionServicePackageName() {
+        throw new UnsupportedOperationException(
+                "getAttentionServicePackageName not implemented in subclass");
+    }
+
+    /**
      * @return the wellbeing app package name, or null if it's not defined by the OEM.
      *
      * @hide
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 35d1eac..0a01dcd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3689,6 +3689,12 @@
             ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE;
         }
 
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_allowExternalStorageSandbox,
+                owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX;
+        }
+
         ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
         ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0);
 
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 49b4cb0..514015f 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1263,12 +1263,19 @@
      */
     @UnsupportedAppUsage
     public boolean isUpToDate() {
-        for (ApkAssets apkAssets : getApkAssets()) {
-            if (!apkAssets.isUpToDate()) {
+        synchronized (this) {
+            if (!mOpen) {
                 return false;
             }
+
+            for (ApkAssets apkAssets : mApkAssets) {
+                if (!apkAssets.isUpToDate()) {
+                    return false;
+                }
+            }
+
+            return true;
         }
-        return true;
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index bad80b8..014bc24 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -39,6 +39,7 @@
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
@@ -47,11 +48,15 @@
  */
 public class SQLiteQueryBuilder {
     private static final String TAG = "SQLiteQueryBuilder";
+
     private static final Pattern sLimitPattern =
             Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
+    private static final Pattern sAggregationPattern = Pattern.compile(
+            "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL)\\((.+)\\)");
 
     private Map<String, String> mProjectionMap = null;
     private List<Pattern> mProjectionGreylist = null;
+    private boolean mProjectionAggregationAllowed = false;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mTables = "";
@@ -203,6 +208,16 @@
         return mProjectionGreylist;
     }
 
+    /** {@hide} */
+    public void setProjectionAggregationAllowed(boolean projectionAggregationAllowed) {
+        mProjectionAggregationAllowed = projectionAggregationAllowed;
+    }
+
+    /** {@hide} */
+    public boolean isProjectionAggregationAllowed() {
+        return mProjectionAggregationAllowed;
+    }
+
     /**
      * Sets the cursor factory to be used for the query.  You can use
      * one factory for all queries on a database but it is normally
@@ -842,26 +857,48 @@
         return query.toString();
     }
 
+    private static @NonNull String maybeWithOperator(@Nullable String operator,
+            @NonNull String column) {
+        if (operator != null) {
+            return operator + "(" + column + ")";
+        } else {
+            return column;
+        }
+    }
+
+    /** {@hide} */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    private String[] computeProjection(String[] projectionIn) {
+    public String[] computeProjection(String[] projectionIn) {
         if (projectionIn != null && projectionIn.length > 0) {
             if (mProjectionMap != null) {
                 String[] projection = new String[projectionIn.length];
                 int length = projectionIn.length;
 
                 for (int i = 0; i < length; i++) {
+                    String operator = null;
                     String userColumn = projectionIn[i];
                     String column = mProjectionMap.get(userColumn);
 
+                    // If aggregation is allowed, extract the underlying column
+                    // that may be aggregated
+                    if (mProjectionAggregationAllowed) {
+                        final Matcher matcher = sAggregationPattern.matcher(userColumn);
+                        if (matcher.matches()) {
+                            operator = matcher.group(1);
+                            userColumn = matcher.group(2);
+                            column = mProjectionMap.get(userColumn);
+                        }
+                    }
+
                     if (column != null) {
-                        projection[i] = column;
+                        projection[i] = maybeWithOperator(operator, column);
                         continue;
                     }
 
                     if (!mStrict &&
                             ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
                         /* A column alias already exist */
-                        projection[i] = userColumn;
+                        projection[i] = maybeWithOperator(operator, userColumn);
                         continue;
                     }
 
@@ -878,7 +915,7 @@
 
                         if (match) {
                             Log.w(TAG, "Allowing abusive custom column: " + userColumn);
-                            projection[i] = userColumn;
+                            projection[i] = maybeWithOperator(operator, userColumn);
                             continue;
                         }
                     }
@@ -911,7 +948,8 @@
         return null;
     }
 
-    private @Nullable String computeWhere(@Nullable String selection) {
+    /** {@hide} */
+    public @Nullable String computeWhere(@Nullable String selection) {
         final boolean hasInternal = !TextUtils.isEmpty(mWhereClause);
         final boolean hasExternal = !TextUtils.isEmpty(selection);
 
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
index 687b721..06c32c6 100644
--- a/core/java/android/net/DnsResolver.java
+++ b/core/java/android/net/DnsResolver.java
@@ -62,7 +62,7 @@
     private static final String TAG = "DnsResolver";
     private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
     private static final int MAXPACKET = 8 * 1024;
-    private static final int SLEEP_TIME = 2;
+    private static final int SLEEP_TIME_MS = 2;
 
     @IntDef(prefix = { "CLASS_" }, value = {
             CLASS_IN
@@ -228,8 +228,11 @@
             return;
         }
 
-        registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
-        maybeAddCancellationSignal(cancellationSignal, queryfd, lock);
+        synchronized (lock)  {
+            registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+            if (cancellationSignal == null) return;
+            addCancellationSignal(cancellationSignal, queryfd, lock);
+        }
     }
 
     /**
@@ -267,9 +270,11 @@
             });
             return;
         }
-
-        registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
-        maybeAddCancellationSignal(cancellationSignal, queryfd, lock);
+        synchronized (lock)  {
+            registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+            if (cancellationSignal == null) return;
+            addCancellationSignal(cancellationSignal, queryfd, lock);
+        }
     }
 
     private class InetAddressAnswerAccumulator extends InetAddressAnswerCallback {
@@ -368,10 +373,10 @@
             queryCount++;
         } else v6fd = null;
 
-        // TODO: Use device flag to controll the sleep time.
+        // TODO: Use device flag to control the sleep time.
         // Avoiding gateways drop packets if queries are sent too close together
         try {
-            Thread.sleep(SLEEP_TIME);
+            Thread.sleep(SLEEP_TIME_MS);
         } catch (InterruptedException ex) { }
 
         if (queryIpv4) {
@@ -391,16 +396,21 @@
         final InetAddressAnswerAccumulator accumulator =
                 new InetAddressAnswerAccumulator(queryCount, callback);
 
-        if (queryIpv6) registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
-        if (queryIpv4) registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
-
-        if (cancellationSignal == null) return;
-        cancellationSignal.setOnCancelListener(() -> {
-            synchronized (lock)  {
-                if (queryIpv4) cancelQuery(v4fd);
-                if (queryIpv6) cancelQuery(v6fd);
+        synchronized (lock)  {
+            if (queryIpv6) {
+                registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
             }
-        });
+            if (queryIpv4) {
+                registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
+            }
+            if (cancellationSignal == null) return;
+            cancellationSignal.setOnCancelListener(() -> {
+                synchronized (lock)  {
+                    if (queryIpv4) cancelQuery(v4fd);
+                    if (queryIpv6) cancelQuery(v6fd);
+                }
+            });
+        }
     }
 
     private <T> void registerFDListener(@NonNull Executor executor,
@@ -443,9 +453,8 @@
         resNetworkCancel(queryfd);  // Closes fd, marks it invalid.
     }
 
-    private void maybeAddCancellationSignal(@Nullable CancellationSignal cancellationSignal,
+    private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal,
             @NonNull FileDescriptor queryfd, @NonNull Object lock) {
-        if (cancellationSignal == null) return;
         cancellationSignal.setOnCancelListener(() -> {
             synchronized (lock)  {
                 cancelQuery(queryfd);
diff --git a/core/java/android/os/BatterySaverPolicyConfig.java b/core/java/android/os/BatterySaverPolicyConfig.java
index bda4e27..879ab1e 100644
--- a/core/java/android/os/BatterySaverPolicyConfig.java
+++ b/core/java/android/os/BatterySaverPolicyConfig.java
@@ -58,7 +58,8 @@
         mAdvertiseIsEnabled = in.mAdvertiseIsEnabled;
         mDeferFullBackup = in.mDeferFullBackup;
         mDeferKeyValueBackup = in.mDeferKeyValueBackup;
-        mDeviceSpecificSettings = Collections.unmodifiableMap(in.mDeviceSpecificSettings);
+        mDeviceSpecificSettings = Collections.unmodifiableMap(
+                new ArrayMap<>(in.mDeviceSpecificSettings));
         mDisableAnimation = in.mDisableAnimation;
         mDisableAod = in.mDisableAod;
         mDisableLaunchBoost = in.mDisableLaunchBoost;
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index cceb6ed..f7e927e 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -20,6 +20,8 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.os.storage.StorageManager;
@@ -1060,7 +1062,7 @@
      * @throws IllegalArgumentException if the path is not a valid storage
      *             device.
      */
-    public static boolean isExternalStorageRemovable(File path) {
+    public static boolean isExternalStorageRemovable(@NonNull File path) {
         final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
         if (volume != null) {
             return volume.isRemovable();
@@ -1103,7 +1105,7 @@
      * @throws IllegalArgumentException if the path is not a valid storage
      *             device.
      */
-    public static boolean isExternalStorageEmulated(File path) {
+    public static boolean isExternalStorageEmulated(@NonNull File path) {
         final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
         if (volume != null) {
             return volume.isEmulated();
@@ -1112,6 +1114,44 @@
         }
     }
 
+    /**
+     * Returns whether the shared/external storage media at the given path is a
+     * sandboxed view that only contains files owned by the app.
+     * <p>
+     * This value may be different from the value requested by
+     * {@code allowExternalStorageSandbox} in the app's manifest, since an app
+     * may inherit its sandboxed state based on when it was first installed.
+     * <p>
+     * Sandboxed apps can continue to discover and read media belonging to other
+     * apps via {@link android.provider.MediaStore}.
+     */
+    public static boolean isExternalStorageSandboxed() {
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
+        return isExternalStorageSandboxed(externalDir);
+    }
+
+    /**
+     * Returns whether the shared/external storage media at the given path is a
+     * sandboxed view that only contains files owned by the app.
+     * <p>
+     * This value may be different from the value requested by
+     * {@code allowExternalStorageSandbox} in the app's manifest, since an app
+     * may inherit its sandboxed state based on when it was first installed.
+     * <p>
+     * Sandboxed apps can continue to discover and read media belonging to other
+     * apps via {@link android.provider.MediaStore}.
+     *
+     * @throws IllegalArgumentException if the path is not a valid storage
+     *             device.
+     */
+    public static boolean isExternalStorageSandboxed(@NonNull File path) {
+        final Context context = AppGlobals.getInitialApplication();
+        final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+        return appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
+                context.getApplicationInfo().uid,
+                context.getPackageName()) != AppOpsManager.MODE_ALLOWED;
+    }
+
     static File getDirectory(String variableName, String defaultPath) {
         String path = System.getenv(variableName);
         return path == null ? new File(defaultPath) : new File(path);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 64e2f89..7d61bf6 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -773,8 +773,10 @@
      */
     public static final int LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF = 4;
 
-    static final int MIN_LOCATION_MODE = LOCATION_MODE_NO_CHANGE;
-    static final int MAX_LOCATION_MODE = LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+    /** @hide */
+    public static final int MIN_LOCATION_MODE = LOCATION_MODE_NO_CHANGE;
+    /** @hide */
+    public static final int MAX_LOCATION_MODE = LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
 
     /**
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1cab250..5c2eacc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14726,7 +14726,6 @@
          *
          * @hide
          */
-        // TODO(b/117663715): require a new write permission restricted to a single source
         @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
         static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode,
                 @Nullable String prefix) {
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 610f7ed..715181f 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -512,8 +512,8 @@
      * @hide
      */
     public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
-        if (isBoundsEmpty()
-                || insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0) {
+        if (insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0
+                || isBoundsEmpty()) {
             return this;
         }
 
@@ -534,6 +534,12 @@
             safeInsets.right = atLeastZero(safeInsets.right - insetRight);
         }
 
+        // If we are not cutting off part of the cutout by insetting it on bottom/right, and we also
+        // don't move it around, we can avoid the allocation and copy of the instance.
+        if (insetLeft == 0 && insetTop == 0 && mSafeInsets.equals(safeInsets)) {
+            return this;
+        }
+
         Rect[] bounds = mBounds.getRects();
         for (int i = 0; i < bounds.length; ++i) {
             if (!bounds[i].equals(ZERO_RECT)) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5df990c..24f4c14 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7955,6 +7955,8 @@
      *                               View is not a pane.
      *
      * {@see AccessibilityNodeInfo#setPaneTitle(CharSequence)}
+     *
+     * @attr ref android.R.styleable#View_accessibilityPaneTitle
      */
     public void setAccessibilityPaneTitle(@Nullable CharSequence accessibilityPaneTitle) {
         if (!TextUtils.equals(accessibilityPaneTitle, mAccessibilityPaneTitle)) {
@@ -7970,6 +7972,8 @@
      * @return The current pane title.
      *
      * {@see #setAccessibilityPaneTitle}.
+     *
+     * @attr ref android.R.styleable#View_accessibilityPaneTitle
      */
     @InspectableProperty
     @Nullable
@@ -12112,6 +12116,8 @@
      * @see #setScreenReaderFocusable(boolean)
      *
      * @return Whether the view should be treated as a focusable unit by screen reader.
+     *
+     * @attr ref android.R.styleable#View_screenReaderFocusable
      */
     @InspectableProperty
     public boolean isScreenReaderFocusable() {
@@ -12130,6 +12136,8 @@
      *
      * @param screenReaderFocusable Whether the view should be treated as a unit by screen reader
      *                              accessibility tools.
+     *
+     * @attr ref android.R.styleable#View_screenReaderFocusable
      */
     public void setScreenReaderFocusable(boolean screenReaderFocusable) {
         updatePflags3AndNotifyA11yIfChanged(PFLAG3_SCREEN_READER_FOCUSABLE, screenReaderFocusable);
@@ -17960,7 +17968,7 @@
         final int scrollX = mScrollX;
         final int scrollY = mScrollY;
         invalidateInternal(dirty.left - scrollX, dirty.top - scrollY,
-                dirty.right - scrollX, dirty.bottom - scrollY, true, false);
+                dirty.right - scrollX, dirty.bottom - scrollY, true);
     }
 
     /**
@@ -17986,7 +17994,7 @@
     public void invalidate(int l, int t, int r, int b) {
         final int scrollX = mScrollX;
         final int scrollY = mScrollY;
-        invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);
+        invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true);
     }
 
     /**
@@ -18016,11 +18024,10 @@
      */
     @UnsupportedAppUsage
     public void invalidate(boolean invalidateCache) {
-        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
+        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache);
     }
 
-    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
-            boolean fullInvalidate) {
+    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache) {
         if (mGhostView != null) {
             mGhostView.invalidate(true);
             return;
@@ -18037,11 +18044,9 @@
         if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
                 || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
                 || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
-                || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
-            if (fullInvalidate) {
-                mLastIsOpaque = isOpaque();
-                mPrivateFlags &= ~PFLAG_DRAWN;
-            }
+                || isOpaque() != mLastIsOpaque) {
+            mLastIsOpaque = isOpaque();
+            mPrivateFlags &= ~PFLAG_DRAWN;
 
             mPrivateFlags |= PFLAG_DIRTY;
 
@@ -22586,12 +22591,7 @@
     @Override
     public void invalidateDrawable(@NonNull Drawable drawable) {
         if (verifyDrawable(drawable)) {
-            final Rect dirty = drawable.getDirtyBounds();
-            final int scrollX = mScrollX;
-            final int scrollY = mScrollY;
-
-            invalidate(dirty.left + scrollX, dirty.top + scrollY,
-                    dirty.right + scrollX, dirty.bottom + scrollY);
+            invalidate();
             rebuildOutline();
         }
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5f60333..49166ad 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1455,6 +1455,7 @@
 
     @Override
     public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
+        checkThread();
         if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
             mIsAnimating = true;
         }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f29174b..f14b50d 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1594,7 +1594,7 @@
             if (info == null) return null;
 
             // Now fetch app icon and raster with no badging even in work profile
-            Bitmap appIcon = (new ActivityInfoPresentationGetter(info)).getIconBitmap();
+            Bitmap appIcon = makePresentationGetter(info).getIconBitmap();
 
             // Raster target drawable with appIcon as a badge
             SimpleIconFactory sif = SimpleIconFactory.obtain(ChooserActivity.this);
@@ -1865,8 +1865,9 @@
                         ri.noResourceId = true;
                         ri.icon = 0;
                     }
+                    ResolveInfoPresentationGetter getter = makePresentationGetter(ri);
                     mCallerTargets.add(new DisplayResolveInfo(ii, ri,
-                            ri.loadLabel(pm), null, ii));
+                            getter.getLabel(), getter.getSubLabel(), ii));
                 }
             }
         }
@@ -1879,12 +1880,6 @@
         }
 
         @Override
-        public boolean showsExtendedInfo(TargetInfo info) {
-            // We have badges so we don't need this text shown.
-            return false;
-        }
-
-        @Override
         public View onCreateView(ViewGroup parent) {
             return mInflater.inflate(
                     com.android.internal.R.layout.resolve_grid_item, parent, false);
@@ -2301,8 +2296,10 @@
             final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
             int columnCount = holder.getColumnCount();
 
+            final boolean isDirectShare = holder instanceof DirectShareViewHolder;
+
             for (int i = 0; i < columnCount; i++) {
-                final View v = mChooserListAdapter.createView(holder.getRow(i));
+                final View v = mChooserListAdapter.createView(holder.getRowByIndex(i));
                 final int column = i;
                 v.setOnClickListener(new OnClickListener() {
                     @Override
@@ -2321,27 +2318,31 @@
                 });
                 ViewGroup row = holder.addView(i, v);
 
-                // Force height to be a given so we don't have visual disruption during scaling.
-                LayoutParams lp = v.getLayoutParams();
-                v.measure(spec, spec);
-                if (lp == null) {
-                    lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight());
-                    row.setLayoutParams(lp);
-                } else {
-                    lp.height = v.getMeasuredHeight();
+                // Force Direct Share to be 2 lines and auto-wrap to second line via hoz scroll =
+                // false. TextView#setHorizontallyScrolling must be reset after #setLines. Must be
+                // done before measuring.
+                if (isDirectShare) {
+                    final ViewHolder vh = (ViewHolder) v.getTag();
+                    vh.text.setLines(2);
+                    vh.text.setHorizontallyScrolling(false);
+                    vh.text2.setVisibility(View.GONE);
                 }
+
+                // Force height to be a given so we don't have visual disruption during scaling.
+                v.measure(spec, spec);
+                setViewHeight(v, v.getMeasuredHeight());
             }
 
             final ViewGroup viewGroup = holder.getViewGroup();
 
-            // Pre-measure so we can scale later.
+            // Pre-measure and fix height so we can scale later.
             holder.measure();
-            LayoutParams lp = viewGroup.getLayoutParams();
-            if (lp == null) {
-                lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.getMeasuredRowHeight());
-                viewGroup.setLayoutParams(lp);
-            } else {
-                lp.height = holder.getMeasuredRowHeight();
+            setViewHeight(viewGroup, holder.getMeasuredRowHeight());
+
+            if (isDirectShare) {
+                DirectShareViewHolder dsvh = (DirectShareViewHolder) holder;
+                setViewHeight(dsvh.getRow(0), holder.getMeasuredRowHeight());
+                setViewHeight(dsvh.getRow(1), holder.getMeasuredRowHeight());
             }
 
             viewGroup.setTag(holder);
@@ -2349,6 +2350,16 @@
             return holder;
         }
 
+        private void setViewHeight(View view, int heightPx) {
+            LayoutParams lp = view.getLayoutParams();
+            if (lp == null) {
+                lp = new LayoutParams(LayoutParams.MATCH_PARENT, heightPx);
+                view.setLayoutParams(lp);
+            } else {
+                lp.height = heightPx;
+            }
+        }
+
         RowViewHolder createViewHolder(int viewType, ViewGroup parent) {
             if (viewType == VIEW_TYPE_DIRECT_SHARE) {
                 ViewGroup parentGroup = (ViewGroup) mLayoutInflater.inflate(
@@ -2386,7 +2397,7 @@
 
             if (startType != lastStartType || rowPosition == getContentPreviewRowCount()) {
                 row.setBackground(mChooserRowLayer);
-                setVertPadding(row, mChooserRowServiceSpacing, 0);
+                setVertPadding(row, 0, 0);
             } else {
                 row.setBackground(null);
                 setVertPadding(row, 0, 0);
@@ -2483,7 +2494,9 @@
 
         abstract ViewGroup getViewGroup();
 
-        abstract ViewGroup getRow(int index);
+        abstract ViewGroup getRowByIndex(int index);
+
+        abstract ViewGroup getRow(int rowNumber);
 
         abstract void setViewVisibility(int i, int visibility);
 
@@ -2532,10 +2545,15 @@
             return mRow;
         }
 
-        public ViewGroup getRow(int index) {
+        public ViewGroup getRowByIndex(int index) {
             return mRow;
         }
 
+        public ViewGroup getRow(int rowNumber) {
+            if (rowNumber == 0) return mRow;
+            return null;
+        }
+
         public ViewGroup addView(int index, View v) {
             mRow.addView(v);
             mCells[index] = v;
@@ -2574,7 +2592,7 @@
         }
 
         public ViewGroup addView(int index, View v) {
-            ViewGroup row = getRow(index);
+            ViewGroup row = getRowByIndex(index);
             row.addView(v);
             mCells[index] = v;
 
@@ -2589,10 +2607,14 @@
             return mParent;
         }
 
-        public ViewGroup getRow(int index) {
+        public ViewGroup getRowByIndex(int index) {
             return mRows.get(index / mCellCountPerRow);
         }
 
+        public ViewGroup getRow(int rowNumber) {
+            return mRows.get(rowNumber);
+        }
+
         public void measure() {
             final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
             getRow(0).measure(spec, spec);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 84a1bed..9f9e083 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -83,7 +83,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
@@ -499,33 +498,40 @@
 
 
     /**
-     * Loads the icon for the provided ApplicationInfo. Defaults to using the application icon over
-     * any IntentFilter or Activity icon to increase user understanding, with an exception for
-     * applications that hold the right permission. Always attempts to use icon resources over
-     * PackageManager loading mechanisms so badging can be done by iconloader.
+     * Loads the icon and label for the provided ApplicationInfo. Defaults to using the application
+     * icon and label over any IntentFilter or Activity icon to increase user understanding, with an
+     * exception for applications that hold the right permission. Always attempts to use available
+     * resources over PackageManager loading mechanisms so badging can be done by iconloader. Uses
+     * Strings to strip creative formatting.
      */
-    private abstract class TargetPresentationGetter {
-        @Nullable abstract Drawable getIconSubstitute();
-        @Nullable abstract String getAppSubLabel();
+    private abstract static class TargetPresentationGetter {
+        @Nullable abstract Drawable getIconSubstituteInternal();
+        @Nullable abstract String getAppSubLabelInternal();
 
+        private Context mCtx;
+        protected PackageManager mPm;
         private final ApplicationInfo mAi;
+        private final int mIconDpi;
         private final boolean mHasSubstitutePermission;
 
-        TargetPresentationGetter(ApplicationInfo ai) {
+        TargetPresentationGetter(Context ctx, int iconDpi, ApplicationInfo ai) {
+            mCtx = ctx;
+            mPm = ctx.getPackageManager();
             mAi = ai;
+            mIconDpi = iconDpi;
             mHasSubstitutePermission = PackageManager.PERMISSION_GRANTED == mPm.checkPermission(
                     android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON,
                     mAi.packageName);
         }
 
-        Drawable getIcon() {
-            return new BitmapDrawable(getResources(), getIconBitmap());
+        public Drawable getIcon() {
+            return new BitmapDrawable(mCtx.getResources(), getIconBitmap());
         }
 
-        Bitmap getIconBitmap() {
+        public Bitmap getIconBitmap() {
             Drawable dr = null;
             if (mHasSubstitutePermission) {
-                dr = getIconSubstitute();
+                dr = getIconSubstituteInternal();
             }
 
             if (dr == null) {
@@ -542,18 +548,18 @@
                 dr = mAi.loadIcon(mPm);
             }
 
-            SimpleIconFactory sif = SimpleIconFactory.obtain(ResolverActivity.this);
+            SimpleIconFactory sif = SimpleIconFactory.obtain(mCtx);
             Bitmap icon = sif.createUserBadgedIconBitmap(dr, Process.myUserHandle());
             sif.recycle();
 
             return icon;
         }
 
-        String getLabel() {
+        public String getLabel() {
             String label = null;
             // Apps with the substitute permission will always show the sublabel as their label
             if (mHasSubstitutePermission) {
-                label = getAppSubLabel();
+                label = getAppSubLabelInternal();
             }
 
             if (label == null) {
@@ -563,10 +569,14 @@
             return label;
         }
 
-        String getSubLabel() {
+        public String getSubLabel() {
             // Apps with the substitute permission will never have a sublabel
             if (mHasSubstitutePermission) return null;
-            return getAppSubLabel();
+            return getAppSubLabelInternal();
+        }
+
+        protected String loadLabelFromResource(Resources res, int resId) {
+            return res.getString(resId);
         }
 
         @Nullable
@@ -576,17 +586,19 @@
 
     }
 
-    protected class ResolveInfoPresentationGetter extends TargetPresentationGetter {
-
+    /**
+     * Loads the icon and label for the provided ResolveInfo.
+     */
+    @VisibleForTesting
+    public static class ResolveInfoPresentationGetter extends TargetPresentationGetter {
         private final ResolveInfo mRi;
-
-        ResolveInfoPresentationGetter(ResolveInfo ri) {
-            super(ri.activityInfo.applicationInfo);
+        public ResolveInfoPresentationGetter(Context ctx, int iconDpi, ResolveInfo ri) {
+            super(ctx, iconDpi, ri.activityInfo.applicationInfo);
             mRi = ri;
         }
 
         @Override
-        Drawable getIconSubstitute() {
+        Drawable getIconSubstituteInternal() {
             Drawable dr = null;
             try {
                 // Do not use ResolveInfo#getIconResource() as it defaults to the app
@@ -603,20 +615,31 @@
         }
 
         @Override
-        String getAppSubLabel() {
+        String getAppSubLabelInternal() {
+            // Will default to app name if no intent filter or activity label set, make sure to
+            // check if subLabel matches label before final display
             return (String) mRi.loadLabel(mPm);
         }
     }
 
-    protected class ActivityInfoPresentationGetter extends TargetPresentationGetter {
+    ResolveInfoPresentationGetter makePresentationGetter(ResolveInfo ri) {
+        return new ResolveInfoPresentationGetter(this, mIconDpi, ri);
+    }
+
+    /**
+     * Loads the icon and label for the provided ActivityInfo.
+     */
+    @VisibleForTesting
+    public static class ActivityInfoPresentationGetter extends TargetPresentationGetter {
         private final ActivityInfo mActivityInfo;
-        protected ActivityInfoPresentationGetter(ActivityInfo activityInfo) {
-            super(activityInfo.applicationInfo);
+        public ActivityInfoPresentationGetter(Context ctx, int iconDpi,
+                ActivityInfo activityInfo) {
+            super(ctx, iconDpi, activityInfo.applicationInfo);
             mActivityInfo = activityInfo;
         }
 
         @Override
-        Drawable getIconSubstitute() {
+        Drawable getIconSubstituteInternal() {
             Drawable dr = null;
             try {
                 // Do not use ActivityInfo#getIconResource() as it defaults to the app
@@ -634,13 +657,19 @@
         }
 
         @Override
-        String getAppSubLabel() {
+        String getAppSubLabelInternal() {
+            // Will default to app name if no activity label set, make sure to check if subLabel
+            // matches label before final display
             return (String) mActivityInfo.loadLabel(mPm);
         }
     }
 
+    protected ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo ai) {
+        return new ActivityInfoPresentationGetter(this, mIconDpi, ai);
+    }
+
     Drawable loadIconForResolveInfo(ResolveInfo ri) {
-        return (new ResolveInfoPresentationGetter(ri)).getIcon();
+        return makePresentationGetter(ri).getIcon();
     }
 
     @Override
@@ -1713,34 +1742,13 @@
                     }
                 }
 
-                // Check for applications with same name and use application name or
-                // package name if necessary
-                ResolvedComponentInfo rci0 = sortedComponents.get(0);
-                ResolveInfo r0 = rci0.getResolveInfoAt(0);
-                int start = 0;
-                CharSequence r0Label = r0.loadLabel(mPm);
-                mHasExtendedInfo = false;
-                for (int i = 1; i < N; i++) {
-                    if (r0Label == null) {
-                        r0Label = r0.activityInfo.packageName;
+                for (ResolvedComponentInfo rci : sortedComponents) {
+                    final ResolveInfo ri = rci.getResolveInfoAt(0);
+                    if (ri != null) {
+                        ResolveInfoPresentationGetter pg = makePresentationGetter(ri);
+                        addResolveInfoWithAlternates(rci, pg.getSubLabel(), pg.getLabel());
                     }
-                    ResolvedComponentInfo rci = sortedComponents.get(i);
-                    ResolveInfo ri = rci.getResolveInfoAt(0);
-                    CharSequence riLabel = ri.loadLabel(mPm);
-                    if (riLabel == null) {
-                        riLabel = ri.activityInfo.packageName;
-                    }
-                    if (riLabel.equals(r0Label)) {
-                        continue;
-                    }
-                    processGroup(sortedComponents, start, (i - 1), rci0, r0Label);
-                    rci0 = rci;
-                    r0 = ri;
-                    r0Label = riLabel;
-                    start = i;
                 }
-                // Process last group
-                processGroup(sortedComponents, start, (N - 1), rci0, r0Label);
             }
 
             postListReadyRunnable();
@@ -1782,55 +1790,6 @@
             return mFilterLastUsed;
         }
 
-        private void processGroup(List<ResolvedComponentInfo> rList, int start, int end,
-                ResolvedComponentInfo ro, CharSequence roLabel) {
-            // Process labels from start to i
-            int num = end - start+1;
-            if (num == 1) {
-                // No duplicate labels. Use label for entry at start
-                addResolveInfoWithAlternates(ro, null, roLabel);
-            } else {
-                mHasExtendedInfo = true;
-                boolean usePkg = false;
-                final ApplicationInfo ai = ro.getResolveInfoAt(0).activityInfo.applicationInfo;
-                final CharSequence startApp = ai.loadLabel(mPm);
-                if (startApp == null) {
-                    usePkg = true;
-                }
-                if (!usePkg) {
-                    // Use HashSet to track duplicates
-                    HashSet<CharSequence> duplicates =
-                        new HashSet<CharSequence>();
-                    duplicates.add(startApp);
-                    for (int j = start+1; j <= end ; j++) {
-                        ResolveInfo jRi = rList.get(j).getResolveInfoAt(0);
-                        CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
-                        if ( (jApp == null) || (duplicates.contains(jApp))) {
-                            usePkg = true;
-                            break;
-                        } else {
-                            duplicates.add(jApp);
-                        }
-                    }
-                    // Clear HashSet for later use
-                    duplicates.clear();
-                }
-                for (int k = start; k <= end; k++) {
-                    final ResolvedComponentInfo rci = rList.get(k);
-                    final ResolveInfo add = rci.getResolveInfoAt(0);
-                    final CharSequence extraInfo;
-                    if (usePkg) {
-                        // Use package name for all entries from start to end-1
-                        extraInfo = add.activityInfo.packageName;
-                    } else {
-                        // Use application name for all entries from start to end-1
-                        extraInfo = add.activityInfo.applicationInfo.loadLabel(mPm);
-                    }
-                    addResolveInfoWithAlternates(rci, extraInfo, roLabel);
-                }
-            }
-        }
-
         private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
                 CharSequence extraInfo, CharSequence roLabel) {
             final int count = rci.getCount();
@@ -1979,31 +1938,32 @@
                     com.android.internal.R.layout.resolve_list_item, parent, false);
         }
 
-        public boolean showsExtendedInfo(TargetInfo info) {
-            return !TextUtils.isEmpty(info.getExtendedInfo());
-        }
-
         public final void bindView(int position, View view) {
             onBindView(view, getItem(position));
         }
 
-        private void onBindView(View view, TargetInfo info) {
+        protected void onBindView(View view, TargetInfo info) {
             final ViewHolder holder = (ViewHolder) view.getTag();
             if (info == null) {
                 holder.icon.setImageDrawable(
                         getDrawable(R.drawable.resolver_icon_placeholder));
                 return;
             }
+
             final CharSequence label = info.getDisplayLabel();
             if (!TextUtils.equals(holder.text.getText(), label)) {
                 holder.text.setText(info.getDisplayLabel());
             }
-            if (showsExtendedInfo(info)) {
-                holder.text2.setVisibility(View.VISIBLE);
-                holder.text2.setText(info.getExtendedInfo());
-            } else {
-                holder.text2.setVisibility(View.GONE);
+
+            // Always show a subLabel for visual consistency across list items. Show an empty
+            // subLabel if the subLabel is the same as the label
+            CharSequence subLabel = info.getExtendedInfo();
+            if (TextUtils.equals(label, subLabel)) subLabel = null;
+
+            if (!TextUtils.equals(holder.text2.getText(), subLabel)) {
+                holder.text2.setText(subLabel);
             }
+
             if (info instanceof DisplayResolveInfo
                     && !((DisplayResolveInfo) info).hasDisplayIcon()) {
                 new LoadAdapterIconTask((DisplayResolveInfo) info).execute();
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 3303374..e8691fa 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -527,6 +527,13 @@
         return (array != null) ? array.clone() : null;
     }
 
+    /**
+     * Clones an array or returns null if the array is null.
+     */
+    public static @Nullable <T> T[] cloneOrNull(@Nullable T[] array) {
+        return (array != null) ? array.clone() : null;
+    }
+
     public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
         return (array != null) ? new ArraySet<T>(array) : null;
     }
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index f40b461..bd4862d 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -106,8 +106,7 @@
 
 static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
   const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  (void)apk_assets;
-  return JNI_TRUE;
+  return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
 }
 
 static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index bf33ac6..d029527 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2328,4 +2328,7 @@
 
     // OPEN: Settings > System > Aware > Info dialog
     DIALOG_AWARE_STATUS = 1701;
+
+    // Open: Settings > app > bubble settings > confirmation dialog
+    DIALOG_APP_BUBBLE_SETTINGS = 1702;
 }
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 4a3dfba..7065149 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -22,46 +22,43 @@
               android:layout_height="wrap_content"
               android:minHeight="100dp"
               android:gravity="center"
-              android:paddingTop="8dp"
+              android:paddingTop="24dp"
               android:paddingBottom="8dp"
+              android:paddingLeft="2dp"
+              android:paddingRight="2dp"
               android:focusable="true"
               android:background="?attr/selectableItemBackgroundBorderless">
 
     <ImageView android:id="@+id/icon"
                android:layout_width="@dimen/resolver_icon_size"
                android:layout_height="@dimen/resolver_icon_size"
-               android:layout_marginLeft="3dp"
-               android:layout_marginRight="3dp"
-               android:layout_marginBottom="3dp"
                android:scaleType="fitCenter" />
 
-    <!-- Activity name -->
+    <!-- Size manually tuned to match specs -->
+    <Space android:layout_width="1dp"
+           android:layout_height="7dp"/>
+
+    <!-- App name or Direct Share target name, DS set to 2 lines -->
     <TextView android:id="@android:id/text1"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
-              android:layout_marginTop="4dp"
-              android:layout_marginLeft="4dp"
-              android:layout_marginRight="4dp"
               android:textAppearance="?attr/textAppearanceSmall"
               android:textColor="?attr/textColorPrimary"
-              android:textSize="12sp"
+              android:textSize="14sp"
               android:fontFamily="sans-serif-condensed"
               android:gravity="top|center_horizontal"
-              android:minLines="2"
-              android:maxLines="2"
-              android:ellipsize="marquee" />
-    <!-- Extended activity info to distinguish between duplicate activity names -->
+              android:lines="1"
+              android:ellipsize="end" />
+
+    <!-- Activity name if set, gone for Direct Share targets -->
     <TextView android:id="@android:id/text2"
               android:textAppearance="?android:attr/textAppearanceSmall"
               android:textSize="12sp"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
-              android:layout_marginLeft="4dp"
-              android:layout_marginRight="4dp"
-              android:minLines="2"
-              android:maxLines="2"
+              android:lines="1"
               android:gravity="top|center_horizontal"
-              android:ellipsize="marquee"
-              android:visibility="gone" />
+              android:ellipsize="end"/>
+
 </LinearLayout>
 
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 362d01c..8f3f25a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1687,6 +1687,17 @@
                  - {@code false} for apps with targetSdkVersion < 29.
              -->
         <attr name="allowAudioPlaybackCapture" format="boolean" />
+        <!-- If {@code true} this app allows shared/external storage media to be
+             a sandboxed view that only contains files owned by the app.
+             <p>
+             Sandboxed apps can continue to discover and read media belonging to other
+             apps via {@code MediaStore}.
+             <p>
+             The default value is:
+                 - {@code true} for apps with targetSdkVersion >= 29 (Q).
+                 - {@code false} for apps with targetSdkVersion < 29.
+             -->
+        <attr name="allowExternalStorageSandbox" format="boolean" />
     </declare-styleable>
     <!-- The <code>permission</code> tag declares a security permission that can be
          used to control access from other packages to specific components or
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 77ce8c3..2139453 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3983,6 +3983,9 @@
     <!-- Whether or not to enable automatic heap dumps for the system server on debuggable builds. -->
     <bool name="config_debugEnableAutomaticSystemServerHeapDumps">false</bool>
 
+    <!-- Trigger a heap dump if the system server pss usage exceeds this threshold. 400 MB -->
+    <integer name="config_debugSystemServerPssThresholdBytes">419430400</integer>
+
     <!-- See DropBoxManagerService.
          The minimum period in milliseconds between broadcasts for entries with low priority
          dropbox tags. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3505994..3bbc03f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2942,6 +2942,7 @@
         <public name="allowClearUserDataOnFailedRestore"/>
         <public name="allowAudioPlaybackCapture"/>
         <public name="secureElementName" />
+        <public name="allowExternalStorageSandbox"/>
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 45494a0..4320bf4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1582,25 +1582,25 @@
     </string-array>
 
     <!-- Error message shown when the face hardware can't be accessed. [CHAR LIMIT=50] -->
-    <string name="face_error_hw_not_available">Face hardware not available.</string>
+    <string name="face_error_hw_not_available">Can\u2019t verify face. Hardware not available.</string>
     <!-- Error message shown when the face hardware timer has expired and the user needs to restart the operation. [CHAR LIMIT=50] -->
     <string name="face_error_timeout">Face timeout reached. Try again.</string>
-    <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=50] -->
-    <string name="face_error_no_space">Face can\u2019t be stored.</string>
+    <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=60] -->
+    <string name="face_error_no_space">Can\u2019t store new face data. Delete an old one first.</string>
     <!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
-    <string name="face_error_canceled">Face operation canceled.</string>
+    <string name="face_error_canceled">Face operation canceled</string>
     <!-- Generic error message shown when the face authentication operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=50] -->
-    <string name="face_error_user_canceled">Face authentication canceled by user.</string>
+    <string name="face_error_user_canceled">Face authentication canceled by user</string>
     <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
     <string name="face_error_lockout">Too many attempts. Try again later.</string>
-    <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=50] -->
-    <string name="face_error_lockout_permanent">Too many attempts. Facial authentication disabled.</string>
+    <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=60] -->
+    <string name="face_error_lockout_permanent">Too many attempts. Face authentication disabled.</string>
     <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
     <string name="face_error_unable_to_process">Can\u2019t verify face. Try again.</string>
     <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=50] -->
-    <string name="face_error_not_enrolled">You haven\u2019t set up face authentication.</string>
+    <string name="face_error_not_enrolled">You haven\u2019t set up face authentication</string>
     <!-- Generic error message shown when the app requests face authentication on a device without a sensor. [CHAR LIMIT=60] -->
-    <string name="face_error_hw_not_present">Face authentication is not supported on this device.</string>
+    <string name="face_error_hw_not_present">Face authentication is not supported on this device</string>
 
     <!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] -->
     <string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string>
@@ -4325,10 +4325,10 @@
 
     <!-- Activity starter -->
     <!-- Toast message for blocking background activity starts feature running in permissive mode -->
-    <string name="activity_starter_block_bg_activity_starts_permissive">This background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will be blocked in future Q builds. See go/q-bg-block.</string>
+    <string name="activity_starter_block_bg_activity_starts_permissive">This background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will be blocked in future Q builds. See g.co/dev/bgblock.</string>
 
     <!-- Toast message for blocking background activity starts feature running in enforcing mode -->
-    <string name="activity_starter_block_bg_activity_starts_enforcing">Background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> blocked. See go/q-bg-block. </string>
+    <string name="activity_starter_block_bg_activity_starts_enforcing">Background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> blocked. See g.co/dev/bgblock. </string>
 
     <!-- Keyguard strings -->
     <!-- Message shown in pattern unlock after some number of unsuccessful attempts -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 35fd88e..4188bd4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3230,6 +3230,7 @@
   <java-symbol type="bool" name="config_batterymeterDualTone" />
 
   <java-symbol type="bool" name="config_debugEnableAutomaticSystemServerHeapDumps" />
+  <java-symbol type="integer" name="config_debugSystemServerPssThresholdBytes" />
 
   <!-- Accessibility Shortcut -->
   <java-symbol type="string" name="accessibility_shortcut_warning_dialog_title" />
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 7430c7a..453bddd 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -23,6 +23,7 @@
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
+import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
 import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -32,6 +33,7 @@
 
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.text.TextUtils;
 import android.view.View;
 import android.widget.RelativeLayout;
 
@@ -40,7 +42,10 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
+import com.android.internal.app.ResolverActivity.ActivityInfoPresentationGetter;
+import com.android.internal.app.ResolverActivity.ResolveInfoPresentationGetter;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+import com.android.internal.app.ResolverDataProvider.PackageManagerMockedInfo;
 import com.android.internal.widget.ResolverDrawerLayout;
 
 import org.junit.Before;
@@ -319,6 +324,50 @@
         assertThat(chosen[0], is(toChoose));
     }
 
+    @Test
+    public void getActivityLabelAndSubLabel() throws Exception {
+        ActivityInfoPresentationGetter pg;
+        PackageManagerMockedInfo info;
+
+        info = createPackageManagerMockedInfo(false);
+        pg = new ActivityInfoPresentationGetter(
+                info.ctx, 0, info.activityInfo);
+        assertThat("Label should match app label", pg.getLabel().equals(
+                info.setAppLabel));
+        assertThat("Sublabel should match activity label if set",
+                pg.getSubLabel().equals(info.setActivityLabel));
+
+        info = createPackageManagerMockedInfo(true);
+        pg = new ActivityInfoPresentationGetter(
+                info.ctx, 0, info.activityInfo);
+        assertThat("With override permission label should match activity label if set",
+                pg.getLabel().equals(info.setActivityLabel));
+        assertThat("With override permission sublabel should be empty",
+                TextUtils.isEmpty(pg.getSubLabel()));
+    }
+
+    @Test
+    public void getResolveInfoLabelAndSubLabel() throws Exception {
+        ResolveInfoPresentationGetter pg;
+        PackageManagerMockedInfo info;
+
+        info = createPackageManagerMockedInfo(false);
+        pg = new ResolveInfoPresentationGetter(
+                info.ctx, 0, info.resolveInfo);
+        assertThat("Label should match app label", pg.getLabel().equals(
+                info.setAppLabel));
+        assertThat("Sublabel should match resolve info label if set",
+                pg.getSubLabel().equals(info.setResolveInfoLabel));
+
+        info = createPackageManagerMockedInfo(true);
+        pg = new ResolveInfoPresentationGetter(
+                info.ctx, 0, info.resolveInfo);
+        assertThat("With override permission label should match resolve info label if set",
+                pg.getLabel().equals(info.setResolveInfoLabel));
+        assertThat("With override permission sublabel should be empty",
+                TextUtils.isEmpty(pg.getSubLabel()));
+    }
+
     private Intent createSendImageIntent() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
index 850b466..59634f6 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
@@ -17,11 +17,17 @@
 package com.android.internal.app;
 
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
 import android.os.UserHandle;
+import android.test.mock.MockContext;
+import android.test.mock.MockPackageManager;
+import android.test.mock.MockResources;
 
 /**
  * Utility class used by resolver tests to create mock data
@@ -71,6 +77,86 @@
         return ai;
     }
 
+    static class PackageManagerMockedInfo {
+        public Context ctx;
+        public ApplicationInfo appInfo;
+        public ActivityInfo activityInfo;
+        public ResolveInfo resolveInfo;
+        public String setAppLabel;
+        public String setActivityLabel;
+        public String setResolveInfoLabel;
+    }
+
+    static PackageManagerMockedInfo createPackageManagerMockedInfo(boolean hasOverridePermission) {
+        final String appLabel = "app_label";
+        final String activityLabel = "activity_label";
+        final String resolveInfoLabel = "resolve_info_label";
+
+        MockContext ctx = new MockContext() {
+            @Override
+            public PackageManager getPackageManager() {
+                return new MockPackageManager() {
+                    @Override
+                    public int checkPermission(String permName, String pkgName) {
+                        if (hasOverridePermission) return PERMISSION_GRANTED;
+                        return PERMISSION_DENIED;
+                    }
+                };
+            }
+
+            @Override
+            public Resources getResources() {
+                return new MockResources() {
+                    @Override
+                    public String getString(int id) throws NotFoundException {
+                        if (id == 1) return appLabel;
+                        if (id == 2) return activityLabel;
+                        if (id == 3) return resolveInfoLabel;
+                        return null;
+                    }
+                };
+            }
+        };
+
+        ApplicationInfo appInfo = new ApplicationInfo() {
+            @Override
+            public CharSequence loadLabel(PackageManager pm) {
+                return appLabel;
+            }
+        };
+        appInfo.labelRes = 1;
+
+        ActivityInfo activityInfo = new ActivityInfo() {
+            @Override
+            public CharSequence loadLabel(PackageManager pm) {
+                return activityLabel;
+            }
+        };
+        activityInfo.labelRes = 2;
+        activityInfo.applicationInfo = appInfo;
+
+        ResolveInfo resolveInfo = new ResolveInfo() {
+            @Override
+            public CharSequence loadLabel(PackageManager pm) {
+                return resolveInfoLabel;
+            }
+        };
+        resolveInfo.activityInfo = activityInfo;
+        resolveInfo.resolvePackageName = "super.fake.packagename";
+        resolveInfo.labelRes = 3;
+
+        PackageManagerMockedInfo mockedInfo = new PackageManagerMockedInfo();
+        mockedInfo.activityInfo = activityInfo;
+        mockedInfo.appInfo = appInfo;
+        mockedInfo.ctx = ctx;
+        mockedInfo.resolveInfo = resolveInfo;
+        mockedInfo.setAppLabel = appLabel;
+        mockedInfo.setActivityLabel = activityLabel;
+        mockedInfo.setResolveInfoLabel = resolveInfoLabel;
+
+        return mockedInfo;
+    }
+
     static Intent createResolverIntent(int i) {
         return new Intent("intentAction" + i);
     }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3c8794f..ed198e6 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -258,6 +258,7 @@
         <permission name="android.permission.ACTIVITY_EMBEDDING"/>
         <permission name="android.permission.FORCE_STOP_PACKAGES"/>
         <permission name="android.permission.GET_APP_OPS_STATS"/>
+        <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
         <permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
         <permission name="android.permission.INSTALL_PACKAGES"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 66a5477..7b7599f 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -29,6 +29,7 @@
 
 #include "androidfw/Asset.h"
 #include "androidfw/Idmap.h"
+#include "androidfw/misc.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/Util.h"
 
@@ -39,8 +40,10 @@
 
 static const std::string kResourcesArsc("resources.arsc");
 
-ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path)
-    : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path) {
+ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
+                     const std::string& path,
+                     time_t last_mod_time)
+    : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time) {
 }
 
 std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
@@ -116,8 +119,10 @@
     return {};
   }
 
+  time_t last_mod_time = getFileModDate(path.c_str());
+
   // Wrap the handle in a unique_ptr so it gets automatically closed.
-  std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path));
+  std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time));
 
   // Find the resource table.
   ::ZipString entry_name(kResourcesArsc.c_str());
@@ -248,4 +253,8 @@
   return result == -1;
 }
 
+bool ApkAssets::IsUpToDate() const {
+  return last_mod_time_ == getFileModDate(path_.c_str());
+}
+
 }  // namespace android
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 35bbb58..49fc82b 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -84,6 +84,8 @@
     return idmap_asset_.get() != nullptr;
   }
 
+  bool IsUpToDate() const;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ApkAssets);
 
@@ -95,12 +97,13 @@
   // Creates an Asset from any file on the file system.
   static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
 
-  ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path);
+  ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, time_t last_mod_time);
 
   using ZipArchivePtr = std::unique_ptr<ZipArchive, void(*)(ZipArchiveHandle)>;
 
   ZipArchivePtr zip_handle_;
   const std::string path_;
+  time_t last_mod_time_;
   std::unique_ptr<Asset> resources_asset_;
   std::unique_ptr<Asset> idmap_asset_;
   std::unique_ptr<const LoadedArsc> loaded_arsc_;
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index fb60a96..89a9b99 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -84,6 +84,7 @@
 }
 
 CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+    ATRACE_CALL();
     if (!mRenderThread.getGrContext()) {
         return CopyResult::UnknownError;
     }
@@ -104,6 +105,7 @@
 
 CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
                                    const Rect& srcRect, SkBitmap* bitmap) {
+    ATRACE_CALL();
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
         mRenderThread.requireGlContext();
     } else {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 1bcb819..16240b4 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -162,6 +162,7 @@
 }
 
 bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) {
+    ATRACE_NAME("TextureView#getBitmap");
     auto& thread = RenderThread::getInstance();
     return thread.queue().runSync([&]() -> bool {
         return thread.readback().copyLayerInto(layer, &bitmap) == CopyResult::Success;
@@ -347,6 +348,7 @@
 }
 
 int RenderProxy::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
+    ATRACE_NAME("HardwareBitmap readback");
     RenderThread& thread = RenderThread::getInstance();
     if (gettid() == thread.getTid()) {
         // TODO: fix everything that hits this. We should never be triggering a readback ourselves.
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 1708d3c..3fed6b0 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -222,7 +222,17 @@
     const SkISize maxSize = SkISize::Make(caps.maxImageExtent.width, caps.maxImageExtent.height);
     ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize);
 
-    windowInfo.bufferCount = std::max<uint32_t>(VulkanSurface::sMaxBufferCount, caps.minImageCount);
+    int query_value;
+    int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
+    if (err != 0 || query_value < 0) {
+        ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
+              query_value);
+        return nullptr;
+    }
+    auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
+
+    windowInfo.bufferCount = min_undequeued_buffers
+            + std::max(VulkanSurface::sTargetBufferCount, caps.minImageCount);
     if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) {
         // Application must settle for fewer images than desired:
         windowInfo.bufferCount = caps.maxImageCount;
@@ -357,10 +367,9 @@
         return false;
     }
 
-    // Lower layer insists that we have at least two buffers.
-    err = native_window_set_buffer_count(window, std::max(2, windowInfo.bufferCount));
+    err = native_window_set_buffer_count(window, windowInfo.bufferCount);
     if (err != 0) {
-        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%d) failed: %s (%d)",
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)",
               windowInfo.bufferCount, strerror(-err), err);
         return false;
     }
@@ -392,7 +401,7 @@
 }
 
 void VulkanSurface::releaseBuffers() {
-    for (uint32_t i = 0; i < VulkanSurface::sMaxBufferCount; i++) {
+    for (uint32_t i = 0; i < mWindowInfo.bufferCount; i++) {
         VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i];
 
         if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index 418d40f..305483f 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -83,14 +83,19 @@
      * as private to this class.
      *
      */
-    static constexpr int sMaxBufferCount = 3;
+
+    // How many buffers we want to be able to use ourselves. We want 1 in active rendering with
+    // 1 more queued, so 2. This will be added to NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, which is
+    // how many buffers the consumer needs (eg, 1 for SurfaceFlinger), getting to a typically
+    // triple-buffered queue as a result.
+    static constexpr uint32_t sTargetBufferCount = 2;
 
     struct WindowInfo {
         SkISize size;
         PixelFormat pixelFormat;
         android_dataspace dataspace;
         int transform;
-        int bufferCount;
+        size_t bufferCount;
         uint64_t windowUsageFlags;
 
         // size of the ANativeWindow if the inverse of transform requires us to swap width/height
@@ -111,7 +116,8 @@
                                               const SkISize& maxSize);
     void releaseBuffers();
 
-    NativeBufferInfo mNativeBuffers[VulkanSurface::sMaxBufferCount];
+    // TODO: Just use a vector?
+    NativeBufferInfo mNativeBuffers[android::BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     sp<ANativeWindow> mNativeWindow;
     WindowInfo mWindowInfo;
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index 44e0a65..a528636 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -18,7 +18,7 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.captiveportallogin"
-    android:versionCode="11"
+    android:versionCode="200000000"
     android:versionName="Q-initial">
 
     <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index 395eac1..2fe9d21 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -18,8 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:fitsSystemWindows="true"
-    android:visibility="gone">
+    android:fitsSystemWindows="true">
 
     <LinearLayout
         android:id="@+id/container"
diff --git a/packages/CarSystemUI/res/layout/notification_center_activity.xml b/packages/CarSystemUI/res/layout/notification_center_activity.xml
index 383aba4..5c915b8 100644
--- a/packages/CarSystemUI/res/layout/notification_center_activity.xml
+++ b/packages/CarSystemUI/res/layout/notification_center_activity.xml
@@ -19,7 +19,8 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/notification_view"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:background="@color/notification_shade_background_color">
 
     <View
          android:id="@+id/glass_pane"
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index 0594dce..9a074dd 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -64,7 +64,6 @@
         <include layout="@layout/car_top_navigation_bar"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_weight="1"
         />
     </LinearLayout>
 
@@ -80,6 +79,13 @@
         android:layout_height="match_parent"
         android:visibility="invisible"/>
 
+    <include layout="@layout/notification_center_activity"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:layout_marginBottom="112dp"
+             android:visibility="invisible"
+    />
+
     <com.android.systemui.statusbar.ScrimView
         android:id="@+id/scrim_in_front"
         android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index c9f9dea..d946fbc 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -21,6 +21,7 @@
     <string name="config_systemUIFactoryComponent" translatable="false">
         com.android.systemui.CarSystemUIFactory
     </string>
+
     <bool name="config_enableFullscreenUserSwitcher">true</bool>
 
     <!-- configure which system ui bars should be displayed -->
@@ -28,30 +29,12 @@
     <bool name="config_enableRightNavigationBar">false</bool>
     <bool name="config_enableBottomNavigationBar">true</bool>
 
-    <!-- SystemUI Services: The classes of the stuff to start. This is duplicated from core
-         SystemUi b/c it can't be overlayed at this level for now
-    -->
-    <string-array name="config_systemUIServiceComponents" translatable="false">
-        <item>com.android.systemui.Dependency$DependencyCreator</item>
-        <item>com.android.systemui.util.NotificationChannels</item>
-        <item>com.android.systemui.notifications.NotificationsUI</item>
-        <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
-        <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
-        <item>com.android.systemui.recents.Recents</item>
-        <item>com.android.systemui.volume.VolumeUI</item>
-        <item>com.android.systemui.stackdivider.Divider</item>
-        <item>com.android.systemui.SystemBars</item>
-        <item>com.android.systemui.usb.StorageNotification</item>
-        <item>com.android.systemui.power.PowerUI</item>
-        <item>com.android.systemui.media.RingtonePlayer</item>
-        <item>com.android.systemui.keyboard.KeyboardUI</item>
-        <item>com.android.systemui.pip.PipUI</item>
-        <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
-        <item>@string/config_systemUIVendorServiceComponent</item>
-        <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
-        <item>com.android.systemui.LatencyTester</item>
-        <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
-        <item>com.android.systemui.ScreenDecorations</item>
-        <item>com.android.systemui.SliceBroadcastRelayHandler</item>
-    </string-array>
+    <bool name="config_hideNavWhenKeyguardBouncerShown">true</bool>
+    <bool name="config_enablePersistentDockedActivity">false</bool>
+    <string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
+
+    <!-- How many icons may be shown at once in the system bar. Includes any
+         slots that may be reused for things like IME control. -->
+    <integer name="config_maxNotificationIcons">0</integer>
+
 </resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
deleted file mode 100644
index 2e2f3b7..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.notifications;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.drivingstate.CarUxRestrictionsManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.ServiceConnection;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
-import android.os.IBinder;
-import android.os.ServiceManager;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.car.notification.CarNotificationListener;
-import com.android.car.notification.CarNotificationView;
-import com.android.car.notification.CarUxRestrictionManagerWrapper;
-import com.android.car.notification.NotificationClickHandlerFactory;
-import com.android.car.notification.NotificationViewController;
-import com.android.car.notification.PreprocessingManager;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.SystemUI;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.car.CarStatusBar;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
-/**
- * Standalone SystemUI for displaying Notifications that have been designed to be used in the car
- */
-public class NotificationsUI extends SystemUI
-        implements ConfigurationController.ConfigurationListener {
-
-    private static final String TAG = "NotificationsUI";
-    // used to calculate how fast to open or close the window
-    private static final float DEFAULT_FLING_VELOCITY = 0;
-    // max time a fling animation takes
-    private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
-    // acceleration rate for the fling animation
-    private static final float FLING_SPEED_UP_FACTOR = 0.6f;
-    private CarNotificationListener mCarNotificationListener;
-    private CarUxRestrictionsManager mCarUxRestrictionsManager;
-    private NotificationClickHandlerFactory mClickHandlerFactory;
-    private Car mCar;
-    private ViewGroup mCarNotificationWindow;
-    private NotificationViewController mNotificationViewController;
-    private boolean mIsShowing;
-    private boolean mIsTracking;
-    private boolean mNotificationListAtBottom;
-    private boolean mNotificationListAtBottomAtTimeOfTouch;
-    private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper =
-            new CarUxRestrictionManagerWrapper();
-    // Used in the Notification panel touch listener
-    private GestureDetector mGestureDetector;
-    // Used in scrollable content of the notifications
-    private GestureDetector mScrollUpDetector;
-    private View mContent;
-    private View.OnTouchListener mOnTouchListener;
-    private FlingAnimationUtils mFlingAnimationUtils;
-    private static int sSettleOpenPercentage;
-    private static int sSettleClosePercentage;
-    private CarStatusBar mCarStatusBar;
-
-    /**
-     * Inits the window that hosts the notifications and establishes the connections
-     * to the car related services.
-     */
-    @Override
-    public void start() {
-        sSettleOpenPercentage = mContext.getResources().getInteger(
-                R.integer.notification_settle_open_percentage);
-        sSettleClosePercentage = mContext.getResources().getInteger(
-                R.integer.notification_settle_close_percentage);
-        WindowManager windowManager =
-                (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        mFlingAnimationUtils = new FlingAnimationUtils(mContext,
-                FLING_ANIMATION_MAX_TIME, FLING_SPEED_UP_FACTOR);
-        mCarNotificationListener = new CarNotificationListener();
-        // create a notification click handler that closes the notification ui if the an activity
-        // is launched successfully
-        mClickHandlerFactory = new NotificationClickHandlerFactory(
-                IStatusBarService.Stub.asInterface(
-                        ServiceManager.getService(Context.STATUS_BAR_SERVICE)),
-                launchResult -> {
-                    if (launchResult == ActivityManager.START_TASK_TO_FRONT
-                            || launchResult == ActivityManager.START_SUCCESS) {
-                        closeCarNotifications(DEFAULT_FLING_VELOCITY);
-                    }
-                });
-        mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper,
-                mClickHandlerFactory);
-        mCar = Car.createCar(mContext, mCarConnectionListener);
-        mCar.connect();
-        NotificationGestureListener gestureListener = new NotificationGestureListener();
-        mGestureDetector = new GestureDetector(mContext, gestureListener);
-        mScrollUpDetector = new GestureDetector(mContext, new ScrollUpDetector());
-        mOnTouchListener = new NotificationPanelTouchListener();
-        mCarNotificationWindow = (ViewGroup) View.inflate(new ContextThemeWrapper(mContext,
-                        R.style.Theme_Notification),
-                R.layout.navigation_bar_window, null);
-        mCarNotificationWindow
-                .setBackgroundColor(mContext.getColor(R.color.notification_shade_background_color));
-        inflateNotificationContent();
-
-        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.TRANSLUCENT);
-        layoutParams.setTitle("Car Notification Window");
-        // start in the hidden state
-        mCarNotificationWindow.setVisibility(View.GONE);
-        windowManager.addView(mCarNotificationWindow, layoutParams);
-
-        // Add this object to the SystemUI component registry such that the status bar
-        // can get a reference to it.
-        putComponent(NotificationsUI.class, this);
-        Dependency.get(ConfigurationController.class).addCallback(this);
-    }
-
-    @SuppressLint("ClickableViewAccessibility")
-    private void inflateNotificationContent() {
-        if (mNotificationViewController != null) {
-            mNotificationViewController.disable();
-        }
-        mCarNotificationWindow.removeAllViews();
-
-        mContent = View.inflate(new ContextThemeWrapper(mContext,
-                        com.android.car.notification.R.style.Theme_Notification),
-                R.layout.notification_center_activity,
-                mCarNotificationWindow);
-        // set the click handler such that we can dismiss the UI when a notification is clicked
-        CarNotificationView noteView = mCarNotificationWindow.findViewById(R.id.notification_view);
-        noteView.setClickHandlerFactory(mClickHandlerFactory);
-
-        mContent.setOnTouchListener(mOnTouchListener);
-        // set initial translation after size is calculated
-        mContent.getViewTreeObserver().addOnGlobalLayoutListener(
-                new ViewTreeObserver.OnGlobalLayoutListener() {
-                    @Override
-                    public void onGlobalLayout() {
-                        mContent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                        if (!mIsShowing && !mIsTracking) {
-                            mContent.setTranslationY(mContent.getHeight() * -1);
-                        }
-                    }
-                });
-
-        RecyclerView notificationList = mCarNotificationWindow.findViewById(R.id.notifications);
-        // register a scroll listener so we can figure out if we are at the bottom of the
-        // list of notifications
-        notificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
-            @Override
-            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
-                super.onScrolled(recyclerView, dx, dy);
-                if (!notificationList.canScrollVertically(1)) {
-                    mNotificationListAtBottom = true;
-                    return;
-                }
-                mNotificationListAtBottom = false;
-                mNotificationListAtBottomAtTimeOfTouch = false;
-            }
-        });
-        // add a touch listener such that when the user scrolls up and they are at the bottom
-        // of the list we can start the closing of the view.
-        notificationList.setOnTouchListener(new NotificationListTouchListener());
-
-        // There's a view installed at a higher z-order such that we can intercept the ACTION_DOWN
-        // to set the initial click state.
-        mCarNotificationWindow.findViewById(R.id.glass_pane).setOnTouchListener((v, event) -> {
-            if (event.getActionMasked() == MotionEvent.ACTION_UP) {
-                mNotificationListAtBottomAtTimeOfTouch = false;
-            }
-            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-                mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
-                // register the down event with the gesture detectors so then know where the down
-                // started. This is needed because at this point we don't know which listener
-                // is going to handle scroll and fling events.
-                mGestureDetector.onTouchEvent(event);
-                mScrollUpDetector.onTouchEvent(event);
-            }
-            return false;
-        });
-
-        mNotificationViewController = new NotificationViewController(
-                mCarNotificationWindow
-                        .findViewById(com.android.car.notification.R.id.notification_view),
-                PreprocessingManager.getInstance(mContext),
-                mCarNotificationListener,
-                mCarUxRestrictionManagerWrapper);
-        mNotificationViewController.enable();
-    }
-
-    // allows for day night switch
-    @Override
-    public void onConfigChanged(Configuration newConfig) {
-        inflateNotificationContent();
-    }
-
-    public void setStatusBar(CarStatusBar carStatusBar) {
-        mCarStatusBar = carStatusBar;
-    }
-
-    public View.OnTouchListener getDragDownListener() {
-        return mOnTouchListener;
-    }
-
-    /**
-     * This listener is attached to the notification list UI to intercept gestures if the user
-     * is scrolling up when the notification list is at the bottom
-     */
-    private class ScrollUpDetector extends GestureDetector.SimpleOnGestureListener {
-
-        @Override
-        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
-            return distanceY > 0;
-        }
-
-        @Override
-        public boolean onSingleTapUp(MotionEvent motionEvent) {
-            closeCarNotifications(DEFAULT_FLING_VELOCITY);
-            return false;
-        }
-    }
-
-    private class NotificationListTouchListener implements View.OnTouchListener {
-
-        @Override
-        public boolean onTouch(View v, MotionEvent event) {
-            // reset mNotificationListAtBottomAtTimeOfTouch here since the "glass pane" will not
-            // get the up event
-            if (event.getActionMasked() == MotionEvent.ACTION_UP) {
-                mNotificationListAtBottomAtTimeOfTouch = false;
-            }
-            boolean wasScrolledUp = mScrollUpDetector.onTouchEvent(event);
-
-            if (mIsTracking
-                    || (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom
-                    && wasScrolledUp)) {
-                mOnTouchListener.onTouch(v, event);
-                // touch event should not be propagated further
-                return true;
-            }
-            return false;
-        }
-    }
-
-    /**
-     * Touch listener installed on the notification panel. It is also used by the Nav and StatusBar
-     */
-    private class NotificationPanelTouchListener implements View.OnTouchListener {
-
-        @Override
-        public boolean onTouch(View v, MotionEvent event) {
-            boolean consumed = mGestureDetector.onTouchEvent(event);
-            if (consumed) {
-                return true;
-            }
-            if (!mIsTracking || event.getActionMasked() != MotionEvent.ACTION_UP) {
-                return false;
-            }
-
-            float percentFromBottom =
-                    Math.abs(mContent.getTranslationY() / mContent.getHeight()) * 100;
-            if (mIsShowing) {
-                if (percentFromBottom < sSettleOpenPercentage) {
-                    // panel started to close but did not cross minimum threshold thus we open
-                    // it back up
-                    openCarNotifications(DEFAULT_FLING_VELOCITY);
-                    return true;
-                }
-                // panel was lifted more than the threshold thus we close it the rest of the way
-                closeCarNotifications(DEFAULT_FLING_VELOCITY);
-                return true;
-            }
-
-            if (percentFromBottom > sSettleClosePercentage) {
-                // panel was only peeked at thus close it back up
-                closeCarNotifications(DEFAULT_FLING_VELOCITY);
-                return true;
-            }
-            // panel has been open more than threshold thus open it the rest of the way
-            openCarNotifications(DEFAULT_FLING_VELOCITY);
-            return true;
-
-        }
-    }
-
-    /**
-     * Listener called by mGestureDetector. This will be initiated from the
-     * NotificationPanelTouchListener
-     */
-    private class NotificationGestureListener extends GestureDetector.SimpleOnGestureListener {
-        private static final int SWIPE_UP_MIN_DISTANCE = 75;
-        private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
-        private static final int SWIPE_MAX_OFF_PATH = 75;
-        private static final int SWIPE_THRESHOLD_VELOCITY = 200;
-
-        @Override
-        public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
-                float distanceY) {
-            if (mCarStatusBar == null || !mCarStatusBar.getIsUserSetup()) {
-                return true;
-            }
-            boolean isDown = event1.getY() - event2.getY() < 0;
-            // CarStatusBar and NavigationBar are identical so avoid the touch if it
-            // starts from NavigationBar to open.
-            if (event1.getRawY() > mCarNotificationWindow.getHeight() && isDown
-                    && mCarNotificationWindow.getVisibility() == View.GONE) {
-                mIsTracking = false;
-                return true;
-            }
-            mIsTracking = true;
-            mCarNotificationWindow.setVisibility(View.VISIBLE);
-
-            mContent.setTranslationY(Math.min(mContent.getTranslationY() - distanceY, 0));
-            if (mContent.getTranslationY() == 0) {
-                mIsTracking = false;
-            }
-            return true;
-        }
-
-        @Override
-        public boolean onFling(MotionEvent event1, MotionEvent event2,
-                float velocityX, float velocityY) {
-            if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
-                    || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
-                // swipe was not vertical or was not fast enough
-                return false;
-            }
-
-            boolean isUp = velocityY < 0;
-            float distanceDelta = Math.abs(event1.getY() - event2.getY());
-            if (isUp && distanceDelta > SWIPE_UP_MIN_DISTANCE) {
-                // fling up
-                mIsTracking = false;
-                closeCarNotifications(Math.abs(velocityY));
-                return true;
-
-            } else if (!isUp && distanceDelta > SWIPE_DOWN_MIN_DISTANCE
-                    && (event1.getRawY() < mCarNotificationWindow.getHeight()
-                    || mCarNotificationWindow.getVisibility() == View.VISIBLE)) {
-                // fling down
-                mIsTracking = false;
-                openCarNotifications(velocityY);
-                return true;
-            }
-
-            return false;
-        }
-    }
-
-    /**
-     * Connection callback to establish UX Restrictions
-     */
-    private ServiceConnection mCarConnectionListener = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            try {
-                mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(
-                        Car.CAR_UX_RESTRICTION_SERVICE);
-                mCarUxRestrictionManagerWrapper
-                        .setCarUxRestrictionsManager(mCarUxRestrictionsManager);
-                PreprocessingManager preprocessingManager = PreprocessingManager.getInstance(
-                        mContext);
-                preprocessingManager
-                        .setCarUxRestrictionManagerWrapper(mCarUxRestrictionManagerWrapper);
-            } catch (CarNotConnectedException e) {
-                Log.e(TAG, "Car not connected in CarConnectionListener", e);
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            Log.e(TAG, "Car service disconnected unexpectedly");
-        }
-    };
-
-    /**
-     * Toggles the visibility of the notifications
-     */
-    public void toggleShowingCarNotifications() {
-        if (mCarNotificationWindow.getVisibility() == View.VISIBLE) {
-            closeCarNotifications(DEFAULT_FLING_VELOCITY);
-            return;
-        }
-        openCarNotifications(DEFAULT_FLING_VELOCITY);
-    }
-
-    /**
-     * Hides the notifications
-     */
-    public void closeCarNotifications(float velocityY) {
-        float closedTranslation = mContent.getHeight() * -1;
-        ValueAnimator animator =
-                ValueAnimator.ofFloat(mContent.getTranslationY(), closedTranslation);
-        animator.addUpdateListener(
-                animation -> mContent.setTranslationY((Float) animation.getAnimatedValue()));
-        mFlingAnimationUtils.apply(
-                animator, mContent.getTranslationY(), closedTranslation, velocityY);
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mCarNotificationWindow.setVisibility(View.GONE);
-            }
-        });
-        animator.start();
-        mNotificationViewController.disable();
-        mIsShowing = false;
-        mIsTracking = false;
-        RecyclerView notificationListView = mCarNotificationWindow.findViewById(R.id.notifications);
-        notificationListView.scrollToPosition(0);
-    }
-
-    /**
-     * Sets the notifications to visible
-     */
-    public void openCarNotifications(float velocityY) {
-        if (mCarStatusBar == null || !mCarStatusBar.getIsUserSetup()) {
-            return;
-        }
-        mCarNotificationWindow.setVisibility(View.VISIBLE);
-
-        ValueAnimator animator = ValueAnimator.ofFloat(mContent.getTranslationY(), 0);
-        animator.addUpdateListener(
-                animation -> mContent.setTranslationY((Float) animation.getAnimatedValue()));
-        mFlingAnimationUtils.apply(animator, mContent.getTranslationY(), 0, velocityY);
-        animator.start();
-
-        mNotificationViewController.enable();
-        mIsShowing = true;
-        mIsTracking = false;
-    }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index afefa1b..a0f2367 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -38,6 +38,7 @@
     private CarStatusBar mCarStatusBar;
     private Context mContext;
     private View mLockScreenButtons;
+    // used to wire in open/close gestures for notifications
     private OnTouchListener mStatusBarWindowTouchListener;
 
 
@@ -65,26 +66,45 @@
             mDarkIconManager.setShouldLog(true);
             Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
         }
+        // needs to be clickable so that it will receive ACTION_MOVE events
+        setClickable(true);
     }
 
+    // Used to forward touch events even if the touch was initiated from a child component
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mStatusBarWindowTouchListener == null) {
-            return false;
+        if (mStatusBarWindowTouchListener != null) {
+            // forward touch events to the status bar window so it can add a drag down
+            // windows if required (Notification shade)
+            mStatusBarWindowTouchListener.onTouch(this, ev);
         }
-        // forward touch events to the status bar window so it can add a drag down
-        // windows if required (Notification shade)
-        mStatusBarWindowTouchListener.onTouch(this, ev);
-        return false;
+        return super.onInterceptTouchEvent(ev);
     }
 
+
     void setStatusBar(CarStatusBar carStatusBar) {
         mCarStatusBar = carStatusBar;
-        mStatusBarWindowTouchListener = carStatusBar.getStatusBarWindowTouchListener();
+    }
+
+    /**
+     * Set a touch listener that will be called from onInterceptTouchEvent and onTouchEvent
+     *
+     * @param statusBarWindowTouchListener The listener to call from touch and intercept touch
+     */
+    void setStatusBarWindowTouchListener(OnTouchListener statusBarWindowTouchListener) {
+        mStatusBarWindowTouchListener = statusBarWindowTouchListener;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (mStatusBarWindowTouchListener != null) {
+            mStatusBarWindowTouchListener.onTouch(this, event);
+        }
+        return super.onTouchEvent(event);
     }
 
     protected void onNotificationsClick(View v) {
-        mCarStatusBar.toggleCarNotifications();
+        mCarStatusBar.togglePanel();
     }
 
     /**
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 abe9be8..9bcc1ab 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -16,17 +16,29 @@
 
 package com.android.systemui.statusbar.car;
 
+import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.car.drivingstate.CarDrivingStateEvent;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.util.Log;
+import android.view.GestureDetector;
 import android.view.Gravity;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.car.notification.CarNotificationListener;
+import com.android.car.notification.CarNotificationView;
+import com.android.car.notification.CarUxRestrictionManagerWrapper;
+import com.android.car.notification.NotificationClickHandlerFactory;
+import com.android.car.notification.NotificationViewController;
+import com.android.car.notification.PreprocessingManager;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.CarSystemUIFactory;
@@ -37,7 +49,6 @@
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.notifications.NotificationsUI;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.car.CarQSFragment;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -70,7 +81,6 @@
     private BatteryMeterView mBatteryMeterView;
     private Drawable mNotificationPanelBackground;
 
-    private ConnectedDeviceSignalController mConnectedDeviceSignalController;
     private ViewGroup mNavigationBarWindow;
     private ViewGroup mLeftNavigationBarWindow;
     private ViewGroup mRightNavigationBarWindow;
@@ -90,6 +100,17 @@
     private DrivingStateHelper mDrivingStateHelper;
     private SwitchToGuestTimer mSwitchToGuestTimer;
 
+    // The container for the notifications.
+    private CarNotificationView mNotificationView;
+    private RecyclerView mNotificationList;
+    // The state of if the notification list is currently showing the bottom.
+    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 navigation bars such that they can close the notification panel if
+    // it's open.
+    private View.OnTouchListener mNavBarNotificationTouchListener;
+
     @Override
     public void start() {
         // get the provisioned state before calling the parent class since it's that flow that
@@ -223,7 +244,6 @@
      * Switch to the keyguard applicable content contained in the nav bars
      */
     private void updateNavBarForKeyguardContent() {
-        getComponent(NotificationsUI.class).closeCarNotifications(0);
         if (mNavigationBarView != null) {
             mNavigationBarView.showKeyguardButtons();
         }
@@ -255,17 +275,150 @@
             // when a device has connected by bluetooth.
             mBatteryMeterView.setVisibility(View.GONE);
         });
-        addTemperatureViewToController(mStatusBarWindow);
+
+        connectNotificationsUI();
+    }
+
+    /**
+     * Attach the notification listeners and controllers to the UI as well as build all the
+     * 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.
+        GestureDetector openGestureDetector = new GestureDetector(mContext,
+                new OpenNotificationGestureListener() {
+                    @Override
+                    protected void openNotification() {
+                        animateExpandNotificationsPanel();
+                    }
+                });
+        // Attached to the notification ui to detect close request of the notification shade.
+        GestureDetector closeGestureDetector = new GestureDetector(mContext,
+                new CloseNotificationGestureListener() {
+                    @Override
+                    protected void close() {
+                        animateCollapsePanels();
+                    }
+                });
+        // Attached to the NavBars to close the notification shade
+        GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext,
+                new NavBarCloseNotificationGestureListener() {
+                    @Override
+                    protected void close() {
+                        animateCollapsePanels();
+                    }
+                });
+        mNavBarNotificationTouchListener =
+                (v, event) -> navBarCloseNotificationGestureDetector.onTouchEvent(event);
+
         // 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);
-        CarNavigationBarView qsTopBar = mStatusBarWindow.findViewById(R.id.qs_car_top_bar);
-        qsTopBar.setStatusBar(this);
-        getComponent(NotificationsUI.class).setStatusBar(this);
+        topBar.setStatusBarWindowTouchListener((v1, event1) ->
+                openGestureDetector.onTouchEvent(event1));
+
+        NotificationClickHandlerFactory clickHandlerFactory = new NotificationClickHandlerFactory(
+                mBarService,
+                launchResult -> {
+                    if (launchResult == ActivityManager.START_TASK_TO_FRONT
+                            || launchResult == ActivityManager.START_SUCCESS) {
+                        animateCollapsePanels();
+                    }
+                });
+        CarNotificationListener carNotificationListener = new CarNotificationListener();
+        CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
+                new CarUxRestrictionManagerWrapper();
+        carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
+                clickHandlerFactory);
+
+        mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
+        View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane);
+        mNotificationView.setClickHandlerFactory(clickHandlerFactory);
+
+        // The glass pane is used to view touch events before passed to the notification list.
+        // This allows us to initialize gesture listeners and detect when to close the notifications
+        glassPane.setOnTouchListener((v, event) -> {
+            if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+                mNotificationListAtBottomAtTimeOfTouch = false;
+            }
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
+                // Pass the down event to gesture detector so that it knows where the touch event
+                // started.
+                closeGestureDetector.onTouchEvent(event);
+            }
+            return false;
+        });
+        mNotificationList = mNotificationView.findViewById(R.id.notifications);
+        mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
+            @Override
+            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+                super.onScrolled(recyclerView, dx, dy);
+                if (!mNotificationList.canScrollVertically(1)) {
+                    mNotificationListAtBottom = true;
+                    return;
+                }
+                mNotificationListAtBottom = false;
+                mNotificationListAtBottomAtTimeOfTouch = false;
+            }
+        });
+        mNotificationList.setOnTouchListener(new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                boolean handled = false;
+                if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
+                    handled = closeGestureDetector.onTouchEvent(event);
+                }
+                // Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
+                // the event has been passed to the closeGestureDetector above, such that the
+                // closeGestureDetector sees the up event before the state has changed.
+                if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+                    mNotificationListAtBottomAtTimeOfTouch = false;
+                }
+                return handled;
+            }
+        });
+
+        NotificationViewController mNotificationViewController = new NotificationViewController(
+                mNotificationView,
+                PreprocessingManager.getInstance(mContext),
+                carNotificationListener,
+                carUxRestrictionManagerWrapper);
+        mNotificationViewController.enable();
     }
 
     @Override
+    public void animateExpandNotificationsPanel() {
+        if (!mCommandQueue.panelsEnabled() || !mUserSetup) {
+            return;
+        }
+        // scroll to top
+        mNotificationList.scrollToPosition(0);
+        mStatusBarWindowController.setPanelVisible(true);
+        mNotificationView.setVisibility(View.VISIBLE);
+        // let the status bar know that the panel is open
+        setPanelExpanded(true);
+    }
+
+    @Override
+    public void animateCollapsePanels(int flags, boolean force, boolean delayed,
+            float speedUpFactor) {
+        super.animateCollapsePanels(flags, force, delayed, speedUpFactor);
+        if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) {
+            return;
+        }
+        mStatusBarWindowController.setStatusBarFocusable(false);
+        mStatusBarWindow.cancelExpandHelper();
+        mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
+        mStatusBarWindowController.setPanelVisible(false);
+        mNotificationView.setVisibility(View.INVISIBLE);
+        // let the status bar know that the panel is cloased
+        setPanelExpanded(false);
+    }
+
+
+    @Override
     protected QS createDefaultQSFragment() {
         return new CarQSFragment();
     }
@@ -338,8 +491,8 @@
             lp.setTitle("CarNavigationBar");
             lp.windowAnimations = 0;
             mWindowManager.addView(mNavigationBarWindow, lp);
-            mNavigationBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
         }
+
         if (mShowLeft) {
             int width = mContext.getResources().getDimensionPixelSize(
                     R.dimen.car_left_navigation_bar_width);
@@ -389,6 +542,7 @@
         }
         mNavigationBarView.setStatusBar(this);
         addTemperatureViewToController(mNavigationBarView);
+        mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
     }
 
     private void buildLeft(int layout) {
@@ -400,6 +554,7 @@
         }
         mLeftNavigationBarView.setStatusBar(this);
         addTemperatureViewToController(mLeftNavigationBarView);
+        mLeftNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
     }
 
 
@@ -412,6 +567,7 @@
         }
         mRightNavigationBarView.setStatusBar(this);
         addTemperatureViewToController(mRightNavigationBarView);
+        mRightNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
     }
 
     @Override
@@ -435,8 +591,6 @@
         pw.println(mCarBatteryController);
         pw.print("  mBatteryMeterView=");
         pw.println(mBatteryMeterView);
-        pw.print("  mConnectedDeviceSignalController=");
-        pw.println(mConnectedDeviceSignalController);
         pw.print("  mNavigationBarView=");
         pw.println(mNavigationBarView);
 
@@ -456,11 +610,6 @@
         }
     }
 
-    @Override
-    protected View.OnTouchListener getStatusBarWindowTouchListener() {
-        // Gets the car specific notification touch listener
-        return getComponent(NotificationsUI.class).getDragDownListener();
-    }
 
     @Override
     public void showBatteryView() {
@@ -585,15 +734,6 @@
                 true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
     }
 
-    @Override
-    public void animateExpandNotificationsPanel() {
-        // Because space is usually constrained in the auto use-case, there should not be a
-        // pinned notification when the shade has been expanded. Ensure this by removing all heads-
-        // up notifications.
-        mHeadsUpManager.releaseAllImmediately();
-        super.animateExpandNotificationsPanel();
-    }
-
     /**
      * Ensures that relevant child views are appropriately recreated when the device's density
      * changes.
@@ -620,12 +760,73 @@
         return mUserSetup;
     }
 
-    public void toggleCarNotifications() {
-        getComponent(NotificationsUI.class).toggleShowingCarNotifications();
+
+    // TODO: add settle down/up logic
+    private static final int SWIPE_UP_MIN_DISTANCE = 75;
+    private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
+    private static final int SWIPE_MAX_OFF_PATH = 75;
+    private static final int SWIPE_THRESHOLD_VELOCITY = 200;
+    // Only responsible for open hooks. Since once the panel opens it covers all elements
+    // there is no need to merge with close.
+    private abstract class OpenNotificationGestureListener extends
+            GestureDetector.SimpleOnGestureListener {
+
+        @Override
+        public boolean onFling(MotionEvent event1, MotionEvent event2,
+                float velocityX, float velocityY) {
+            if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
+                    || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
+                // swipe was not vertical or was not fast enough
+                return false;
+            }
+            boolean isDown = velocityY > 0;
+            float distanceDelta = Math.abs(event1.getY() - event2.getY());
+            if (isDown && distanceDelta > SWIPE_DOWN_MIN_DISTANCE) {
+                openNotification();
+                return true;
+            }
+
+            return false;
+        }
+        protected abstract void openNotification();
     }
 
-    @Override
-    public void maybeEscalateHeadsUp() {
-        // Never send full screen intent in car.
+    // to be installed on the open panel notification panel
+    private abstract class CloseNotificationGestureListener extends
+            GestureDetector.SimpleOnGestureListener {
+
+        @Override
+        public boolean onFling(MotionEvent event1, MotionEvent event2,
+                float velocityX, float velocityY) {
+            if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
+                    || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
+                // swipe was not vertical or was not fast enough
+                return false;
+            }
+            boolean isUp = velocityY < 0;
+            float distanceDelta = Math.abs(event1.getY() - event2.getY());
+            if (isUp && distanceDelta > SWIPE_UP_MIN_DISTANCE) {
+                close();
+                return true;
+            }
+            return false;
+        }
+        protected abstract void close();
+    }
+
+    // to be installed on the nav bars
+    private abstract class NavBarCloseNotificationGestureListener extends
+            CloseNotificationGestureListener {
+        @Override
+        public boolean onSingleTapUp(MotionEvent e) {
+            close();
+            return super.onSingleTapUp(e);
+        }
+
+        @Override
+        public void onLongPress(MotionEvent e) {
+            close();
+            super.onLongPress(e);
+        }
     }
 }
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 f896cf1..0a167d9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -38,7 +38,9 @@
     public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
         mStatusBar = statusBar;
         mParent = containerStub.inflate();
-        mParent.setVisibility(View.VISIBLE);
+        // Hide the user grid by default. It will only be made visible by clicking on a cancel
+        // button in a bouncer.
+        hide();
         View container = mParent.findViewById(R.id.container);
 
         // Initialize user grid.
@@ -49,10 +51,6 @@
         mUserGridView.buildAdapter();
         mUserGridView.setUserSelectionListener(this::onUserSelected);
 
-        // Hide the user grid by default. It will only be made visible by clicking on a cancel
-        // button in a bouncer.
-        hide();
-
         mShortAnimDuration = container.getResources()
                 .getInteger(android.R.integer.config_shortAnimTime);
     }
@@ -61,21 +59,21 @@
      * Makes user grid visible.
      */
     public void show() {
-        mUserGridView.setVisibility(View.VISIBLE);
+        mParent.setVisibility(View.VISIBLE);
     }
 
     /**
      * Hides the user grid.
      */
     public void hide() {
-        mUserGridView.setVisibility(View.INVISIBLE);
+        mParent.setVisibility(View.INVISIBLE);
     }
 
     /**
      * @return {@code true} if user grid is visible, {@code false} otherwise.
      */
     public boolean isVisible() {
-        return mUserGridView.getVisibility() == View.VISIBLE;
+        return mParent.getVisibility() == View.VISIBLE;
     }
 
     /**
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index d409758..45a59a3 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -17,7 +17,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     package="android.ext.services"
-    android:versionCode="1"
+    android:versionCode="200000000"
     android:versionName="1"
     coreApp="true">
 
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 9b60dc3..8d8e222 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -17,8 +17,12 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.networkstack"
-          android:sharedUserId="android.uid.networkstack">
+  package="com.android.networkstack"
+  android:sharedUserId="android.uid.networkstack"
+  android:versionCode="200000000"
+  android:versionName="29 system image"
+>
+
     <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
 
     <uses-permission android:name="android.permission.INTERNET" />
diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
new file mode 100644
index 0000000..7f4442d
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+    name: "SettingsLibAdaptiveIcon",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+          "androidx.annotation_annotation",
+          "SettingsLibTile"
+    ],
+
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml b/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
new file mode 100644
index 0000000..256b8f3
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml b/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml
new file mode 100644
index 0000000..76d106a
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<resources>
+    <color name="homepage_generic_icon_background">#1A73E8</color>
+
+    <color name="bt_outline_color">#1f000000</color> <!-- icon outline color -->
+</resources>
diff --git a/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml b/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml
new file mode 100644
index 0000000..7f5b58c
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+
+<resources>
+    <!-- Dashboard foreground image inset (from background edge to foreground edge) -->
+    <dimen name="dashboard_tile_foreground_image_inset">6dp</dimen>
+
+    <!-- Stroke size of adaptive outline -->
+    <dimen name="adaptive_outline_stroke">1dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
new file mode 100644
index 0000000..fc93650
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static androidx.annotation.VisibleForTesting.NONE;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settingslib.drawer.Tile;
+
+/**
+ * Adaptive icon that can set background color
+ */
+public class AdaptiveIcon extends LayerDrawable {
+
+    private static final String TAG = "AdaptiveHomepageIcon";
+
+    @VisibleForTesting(otherwise = NONE)
+    int mBackgroundColor = -1;
+    private AdaptiveConstantState mAdaptiveConstantState;
+
+    public AdaptiveIcon(Context context, Drawable foreground) {
+        super(new Drawable[]{
+                new AdaptiveIconShapeDrawable(context.getResources()),
+                foreground
+        });
+        final int insetPx = context.getResources()
+                .getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset);
+        setLayerInset(1 /* index */, insetPx, insetPx, insetPx, insetPx);
+        mAdaptiveConstantState = new AdaptiveConstantState(context, foreground);
+    }
+
+    /**
+     *  According {@code tile} metaData to set background color
+     */
+    public void setBackgroundColor(Context context, Tile tile) {
+        final Bundle metaData = tile.getMetaData();
+        try {
+            if (metaData != null) {
+                // Load from bg.argb first
+                int bgColor = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB,
+                        0 /* default */);
+                // Not found, load from bg.hint
+                if (bgColor == 0) {
+                    final int colorRes = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
+                            0 /* default */);
+                    if (colorRes != 0) {
+                        bgColor = context.getPackageManager()
+                                .getResourcesForApplication(tile.getPackageName())
+                                .getColor(colorRes, null /* theme */);
+                    }
+                }
+                // If found anything, use it.
+                if (bgColor != 0) {
+                    setBackgroundColor(bgColor);
+                    return;
+                }
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Failed to set background color for " + tile.getPackageName());
+        }
+        setBackgroundColor(context.getColor(R.color.homepage_generic_icon_background));
+    }
+
+    /**
+     * Set background color by {@code color}
+     */
+    public void setBackgroundColor(int color) {
+        mBackgroundColor = color;
+        getDrawable(0).setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+        Log.d(TAG, "Setting background color " + mBackgroundColor);
+        mAdaptiveConstantState.mColor = color;
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mAdaptiveConstantState;
+    }
+
+    @VisibleForTesting
+    static class AdaptiveConstantState extends ConstantState {
+        Context mContext;
+        Drawable mDrawable;
+        int mColor;
+
+        AdaptiveConstantState(Context context, Drawable drawable) {
+            this.mContext = context;
+            this.mDrawable = drawable;
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            final AdaptiveIcon
+                    icon = new AdaptiveIcon(mContext, mDrawable);
+            icon.setBackgroundColor(mColor);
+
+            return icon;
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return 0;
+        }
+    }
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
new file mode 100644
index 0000000..4d7610c
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
+import android.util.AttributeSet;
+import android.util.PathParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Draws a filled {@link ShapeDrawable} using the path from {@link AdaptiveIconDrawable}.
+ */
+public class AdaptiveIconShapeDrawable extends ShapeDrawable {
+    public AdaptiveIconShapeDrawable() {
+        super();
+    }
+
+    public AdaptiveIconShapeDrawable(Resources resources) {
+        super();
+        init(resources);
+    }
+
+    @Override
+    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+            throws XmlPullParserException, IOException {
+        super.inflate(r, parser, attrs, theme);
+        init(r);
+    }
+
+    private void init(Resources resources) {
+        final float pathSize = AdaptiveIconDrawable.MASK_SIZE;
+        final Path path = new Path(PathParser.createPathFromPathData(
+                resources.getString(com.android.internal.R.string.config_icon_mask)));
+        setShape(new PathShape(path, pathSize, pathSize));
+    }
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
new file mode 100644
index 0000000..1c65bc2
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.util.PathParser;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Adaptive outline drawable with white plain background color and black outline
+ */
+public class AdaptiveOutlineDrawable extends DrawableWrapper {
+    @VisibleForTesting
+    final Paint mOutlinePaint;
+    private Path mPath;
+    private final int mInsetPx;
+    private final Bitmap mBitmap;
+
+    public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) {
+        super(new AdaptiveIconShapeDrawable(resources));
+
+        getDrawable().setTint(Color.WHITE);
+        mPath = new Path(PathParser.createPathFromPathData(
+                resources.getString(com.android.internal.R.string.config_icon_mask)));
+        mOutlinePaint = new Paint();
+        mOutlinePaint.setColor(resources.getColor(R.color.bt_outline_color, null));
+        mOutlinePaint.setStyle(Paint.Style.STROKE);
+        mOutlinePaint.setStrokeWidth(resources.getDimension(R.dimen.adaptive_outline_stroke));
+        mOutlinePaint.setAntiAlias(true);
+
+        mInsetPx = resources
+                .getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset);
+        mBitmap = bitmap;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        final Rect bounds = getBounds();
+        final float pathSize = AdaptiveIconDrawable.MASK_SIZE;
+
+        final float scaleX = (bounds.right - bounds.left) / pathSize;
+        final float scaleY = (bounds.bottom - bounds.top) / pathSize;
+
+        final int count = canvas.save();
+        canvas.scale(scaleX, scaleY);
+        // Draw outline
+        canvas.drawPath(mPath, mOutlinePaint);
+        canvas.restoreToCount(count);
+
+        // Draw the foreground icon
+        canvas.drawBitmap(mBitmap, bounds.left + mInsetPx, bounds.top + mInsetPx, null);
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mBitmap.getHeight() + 2 * mInsetPx;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mBitmap.getWidth() + 2 * mInsetPx;
+    }
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 730e9e1..b532621 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -22,6 +22,7 @@
         "SettingsLibEntityHeaderWidgets",
         "SettingsLibBarChartPreference",
         "SettingsLibProgressBar",
+        "SettingsLibAdaptiveIcon",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp
new file mode 100644
index 0000000..bf16ef3
--- /dev/null
+++ b/packages/SettingsLib/Tile/Android.bp
@@ -0,0 +1,11 @@
+android_library {
+    name: "SettingsLibTile",
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+          "androidx.annotation_annotation",
+    ],
+
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/Tile/AndroidManifest.xml b/packages/SettingsLib/Tile/AndroidManifest.xml
new file mode 100644
index 0000000..b13532e
--- /dev/null
+++ b/packages/SettingsLib/Tile/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.drawer">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
similarity index 93%
rename from packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
index a3dda65..7b062b1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.settingslib.drawer;
 
 import static java.lang.String.CASE_INSENSITIVE_ORDER;
@@ -26,6 +25,9 @@
 import java.util.Collections;
 import java.util.List;
 
+/**
+ * The category for handle {@link Tile}
+ */
 public class DashboardCategory implements Parcelable {
 
     /**
@@ -67,18 +69,30 @@
         return result;
     }
 
+    /**
+     * Add tile
+     */
     public synchronized void addTile(Tile tile) {
         mTiles.add(tile);
     }
 
+    /**
+     * Remove tile
+     */
     public synchronized void removeTile(int n) {
         mTiles.remove(n);
     }
 
+    /**
+     * Get size of tile
+     */
     public int getTilesCount() {
         return mTiles.size();
     }
 
+    /**
+     * Get tile
+     */
     public Tile getTile(int n) {
         return mTiles.get(n);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
similarity index 95%
rename from packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index d28b00a..5108efb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -84,8 +84,8 @@
         mActivityPackage = in.readString();
         mActivityName = in.readString();
         mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
-        final int N = in.readInt();
-        for (int i = 0; i < N; i++) {
+        final int number = in.readInt();
+        for (int i = 0; i < number; i++) {
             userHandle.add(UserHandle.CREATOR.createFromParcel(in));
         }
         mCategory = in.readString();
@@ -101,9 +101,9 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mActivityPackage);
         dest.writeString(mActivityName);
-        final int N = userHandle.size();
-        dest.writeInt(N);
-        for (int i = 0; i < N; i++) {
+        final int size = userHandle.size();
+        dest.writeInt(size);
+        for (int i = 0; i < size; i++) {
             userHandle.get(i).writeToParcel(dest, flags);
         }
         dest.writeString(mCategory);
@@ -151,6 +151,9 @@
         }
     }
 
+    /**
+     * Check whether title has order.
+     */
     public boolean hasOrder() {
         return mMetaData.containsKey(META_DATA_KEY_ORDER)
                 && mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
@@ -262,6 +265,9 @@
         }
     }
 
+    /**
+     * Check whether title has key.
+     */
     public boolean hasKey() {
         return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_KEYHINT);
     }
@@ -361,9 +367,12 @@
         }
     };
 
+    /**
+     * Check whether title is only have primary profile
+     */
     public boolean isPrimaryProfileOnly() {
-        String profile = mMetaData != null ?
-                mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+        String profile = mMetaData != null
+                ? mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
         profile = (profile != null ? profile : PROFILE_ALL);
         return TextUtils.equals(profile, PROFILE_PRIMARY);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
similarity index 98%
rename from packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index 91892ab..31925ab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 package com.android.settingslib.drawer;
 
@@ -39,6 +39,10 @@
 import java.util.List;
 import java.util.Map;
 
+/**
+ * Utils is a helper class that contains profile key, meta data, settings action
+ * and static methods for get icon or text from uri.
+ */
 public class TileUtils {
 
     private static final boolean DEBUG_TIMING = false;
diff --git a/packages/SettingsLib/res/drawable/ic_media_device.xml b/packages/SettingsLib/res/drawable/ic_media_device.xml
new file mode 100644
index 0000000..5a6aeb4
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_media_device.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:width="24dp"
+        android:height="24dp"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#00000000"
+        android:fillAlpha=".1"
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M21 3H3c-1.1 0-2 0.9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2 -0.9 2-2V5c0-1.1 -0.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index ed3c11c..39c55fd 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -603,4 +603,26 @@
         <item>3</item><item>3</item>
     </array>
 
+    <!-- Bluetooth icon foreground colors -->
+    <integer-array name="bt_icon_fg_colors">
+        <item>@color/bt_color_icon_1</item>
+        <item>@color/bt_color_icon_2</item>
+        <item>@color/bt_color_icon_3</item>
+        <item>@color/bt_color_icon_4</item>
+        <item>@color/bt_color_icon_5</item>
+        <item>@color/bt_color_icon_6</item>
+        <item>@color/bt_color_icon_7</item>
+    </integer-array>
+
+    <!-- Bluetooth icon background colors -->
+    <integer-array name="bt_icon_bg_colors">
+        <item>@color/bt_color_bg_1</item>
+        <item>@color/bt_color_bg_2</item>
+        <item>@color/bt_color_bg_3</item>
+        <item>@color/bt_color_bg_4</item>
+        <item>@color/bt_color_bg_5</item>
+        <item>@color/bt_color_bg_6</item>
+        <item>@color/bt_color_bg_7</item>
+    </integer-array>
+
 </resources>
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
index 66bbb3a..4b91bbb 100644
--- a/packages/SettingsLib/res/values/colors.xml
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -19,4 +19,20 @@
 
     <color name="usage_graph_dots">@*android:color/tertiary_device_default_settings</color>
     <color name="list_divider_color">#64000000</color>
+
+    <color name="bt_color_icon_1">#48a50e0e</color> <!-- 72% Material Red 900 -->
+    <color name="bt_color_icon_2">#480d652d</color> <!-- 72% Material Green 900 -->
+    <color name="bt_color_icon_3">#48e37400</color> <!-- 72% Material Yellow 900 -->
+    <color name="bt_color_icon_4">#48b06000</color> <!-- 72% Material Orange 900 -->
+    <color name="bt_color_icon_5">#489c166b</color> <!-- 72% Material Pink 900 -->
+    <color name="bt_color_icon_6">#48681da8</color> <!-- 72% Material Purple 900 -->
+    <color name="bt_color_icon_7">#48007b83</color> <!-- 72% Material Cyan 900 -->
+
+    <color name="bt_color_bg_1">#fad2cf</color> <!-- Material Red 100 -->
+    <color name="bt_color_bg_2">#ceead6</color> <!-- Material Green 100 -->
+    <color name="bt_color_bg_3">#feefc3</color> <!-- Material Yellow 100 -->
+    <color name="bt_color_bg_4">#fedfc8</color> <!-- Material Orange 100 -->
+    <color name="bt_color_bg_5">#fdcfe8</color> <!-- Material Pink 100 -->
+    <color name="bt_color_bg_6">#e9d2fd</color> <!-- Material Purple 100 -->
+    <color name="bt_color_bg_7">#cbf0f8</color> <!-- Material Cyan 100 -->
 </resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index a9c5061..2cb9d4b 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -91,6 +91,7 @@
     <!-- How far to inset the rounded edges -->
     <dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen>
 
-
+    <!-- Size of nearby icon -->
+    <dimen name="bt_nearby_icon_size">24dp</dimen>
 
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index bb8c8a6..867efb4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,18 +1,30 @@
 package com.android.settingslib.bluetooth;
 
 import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.util.Log;
 import android.util.Pair;
 
 import androidx.annotation.DrawableRes;
 
 import com.android.settingslib.R;
+import com.android.settingslib.widget.AdaptiveIcon;
+import com.android.settingslib.widget.AdaptiveOutlineDrawable;
 
+import java.io.IOException;
 import java.util.List;
 
 public class BluetoothUtils {
+    private static final String TAG = "BluetoothUtils";
+
     public static final boolean V = false; // verbose logging
     public static final boolean D = true;  // regular logging
 
@@ -112,4 +124,68 @@
     public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId) {
         return context.getDrawable(resId);
     }
+
+    /**
+     * Get colorful bluetooth icon with description
+     */
+    public static Pair<Drawable, String> getBtRainbowDrawableWithDescription(Context context,
+            CachedBluetoothDevice cachedDevice) {
+        final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
+                context, cachedDevice);
+        final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
+        final boolean untetheredHeadset = bluetoothDevice != null
+                ? Boolean.parseBoolean(bluetoothDevice.getMetadata(
+                        BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))
+                : false;
+        final int iconSize = context.getResources().getDimensionPixelSize(
+                R.dimen.bt_nearby_icon_size);
+        final Resources resources = context.getResources();
+
+        // Deal with untethered headset
+        if (untetheredHeadset) {
+            final String uriString = bluetoothDevice != null
+                    ? bluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)
+                    : null;
+            final Uri iconUri = uriString != null ? Uri.parse(uriString) : null;
+            if (iconUri != null) {
+                try {
+                    final Bitmap bitmap = MediaStore.Images.Media.getBitmap(
+                            context.getContentResolver(), iconUri);
+                    if (bitmap != null) {
+                        final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
+                                iconSize, false);
+                        bitmap.recycle();
+                        final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(
+                                resources, resizedBitmap);
+                        return new Pair<>(drawable, pair.second);
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to get drawable for: " + iconUri, e);
+                }
+            }
+        }
+
+        return new Pair<>(buildBtRainbowDrawable(context,
+                pair.first, cachedDevice.getAddress().hashCode()), pair.second);
+    }
+
+    /**
+     * Build Bluetooth device icon with rainbow
+     */
+    public static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
+            int hashCode) {
+        final Resources resources = context.getResources();
+
+        // Deal with normal headset
+        final int[] iconFgColors = resources.getIntArray(R.array.bt_icon_fg_colors);
+        final int[] iconBgColors = resources.getIntArray(R.array.bt_icon_bg_colors);
+
+        // get color index based on mac address
+        final int index =  Math.abs(hashCode % iconBgColors.length);
+        drawable.setColorFilter(iconFgColors[index], PorterDuff.Mode.SRC_ATOP);
+        final Drawable adaptiveIcon = new AdaptiveIcon(context, drawable);
+        ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(iconBgColors[index]);
+
+        return adaptiveIcon;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 3092b99..2711e31 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -18,8 +18,11 @@
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.util.Log;
+import android.util.Pair;
 
+import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
 /**
@@ -48,9 +51,10 @@
     }
 
     @Override
-    public int getIcon() {
-        //TODO(b/117129183): This is not final icon for bluetooth device, just for demo.
-        return com.android.internal.R.drawable.ic_bt_headphones_a2dp;
+    public Drawable getIcon() {
+        final Pair<Drawable, String> pair = BluetoothUtils
+                .getBtRainbowDrawableWithDescription(mContext, mCachedDevice);
+        return pair.first;
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 95f3d3d..732e8db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,10 +16,14 @@
 package com.android.settingslib.media;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.widget.Toast;
 
 import androidx.mediarouter.media.MediaRouter;
 
+import com.android.settingslib.R;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+
 /**
  * InfoMediaDevice extends MediaDevice to represents wifi device.
  */
@@ -46,9 +50,10 @@
     }
 
     @Override
-    public int getIcon() {
-        //TODO(b/121083246): This is not final icon for cast device, just for demo.
-        return com.android.internal.R.drawable.ic_settings_print;
+    public Drawable getIcon() {
+        //TODO(b/120669861): Return remote device icon uri once api is ready.
+        return BluetoothUtils.buildBtRainbowDrawable(mContext,
+                mContext.getDrawable(R.drawable.ic_media_device), getId().hashCode());
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 9b9e803..53a8520 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -16,6 +16,7 @@
 package com.android.settingslib.media;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 
 import androidx.annotation.IntDef;
@@ -70,11 +71,11 @@
     public abstract String getSummary();
 
     /**
-     * Get resource id of MediaDevice.
+     * Get icon of MediaDevice.
      *
-     * @return resource id of MediaDevice.
+     * @return drawable of icon.
      */
-    public abstract int getIcon();
+    public abstract Drawable getIcon();
 
     /**
      * Get unique ID that represent MediaDevice
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 8c3fcc0..af91c34 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -16,10 +16,12 @@
 package com.android.settingslib.media;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.util.Log;
 
 import com.android.settingslib.R;
 import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.HearingAidProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -56,8 +58,9 @@
     }
 
     @Override
-    public int getIcon() {
-        return R.drawable.ic_smartphone;
+    public Drawable getIcon() {
+        return BluetoothUtils.buildBtRainbowDrawable(mContext,
+                mContext.getDrawable(R.drawable.ic_smartphone), getId().hashCode());
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index fdc0fd3..5f2bc4e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -84,7 +84,7 @@
     private static final long DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS = 20 * DateUtils.MINUTE_IN_MILLIS;
 
     /** Maximum age of scan results to hold onto while actively scanning. **/
-    private static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000;
+    @VisibleForTesting static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000;
 
     private static final String TAG = "WifiTracker";
     private static final boolean DBG() {
@@ -142,6 +142,13 @@
      */
     private boolean mStaleScanResults = true;
 
+    /**
+     * Tracks whether the latest SCAN_RESULTS_AVAILABLE_ACTION contained new scans. If not, then
+     * we treat the last scan as an aborted scan and increase the eviction timeout window to avoid
+     * completely flushing the AP list before the next successful scan completes.
+     */
+    private boolean mLastScanSucceeded = true;
+
     // Does not need to be locked as it only updated on the worker thread, with the exception of
     // during onStart, which occurs before the receiver is registered on the work handler.
     private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>();
@@ -478,17 +485,22 @@
     }
 
     /**
-     * Remove old scan results from the cache.
+     * Remove old scan results from the cache. If {@link #mLastScanSucceeded} is false, then
+     * increase the timeout window to avoid completely flushing the AP list before the next
+     * successful scan completes.
      *
      * <p>Should only ever be invoked from {@link #updateScanResultCache(List)} when
      * {@link #mStaleScanResults} is false.
      */
     private void evictOldScans() {
+        long evictionTimeoutMillis = mLastScanSucceeded ? MAX_SCAN_RESULT_AGE_MILLIS
+                : MAX_SCAN_RESULT_AGE_MILLIS * 2;
+
         long nowMs = SystemClock.elapsedRealtime();
         for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
             ScanResult result = iter.next();
             // result timestamp is in microseconds
-            if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MILLIS) {
+            if (nowMs - result.timestamp / 1000 > evictionTimeoutMillis) {
                 iter.remove();
             }
         }
@@ -840,6 +852,8 @@
                                 WifiManager.WIFI_STATE_UNKNOWN));
             } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
                 mStaleScanResults = false;
+                mLastScanSucceeded =
+                        intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true);
 
                 fetchScansAndConfigsAndUpdateAccessPoints();
             } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index edf414d..683ec8b 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -270,7 +270,7 @@
                 SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */);
     }
 
-    private static ScanResult buildStaleScanResult() {
+    private static ScanResult buildScanResultWithTimestamp(long timestampMillis) {
         return new ScanResult(
                 WifiSsid.createFromAsciiEncoded(SSID_3),
                 BSSID_3,
@@ -280,7 +280,7 @@
                 "", // capabilities
                 RSSI_3,
                 0, // frequency
-                0 /* microsecond timestamp */);
+                timestampMillis * 1000 /* microsecond timestamp */);
     }
 
     private static WifiConfiguration buildPasspointConfiguration(String fqdn, String friendlyName) {
@@ -379,6 +379,12 @@
         tracker.mReceiver.onReceive(mContext, i);
     }
 
+    private void sendFailedScanResults(WifiTracker tracker) throws InterruptedException {
+        Intent i = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+        i.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
+        tracker.mReceiver.onReceive(mContext, i);
+    }
+
     private void sendUpdatedScores() throws InterruptedException {
         Bundle attr1 = new Bundle();
         attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve1);
@@ -982,8 +988,8 @@
 
     @Test
     public void onStart_updateScanResults_evictOldScanResult() {
-        when(mockWifiManager.getScanResults()).thenReturn(
-                Arrays.asList(buildScanResult1(), buildScanResult2(), buildStaleScanResult()));
+        when(mockWifiManager.getScanResults()).thenReturn(Arrays.asList(
+                buildScanResult1(), buildScanResult2(), buildScanResultWithTimestamp(0)));
         WifiTracker tracker = createMockedWifiTracker();
 
         tracker.forceUpdate();
@@ -995,6 +1001,33 @@
     }
 
     /**
+     * Verifies that a failed scan reported on SCAN_RESULTS_AVAILABLE_ACTION should increase the
+     * ScanResult eviction timeout to twice the default.
+     */
+    @Test
+    public void failedScan_increasesEvictionTimeout() throws InterruptedException {
+        when(mockWifiManager.getScanResults()).thenReturn(Arrays.asList(
+                buildScanResult1(), buildScanResult2(), buildScanResultWithTimestamp(
+                        SystemClock.elapsedRealtime() - WifiTracker.MAX_SCAN_RESULT_AGE_MILLIS)));
+        WifiTracker tracker = createMockedWifiTracker();
+
+        sendFailedScanResults(tracker);
+
+        // Failed scan increases timeout window to include the stale scan
+        assertThat(tracker.getAccessPoints()).hasSize(3);
+        assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1);
+        assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2);
+        assertThat(tracker.getAccessPoints().get(2).getBssid()).isEqualTo(BSSID_3);
+
+        sendScanResults(tracker);
+
+        // Successful scan resets the timeout window to remove the stale scan
+        assertThat(tracker.getAccessPoints()).hasSize(2);
+        assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1);
+        assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2);
+    }
+
+    /**
      * Verifies that updatePasspointAccessPoints will only return AccessPoints whose
      * isPasspoint() evaluates as true.
      */
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index b713e08..b228cf7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -15,15 +15,20 @@
  */
 package com.android.settingslib.bluetooth;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.util.Pair;
 
+import com.android.settingslib.widget.AdaptiveIcon;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -38,6 +43,9 @@
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+
     private Context mContext;
 
     @Before
@@ -66,4 +74,16 @@
 
         verify(mContext).getDrawable(com.android.internal.R.drawable.ic_bt_laptop);
     }
+
+    @Test
+    public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() {
+        when(mBluetoothDevice.getMetadata(
+                BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn("false");
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mCachedBluetoothDevice.getAddress()).thenReturn("1f:aa:bb");
+
+        assertThat(BluetoothUtils.getBtRainbowDrawableWithDescription(
+                RuntimeEnvironment.application,
+                mCachedBluetoothDevice).first).isInstanceOf(AdaptiveIcon.class);
+    }
 }
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
new file mode 100644
index 0000000..ed6b9b0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Icon;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Bundle;
+
+import com.android.settingslib.R;
+import com.android.settingslib.drawer.CategoryKey;
+import com.android.settingslib.drawer.Tile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AdaptiveIconTest {
+
+    private Context mContext;
+    private ActivityInfo mActivityInfo;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mActivityInfo = new ActivityInfo();
+        mActivityInfo.packageName = mContext.getPackageName();
+        mActivityInfo.name = "class";
+        mActivityInfo.metaData = new Bundle();
+    }
+
+    @Test
+    public void createIcon_shouldSetBackgroundAndInset() {
+        final AdaptiveIcon icon =
+                new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+        assertThat(icon.getNumberOfLayers()).isEqualTo(2);
+        assertThat(icon.getDrawable(0)).isInstanceOf(AdaptiveIconShapeDrawable.class);
+    }
+
+    @Test
+    public void setBackgroundColor_shouldUpdateColorFilter() {
+        final AdaptiveIcon icon =
+                spy(new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)));
+        final ShapeDrawable background = mock(ShapeDrawable.class);
+        when(icon.getDrawable(0)).thenReturn(background);
+
+        icon.setBackgroundColor(Color.BLUE);
+
+        verify(background).setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP);
+    }
+
+    @Test
+    public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() {
+        final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+        mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000);
+        doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+                .when(tile).getIcon(mContext);
+        final AdaptiveIcon icon =
+                new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+        icon.setBackgroundColor(mContext, tile);
+        assertThat(icon.mBackgroundColor).isEqualTo(0xff0000);
+    }
+
+    @Test
+    public void setBackgroundColor_tileWithoutBackgroundColor_shouldSetDefaultBackgroundColor() {
+        final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+        doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+            .when(tile).getIcon(mContext);
+        final AdaptiveIcon icon = new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+        icon.setBackgroundColor(mContext, tile);
+
+        assertThat(icon.mBackgroundColor)
+                .isEqualTo(mContext.getColor(R.color.homepage_generic_icon_background));
+    }
+
+    @Test
+    public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
+        final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+        mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
+                R.color.bt_outline_color);
+        doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+                .when(tile).getIcon(mContext);
+
+        final AdaptiveIcon icon =
+                new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+        icon.setBackgroundColor(mContext, tile);
+
+        assertThat(icon.mBackgroundColor)
+                .isEqualTo(mContext.getColor(R.color.bt_outline_color));
+    }
+
+    @Test
+    public void getConstantState_returnCorrectState() {
+        final AdaptiveIcon icon =
+                new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+        icon.setBackgroundColor(Color.YELLOW);
+
+        final AdaptiveIcon.AdaptiveConstantState state =
+                (AdaptiveIcon.AdaptiveConstantState) icon.getConstantState();
+
+        assertThat(state.mColor).isEqualTo(Color.YELLOW);
+        assertThat(state.mContext).isEqualTo(mContext);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
new file mode 100644
index 0000000..71d55bc
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Resources;
+import android.graphics.Paint;
+
+import com.android.settingslib.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AdaptiveOutlineDrawableTest {
+
+    @Test
+    public void constructor_initPaint() {
+        final Resources resources = RuntimeEnvironment.application.getResources();
+        final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(resources, null);
+
+        assertThat(drawable.mOutlinePaint.getStyle()).isEqualTo(Paint.Style.STROKE);
+        assertThat(drawable.mOutlinePaint.getStrokeWidth()).isWithin(0.01f).of(
+                resources.getDimension(R.dimen.adaptive_outline_stroke));
+    }
+
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d39646b..2a9456d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -168,6 +168,9 @@
     <uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
     <uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
     <uses-permission android:name="android.permission.WATCH_APPOPS" />
+    <!-- Permission needed to invoke DynamicSystem (AOT) -->
+    <uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
+
 
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
     <uses-permission android:name="android.permission.SUSPEND_APPS" />
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 7b7657a..105be46 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -28,7 +28,7 @@
 public interface ClockPlugin extends Plugin {
 
     String ACTION = "com.android.systemui.action.PLUGIN_CLOCK";
-    int VERSION = 2;
+    int VERSION = 3;
 
     /**
      * Get the name of the clock face.
@@ -48,6 +48,17 @@
     Bitmap getThumbnail();
 
     /**
+     * Get preview images of clock face to be shown in the picker app.
+     *
+     * Preview image should be realistic and show what the clock face will look like on AOD and lock
+     * screen.
+     *
+     * @param width width of the preview image, should be the same as device width in pixels.
+     * @param height height of the preview image, should be the same as device height in pixels.
+     */
+    Bitmap getPreview(int width, int height);
+
+    /**
      * Get clock view.
      * @return clock view from plugin.
      */
diff --git a/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml
deleted file mode 100644
index b20e158..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="12.0dp"
-        android:height="12.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="#4DFFFFFF"
-        android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/>
-    <path
-        android:fillColor="#4DFFFFFF"
-        android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_vpn.xml
deleted file mode 100644
index 6567d12..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_vpn.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="12.0dp"
-        android:height="12.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml b/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml
index bfae857..5913cdf 100644
--- a/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml
@@ -13,6 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<!-- This icon is statically overlayed - do not remove.-->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="17.0dp"
         android:height="17.0dp"
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index 9acfbaf..c420117 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -54,6 +54,8 @@
                 android:background="@drawable/rounded_ripple"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
+                android:scaleType="fitCenter"
+                android:padding="@dimen/volume_dialog_ringer_icon_padding"
                 android:tint="@color/accent_tint_color_selector"
                 android:layout_gravity="center"
                 android:soundEffectsEnabled="false" />
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
deleted file mode 100644
index 665fc3f..0000000
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-            android:id="@+id/container"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:fillViewport ="true"
-            android:orientation="vertical">
-
-    <LinearLayout
-        android:id="@+id/dialog_container"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical" >
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="center"
-            android:textDirection="locale"
-            android:textAppearance="@style/TextAppearance.AppOpsDialog.Title"
-            android:textColor="@*android:color/text_color_primary"
-            android:layout_marginStart="@dimen/ongoing_appops_dialog_title_margin_sides"
-            android:layout_marginEnd="@dimen/ongoing_appops_dialog_title_margin_sides"
-            android:layout_marginBottom="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
-            android:layout_marginTop="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
-        />
-
-        <LinearLayout
-            android:orientation="vertical"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="@dimen/ongoing_appops_dialog_items_bottom_margin" >
-
-            <LinearLayout
-                android:id="@+id/items_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="vertical"
-                android:gravity="start"
-            />
-        </LinearLayout>
-
-    </LinearLayout>
-
-</ScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
deleted file mode 100644
index c8e0845..0000000
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/ongoing_appops_dialog_line_height"
-    android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
-    android:layout_marginEnd="@dimen/ongoing_appops_dialog_text_padding"
-    android:fillViewport="true"
-    android:orientation="horizontal"
-    android:focusable="true"
-    android:layout_gravity="center_vertical">
-
-    <FrameLayout
-        android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
-        android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
-        android:layout_gravity="start|center_vertical">
-
-        <ImageView
-            android:id="@+id/app_icon"
-            android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
-            android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
-            android:layout_gravity="center"
-            />
-    </FrameLayout>
-
-    <TextView
-        android:id="@+id/app_name"
-        android:layout_height="match_parent"
-        android:layout_width="0dp"
-        android:layout_weight="1"
-        android:gravity="start|center_vertical"
-        android:textDirection="locale"
-        android:textAppearance="@style/TextAppearance.AppOpsDialog.Item"
-        android:textColor="@*android:color/text_color_primary"
-        android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
-    />
-
-    <LinearLayout
-        android:id="@+id/icons"
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:gravity="end"
-        android:layout_gravity="end|center_vertical"
-        android:visibility="gone"
-    />
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index d1c80c4..a90b1eb 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -56,6 +56,8 @@
                 android:background="@drawable/rounded_ripple"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
+                android:scaleType="fitCenter"
+                android:padding="@dimen/volume_dialog_ringer_icon_padding"
                 android:tint="@color/accent_tint_color_selector"
                 android:layout_gravity="center"
                 android:soundEffectsEnabled="false" />
diff --git a/packages/SystemUI/res/values/config_car.xml b/packages/SystemUI/res/values/config_car.xml
deleted file mode 100644
index 2c549bc..0000000
--- a/packages/SystemUI/res/values/config_car.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources>
-    <!-- These next two work together, if you enable this first one you need to provide an intent
-         uri that will be launched into the docked window. -->
-    <bool name="config_enablePersistentDockedActivity">false</bool>
-    <string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
-
-    <!-- configure which system ui bars should be displayed -->
-    <bool name="config_enableLeftNavigationBar">false</bool>
-    <bool name="config_enableRightNavigationBar">false</bool>
-    <bool name="config_enableBottomNavigationBar">true</bool>
-    <bool name="config_hideNavWhenKeyguardBouncerShown">true</bool>
-</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 039eca6..1a865ee 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -341,6 +341,8 @@
 
     <dimen name="volume_dialog_ringer_size">64dp</dimen>
 
+    <dimen name="volume_dialog_ringer_icon_padding">20dp</dimen>
+
     <dimen name="volume_dialog_caption_size">64dp</dimen>
 
     <dimen name="volume_dialog_tap_target_size">48dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 03b6a52..0411d01 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2345,18 +2345,6 @@
     <!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
     <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
 
-    <!-- Action for accepting the Ongoing privacy dialog [CHAR LIMIT=10]-->
-    <string name="ongoing_privacy_dialog_ok">Got it</string>
-
-    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=23]-->
-    <string name="ongoing_privacy_dialog_open_settings">Privacy settings</string>
-
-    <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
-    <string name="ongoing_privacy_dialog_single_app_title">App using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
-
-    <!-- Text for item in Ongoing Privacy Dialog title when multiple apps is using app ops [CHAR LIMIT=NONE] -->
-    <string name="ongoing_privacy_dialog_multiple_apps_title">Apps using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
-
     <!-- Separator for types. Include spaces before and after if needed [CHAR LIMIT=10] -->
     <string name="ongoing_privacy_dialog_separator">,\u0020</string>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index c0ec405..fb4fe81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -99,13 +99,7 @@
         esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
     }
 
-    private void showDefaultMessage() {
-        if (mRemainingAttempts >= 0) {
-            mSecurityMessageDisplay.setMessage(getPinPasswordErrorMessage(
-                    mRemainingAttempts, true));
-            return;
-        }
-
+    private void setLockedSimMessage() {
         boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
         int count = TelephonyManager.getDefault().getSimCount();
         Resources rez = getResources();
@@ -122,13 +116,20 @@
                 color = info.getIconTint();
             }
         }
-
         if (isEsimLocked) {
             msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
         }
 
         mSecurityMessageDisplay.setMessage(msg);
         mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+    }
+
+    private void showDefaultMessage() {
+        setLockedSimMessage();
+        if (mRemainingAttempts >= 0) {
+            return;
+        }
+
 
         // Sending empty PIN here to query the number of remaining PIN attempts
         new CheckSimPin("", mSubId) {
@@ -137,8 +138,7 @@
                         " attemptsRemaining=" + attemptsRemaining);
                 if (attemptsRemaining >= 0) {
                     mRemainingAttempts = attemptsRemaining;
-                    mSecurityMessageDisplay.setMessage(
-                            getPinPasswordErrorMessage(attemptsRemaining, true));
+                    setLockedSimMessage();
                 }
             }
         }.start();
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
index 32c1242..147def39 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -15,15 +15,19 @@
  */
 package com.android.keyguard.clock;
 
+import android.app.WallpaperManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Color;
 import android.graphics.Paint.Style;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.TextClock;
 
+import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.ClockPlugin;
 
 import java.util.TimeZone;
@@ -44,6 +48,16 @@
     private final LayoutInflater mLayoutInflater;
 
     /**
+     * Extracts accent color from wallpaper.
+     */
+    private final SysuiColorExtractor mColorExtractor;
+
+    /**
+     * Renders preview from clock view.
+     */
+    private final ViewPreviewer mRenderer = new ViewPreviewer();
+
+    /**
      * Custom clock shown on AOD screen and behind stack scroller on lock.
      */
     private View mView;
@@ -64,11 +78,15 @@
     /**
      * Create a BubbleClockController instance.
      *
-     * @param layoutInflater Inflater used to inflate custom clock views.
+     * @param res Resources contains title and thumbnail.
+     * @param inflater Inflater used to inflate custom clock views.
+     * @param colorExtractor Extracts accent color from wallpaper.
      */
-    public BubbleClockController(Resources res, LayoutInflater inflater) {
+    public BubbleClockController(Resources res, LayoutInflater inflater,
+            SysuiColorExtractor colorExtractor) {
         mResources = res;
         mLayoutInflater = inflater;
+        mColorExtractor = colorExtractor;
     }
 
     private void createViews() {
@@ -99,6 +117,23 @@
     }
 
     @Override
+    public Bitmap getPreview(int width, int height) {
+
+        // Use the big clock view for the preview
+        View view = getBigClockView();
+
+        // Initialize state of plugin before generating preview.
+        setDarkAmount(1f);
+        setTextColor(Color.WHITE);
+        ColorExtractor.GradientColors colors = mColorExtractor.getColors(
+                WallpaperManager.FLAG_LOCK, true);
+        setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
+        onTimeTick();
+
+        return mRenderer.createPreview(view, width, height);
+    }
+
+    @Override
     public View getView() {
         if (mLockClockContainer == null) {
             createViews();
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 8ad5c7b..d0fff74 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -16,29 +16,19 @@
 package com.android.keyguard.clock;
 
 import android.annotation.Nullable;
-import android.app.WallpaperManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Color;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.colorextraction.ColorExtractor;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dock.DockManager;
@@ -51,8 +41,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.FutureTask;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -128,7 +116,6 @@
 
     private final List<ClockChangedListener> mListeners = new ArrayList<>();
 
-    private final SysuiColorExtractor mColorExtractor;
     private final int mWidth;
     private final int mHeight;
 
@@ -144,17 +131,16 @@
             ContentResolver contentResolver, SettingsWrapper settingsWrapper) {
         mContext = context;
         mPluginManager = pluginManager;
-        mColorExtractor = colorExtractor;
         mContentResolver = contentResolver;
         mSettingsWrapper = settingsWrapper;
 
         Resources res = context.getResources();
         LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context));
 
-        addClockPlugin(new DefaultClockController(res, layoutInflater));
-        addClockPlugin(new BubbleClockController(res, layoutInflater));
-        addClockPlugin(new StretchAnalogClockController(res, layoutInflater));
-        addClockPlugin(new TypeClockController(res, layoutInflater));
+        addClockPlugin(new DefaultClockController(res, layoutInflater, colorExtractor));
+        addClockPlugin(new BubbleClockController(res, layoutInflater, colorExtractor));
+        addClockPlugin(new StretchAnalogClockController(res, layoutInflater, colorExtractor));
+        addClockPlugin(new TypeClockController(res, layoutInflater, colorExtractor));
 
         // Store the size of the display for generation of clock preview.
         DisplayMetrics dm = res.getDisplayMetrics();
@@ -217,7 +203,7 @@
                 .setTitle(plugin.getTitle())
                 .setId(id)
                 .setThumbnail(() -> plugin.getThumbnail())
-                .setPreview(() -> getClockPreview(id))
+                .setPreview(() -> plugin.getPreview(mWidth, mHeight))
                 .build());
     }
 
@@ -232,81 +218,6 @@
         }
     }
 
-    /**
-     * Generate a realistic preview of a clock face.
-     * @param clockId ID of clock to use for preview, should be obtained from {@link getClockInfos}.
-     *        Returns null if clockId is not found.
-     */
-    @Nullable
-    private Bitmap getClockPreview(String clockId) {
-        FutureTask<Bitmap> task = new FutureTask<>(new Callable<Bitmap>() {
-            @Override
-            public Bitmap call() {
-                Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888);
-                ClockPlugin plugin = mClocks.get(clockId);
-                if (plugin == null) {
-                    return null;
-                }
-
-                // Use the big clock view for the preview
-                View clockView = plugin.getBigClockView();
-                if (clockView == null) {
-                    return null;
-                }
-
-                // Initialize state of plugin before generating preview.
-                plugin.setDarkAmount(1f);
-                plugin.setTextColor(Color.WHITE);
-
-                ColorExtractor.GradientColors colors = mColorExtractor.getColors(
-                        WallpaperManager.FLAG_LOCK, true);
-                plugin.setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
-                plugin.onTimeTick();
-
-                // Draw clock view hierarchy to canvas.
-                Canvas canvas = new Canvas(bitmap);
-                canvas.drawColor(Color.BLACK);
-                dispatchVisibilityAggregated(clockView, true);
-                clockView.measure(MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY));
-                clockView.layout(0, 0, mWidth, mHeight);
-                clockView.draw(canvas);
-                return bitmap;
-            }
-        });
-
-        if (Looper.myLooper() == Looper.getMainLooper()) {
-            task.run();
-        } else {
-            mMainHandler.post(task);
-        }
-
-        try {
-            return task.get();
-        } catch (Exception e) {
-            Log.e(TAG, "Error completing task", e);
-            return null;
-        }
-    }
-
-    private void dispatchVisibilityAggregated(View view, boolean isVisible) {
-        // Similar to View.dispatchVisibilityAggregated implementation.
-        final boolean thisVisible = view.getVisibility() == View.VISIBLE;
-        if (thisVisible || !isVisible) {
-            view.onVisibilityAggregated(isVisible);
-        }
-
-        if (view instanceof ViewGroup) {
-            isVisible = thisVisible && isVisible;
-            ViewGroup vg = (ViewGroup) view;
-            int count = vg.getChildCount();
-
-            for (int i = 0; i < count; i++) {
-                dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
-            }
-        }
-    }
-
     private void notifyClockChanged(ClockPlugin plugin) {
         for (int i = 0; i < mListeners.size(); i++) {
             // It probably doesn't make sense to supply the same plugin instances to multiple
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java
index 8a6a4cd..73414b3 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java
@@ -15,15 +15,19 @@
  */
 package com.android.keyguard.clock;
 
+import android.app.WallpaperManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Color;
 import android.graphics.Paint.Style;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.TextView;
 
+import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.ClockPlugin;
 
 import java.util.TimeZone;
@@ -44,6 +48,16 @@
     private final LayoutInflater mLayoutInflater;
 
     /**
+     * Extracts accent color from wallpaper.
+     */
+    private final SysuiColorExtractor mColorExtractor;
+
+    /**
+     * Renders preview from clock view.
+     */
+    private final ViewPreviewer mRenderer = new ViewPreviewer();
+
+    /**
      * Root view of preview.
      */
     private View mView;
@@ -61,11 +75,15 @@
     /**
      * Create a DefaultClockController instance.
      *
+     * @param res Resources contains title and thumbnail.
      * @param inflater Inflater used to inflate custom clock views.
+     * @param colorExtractor Extracts accent color from wallpaper.
      */
-    public DefaultClockController(Resources res, LayoutInflater inflater) {
+    public DefaultClockController(Resources res, LayoutInflater inflater,
+            SysuiColorExtractor colorExtractor) {
         mResources = res;
         mLayoutInflater = inflater;
+        mColorExtractor = colorExtractor;
     }
 
     private void createViews() {
@@ -90,6 +108,23 @@
     }
 
     @Override
+    public Bitmap getPreview(int width, int height) {
+
+        // Use the big clock view for the preview
+        View view = getBigClockView();
+
+        // Initialize state of plugin before generating preview.
+        setDarkAmount(1f);
+        setTextColor(Color.WHITE);
+        ColorExtractor.GradientColors colors = mColorExtractor.getColors(
+                WallpaperManager.FLAG_LOCK, true);
+        setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
+        onTimeTick();
+
+        return mRenderer.createPreview(view, width, height);
+    }
+
+    @Override
     public View getView() {
         return null;
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
index 34b2fd8..ea9f0cd 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
@@ -15,15 +15,19 @@
  */
 package com.android.keyguard.clock;
 
+import android.app.WallpaperManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Color;
 import android.graphics.Paint.Style;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.TextClock;
 
+import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.ClockPlugin;
 
 import java.util.TimeZone;
@@ -44,6 +48,16 @@
     private final LayoutInflater mLayoutInflater;
 
     /**
+     * Extracts accent color from wallpaper.
+     */
+    private final SysuiColorExtractor mColorExtractor;
+
+    /**
+     * Renders preview from clock view.
+     */
+    private final ViewPreviewer mRenderer = new ViewPreviewer();
+
+    /**
      * Custom clock shown on AOD screen and behind stack scroller on lock.
      */
     private View mBigClockView;
@@ -64,11 +78,15 @@
     /**
      * Create a BubbleClockController instance.
      *
-     * @param layoutInflater Inflater used to inflate custom clock views.
+     * @param res Resources contains title and thumbnail.
+     * @param inflater Inflater used to inflate custom clock views.
+     * @param colorExtractor Extracts accent color from wallpaper.
      */
-    public StretchAnalogClockController(Resources res, LayoutInflater inflater) {
+    public StretchAnalogClockController(Resources res, LayoutInflater inflater,
+            SysuiColorExtractor colorExtractor) {
         mResources = res;
         mLayoutInflater = inflater;
+        mColorExtractor = colorExtractor;
     }
 
     private void createViews() {
@@ -99,6 +117,23 @@
     }
 
     @Override
+    public Bitmap getPreview(int width, int height) {
+
+        // Use the big clock view for the preview
+        View view = getBigClockView();
+
+        // Initialize state of plugin before generating preview.
+        setDarkAmount(1f);
+        setTextColor(Color.WHITE);
+        ColorExtractor.GradientColors colors = mColorExtractor.getColors(
+                WallpaperManager.FLAG_LOCK, true);
+        setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
+        onTimeTick();
+
+        return mRenderer.createPreview(view, width, height);
+    }
+
+    @Override
     public View getView() {
         if (mView == null) {
             createViews();
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
index 387f265..67c0989 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
@@ -15,14 +15,18 @@
  */
 package com.android.keyguard.clock;
 
+import android.app.WallpaperManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Color;
 import android.graphics.Paint.Style;
 import android.view.LayoutInflater;
 import android.view.View;
 
+import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.ClockPlugin;
 
 import java.util.TimeZone;
@@ -43,6 +47,16 @@
     private final LayoutInflater mLayoutInflater;
 
     /**
+     * Extracts accent color from wallpaper.
+     */
+    private final SysuiColorExtractor mColorExtractor;
+
+    /**
+     * Renders preview from clock view.
+     */
+    private final ViewPreviewer mRenderer = new ViewPreviewer();
+
+    /**
      * Custom clock shown on AOD screen and behind stack scroller on lock.
      */
     private View mView;
@@ -61,11 +75,15 @@
     /**
      * Create a TypeClockController instance.
      *
+     * @param res Resources contains title and thumbnail.
      * @param inflater Inflater used to inflate custom clock views.
+     * @param colorExtractor Extracts accent color from wallpaper.
      */
-    TypeClockController(Resources res, LayoutInflater inflater) {
+    TypeClockController(Resources res, LayoutInflater inflater,
+            SysuiColorExtractor colorExtractor) {
         mResources = res;
         mLayoutInflater = inflater;
+        mColorExtractor = colorExtractor;
     }
 
     private void createViews() {
@@ -96,6 +114,23 @@
     }
 
     @Override
+    public Bitmap getPreview(int width, int height) {
+
+        // Use the big clock view for the preview
+        View view = getBigClockView();
+
+        // Initialize state of plugin before generating preview.
+        setDarkAmount(1f);
+        setTextColor(Color.WHITE);
+        ColorExtractor.GradientColors colors = mColorExtractor.getColors(
+                WallpaperManager.FLAG_LOCK, true);
+        setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
+        onTimeTick();
+
+        return mRenderer.createPreview(view, width, height);
+    }
+
+    @Override
     public View getView() {
         if (mLockClock == null) {
             createViews();
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java b/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java
new file mode 100644
index 0000000..abd0dd2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Creates a preview image ({@link Bitmap}) of a {@link View} for a custom clock face.
+ */
+final class ViewPreviewer {
+
+    private static final String TAG = "ViewPreviewer";
+
+    /**
+     * Handler used to run {@link View#draw(Canvas)} on the main thread.
+     */
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+    /**
+     * Generate a realistic preview of a clock face.
+     *
+     * @param view view is used to generate preview image.
+     * @param width width of the preview image, should be the same as device width in pixels.
+     * @param height height of the preview image, should be the same as device height in pixels.
+     * @return bitmap of view.
+     */
+    @Nullable
+    Bitmap createPreview(View view, int width, int height) {
+        if (view == null) {
+            return null;
+        }
+        FutureTask<Bitmap> task = new FutureTask<>(new Callable<Bitmap>() {
+            @Override
+            public Bitmap call() {
+                Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+                // Draw clock view hierarchy to canvas.
+                Canvas canvas = new Canvas(bitmap);
+                canvas.drawColor(Color.BLACK);
+                dispatchVisibilityAggregated(view, true);
+                view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+                        View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
+                view.layout(0, 0, width, height);
+                view.draw(canvas);
+
+                return bitmap;
+            }
+        });
+
+        if (Looper.myLooper() == Looper.getMainLooper()) {
+            task.run();
+        } else {
+            mMainHandler.post(task);
+        }
+
+        try {
+            return task.get();
+        } catch (Exception e) {
+            Log.e(TAG, "Error completing task", e);
+            return null;
+        }
+    }
+
+    private void dispatchVisibilityAggregated(View view, boolean isVisible) {
+        // Similar to View.dispatchVisibilityAggregated implementation.
+        final boolean thisVisible = view.getVisibility() == View.VISIBLE;
+        if (thisVisible || !isVisible) {
+            view.onVisibilityAggregated(isVisible);
+        }
+
+        if (view instanceof ViewGroup) {
+            isVisible = thisVisible && isVisible;
+            ViewGroup vg = (ViewGroup) view;
+            int count = vg.getChildCount();
+
+            for (int i = 0; i < count; i++) {
+                dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 559c9f6..de887ff 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -381,10 +381,18 @@
     }
 
     /**
-     * Update bubble expanded view header when user toggles dark mode.
+     * Update header color when user toggles dark mode.
      */
     void updateHeaderColor() {
-        mHeaderView.setBackgroundColor(mContext.getColor(R.attr.colorAccent));
+        TypedArray ta = mContext.obtainStyledAttributes(
+                new int[] {android.R.attr.colorBackgroundFloating, android.R.attr.colorForeground});
+        int bgColor = ta.getColor(0, Color.WHITE /* default */);
+        int btnColor = ta.getColor(1, Color.BLACK /* default */);
+        ta.recycle();
+
+        mHeaderView.setBackgroundColor(bgColor);
+        mSettingsIcon.setColorFilter(btnColor);
+        mDeepLinkIcon.setColorFilter(btnColor);
     }
 
     private void updateHeaderView() {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 617090a..be55829 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -52,6 +52,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.ViewClippingUtil;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.BubbleController.DismissReason;
 import com.android.systemui.bubbles.animation.ExpandedAnimationController;
@@ -270,8 +271,8 @@
      * Handle config changes.
      */
     public void onConfigChanged() {
-        if (mExpandedBubble != null) {
-            mExpandedBubble.expandedView.updateHeaderColor();
+        for (Bubble b: mBubbleData.getBubbles()) {
+            b.expandedView.updateHeaderColor();
         }
     }
 
@@ -316,7 +317,8 @@
         }
         switch (action) {
             case AccessibilityNodeInfo.ACTION_DISMISS:
-                stackDismissed(BubbleController.DISMISS_ACCESSIBILITY_ACTION);
+                Dependency.get(BubbleController.class).dismissStack(
+                        BubbleController.DISMISS_ACCESSIBILITY_ACTION);
                 return true;
             case AccessibilityNodeInfo.ACTION_COLLAPSE:
                 collapseStack();
@@ -422,7 +424,7 @@
      * Sets the entry that should be expanded and expands if needed.
      */
     @VisibleForTesting
-    public void setExpandedBubble(NotificationEntry entry) {
+    void setExpandedBubble(NotificationEntry entry) {
         for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
             BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
             if (entry.equals(bv.getEntry())) {
@@ -436,7 +438,7 @@
      *
      * @param entry the notification to add to the stack of bubbles.
      */
-    public void addBubble(NotificationEntry entry) {
+    void addBubble(NotificationEntry entry) {
         Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener);
         mBubbleData.addBubble(b);
 
@@ -451,12 +453,17 @@
     /**
      * Remove a bubble from the stack.
      */
-    public void removeBubble(String key, int reason) {
+    void removeBubble(String key, int reason) {
         Bubble b = mBubbleData.removeBubble(key);
         if (b == null) {
             return;
         }
-        int removedIndex = dismissBubble(b, reason);
+        setBubbleDismissed(b, reason);
+
+        // Remove it from the views
+        int removedIndex = mBubbleContainer.indexOfChild(b.iconView);
+        mBubbleContainer.removeViewAt(removedIndex);
+
         int bubbleCount = mBubbleContainer.getChildCount();
         if (bubbleCount == 0) {
             // If no bubbles remain, collapse the entire stack.
@@ -481,9 +488,9 @@
     /**
      * Dismiss the stack of bubbles.
      */
-    public void stackDismissed(int reason) {
+    void stackDismissed(int reason) {
         for (Bubble bubble : mBubbleData.getBubbles()) {
-            dismissBubble(bubble, reason);
+            setBubbleDismissed(bubble, reason);
         }
         mBubbleData.clear();
         collapseStack();
@@ -495,8 +502,7 @@
     }
 
     /**
-     * Marks the notification entry as dismissed, cleans up Bubble icon and expanded view UI
-     * elements and calls deleteIntent if necessary.
+     * Marks the notification entry as dismissed & calls any delete intents for the bubble.
      *
      * <p>Note: This does not remove the Bubble from BubbleData.
      *
@@ -504,17 +510,13 @@
      * @param reason code for the reason the dismiss was triggered
      * @see BubbleController.DismissReason
      */
-    private int dismissBubble(Bubble bubble, @DismissReason int reason) {
+    private void setBubbleDismissed(Bubble bubble, @DismissReason int reason) {
         if (DEBUG) {
             Log.d(TAG, "dismissBubble: " + bubble + " reason=" + reason);
         }
         bubble.entry.setBubbleDismissed(true);
         bubble.expandedView.cleanUpExpandedState();
 
-        // Remove it from the views
-        int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView);
-        mBubbleContainer.removeViewAt(removedIndex);
-
         if (reason == BubbleController.DISMISS_USER_GESTURE) {
             Notification.BubbleMetadata bubbleMetadata = bubble.entry.getBubbleMetadata();
             PendingIntent deleteIntent = bubbleMetadata != null
@@ -529,7 +531,6 @@
                 }
             }
         }
-        return removedIndex;
     }
 
     /**
@@ -856,7 +857,6 @@
 
     private void applyCurrentState() {
         Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
-
         mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         if (mIsExpanded) {
             // First update the view so that it calculates a new height (ensuring the y position
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 1dd31010..bd6882c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -56,6 +56,7 @@
     private boolean mRegistered;
     private int mDefaultDozeBrightness;
     private boolean mPaused = false;
+    private boolean mScreenOff = false;
     private int mLastSensorValue = -1;
 
     /**
@@ -118,6 +119,7 @@
                 break;
         }
         if (newState != DozeMachine.State.FINISH) {
+            setScreenOff(newState == DozeMachine.State.DOZE);
             setPaused(newState == DozeMachine.State.DOZE_AOD_PAUSED);
         }
     }
@@ -135,15 +137,15 @@
         try {
             if (mRegistered) {
                 mLastSensorValue = (int) event.values[0];
-                updateBrightnessAndReady();
+                updateBrightnessAndReady(false /* force */);
             }
         } finally {
             Trace.endSection();
         }
     }
 
-    private void updateBrightnessAndReady() {
-        if (mRegistered || mDebugBrightnessBucket != -1) {
+    private void updateBrightnessAndReady(boolean force) {
+        if (force || mRegistered || mDebugBrightnessBucket != -1) {
             int sensorValue = mDebugBrightnessBucket == -1
                     ? mLastSensorValue : mDebugBrightnessBucket;
             int brightness = computeBrightness(sensorValue);
@@ -153,7 +155,7 @@
             }
 
             int scrimOpacity = -1;
-            if (mPaused) {
+            if (mPaused || mScreenOff) {
                 // If AOD is paused, force the screen black until the
                 // sensor reports a new brightness. This ensures that when the screen comes on
                 // again, it will only show after the brightness sensor has stabilized,
@@ -216,13 +218,20 @@
     private void setPaused(boolean paused) {
         if (mPaused != paused) {
             mPaused = paused;
-            updateBrightnessAndReady();
+            updateBrightnessAndReady(false /* force */);
+        }
+    }
+
+    private void setScreenOff(boolean screenOff) {
+        if (mScreenOff != screenOff) {
+            mScreenOff = screenOff;
+            updateBrightnessAndReady(true /* force */);
         }
     }
 
     @Override
     public void onReceive(Context context, Intent intent) {
         mDebugBrightnessBucket = intent.getIntExtra(BRIGHTNESS_BUCKET, -1);
-        updateBrightnessAndReady();
+        updateBrightnessAndReady(false /* force */);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
index 59b3c34..d08a373 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
@@ -53,9 +53,4 @@
             else -> types.map { it.getName(context) }.joinWithAnd().toString()
         }
     }
-
-    fun getDialogTitle(): String {
-        return context.getString(R.string.ongoing_privacy_dialog_multiple_apps_title,
-                    joinTypes())
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 7c937a9..b682cb0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -160,9 +160,9 @@
         int footerIconId = R.drawable.ic_info_outline;
         if (vpnName != null || vpnNameWorkProfile != null) {
             if (mSecurityController.isVpnBranded()) {
-                footerIconId = R.drawable.ic_qs_branded_vpn;
+                footerIconId = R.drawable.stat_sys_branded_vpn;
             } else {
-                footerIconId = R.drawable.ic_qs_vpn;
+                footerIconId = R.drawable.stat_sys_vpn_ic;
             }
         }
         if (mFooterIconId != footerIconId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 7905617..211a40a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -70,7 +70,7 @@
 
     private final String mTag;
     private final View mView;
-    private final BarBackgroundDrawable mBarBackground;
+    protected final BarBackgroundDrawable mBarBackground;
 
     private int mMode;
     private boolean mAlwaysOpaque = false;
@@ -152,7 +152,7 @@
         return mode == MODE_LIGHTS_OUT || mode == MODE_LIGHTS_OUT_TRANSPARENT;
     }
 
-    private static class BarBackgroundDrawable extends Drawable {
+    protected static class BarBackgroundDrawable extends Drawable {
         private final int mOpaque;
         private final int mSemiTransparent;
         private final int mTransparent;
@@ -171,6 +171,7 @@
 
         private int mGradientAlphaStart;
         private int mColorStart;
+        private Rect mFrame;
 
 
         public BarBackgroundDrawable(Context context, int gradientResourceId) {
@@ -190,6 +191,10 @@
             mGradient = context.getDrawable(gradientResourceId);
         }
 
+        public void setFrame(Rect frame) {
+            mFrame = frame;
+        }
+
         @Override
         public void setAlpha(int alpha) {
             // noop
@@ -296,7 +301,11 @@
                 if (mTintFilter != null) {
                     mPaint.setColorFilter(mTintFilter);
                 }
-                canvas.drawPaint(mPaint);
+                if (mFrame != null) {
+                    canvas.drawRect(mFrame, mPaint);
+                } else {
+                    canvas.drawPaint(mPaint);
+                }
             }
             if (mAnimating) {
                 invalidateSelf();  // keep going
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index cbb5d54..9485623 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -988,11 +988,11 @@
                 if (Intent.ACTION_SCREEN_ON.equals(action)) {
                     // Enabled and screen is on, start it again if enabled
                     if (NavBarTintController.isEnabled(getContext())) {
-                        mNavigationBarView.getColorAdaptionController().start();
+                        mNavigationBarView.getTintController().start();
                     }
                 } else {
                     // Screen off disable it
-                    mNavigationBarView.getColorAdaptionController().stop();
+                    mNavigationBarView.getTintController().stop();
                 }
             }
             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 3984405..d4cec42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -105,6 +106,10 @@
         applyLightsOut(true, false);
     }
 
+    void setBackgroundFrame(Rect frame) {
+        mBarBackground.setFrame(frame);
+    }
+
     @Override
     protected boolean isLightsOut(int mode) {
         return super.isLightsOut(mode) || (mAllowAutoDimWallpaperNotVisible && mAutoDim
@@ -119,6 +124,7 @@
     protected void onTransition(int oldMode, int newMode, boolean animate) {
         super.onTransition(oldMode, newMode, animate);
         applyLightsOut(animate, false /*force*/);
+        mView.onBarTransition(newMode);
     }
 
     private void applyLightsOut(boolean animate, boolean force) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index f2d6241..f22ecf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -30,6 +30,7 @@
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS;
 
 import android.animation.LayoutTransition;
@@ -172,7 +173,7 @@
     private RecentsOnboarding mRecentsOnboarding;
     private NotificationPanelView mPanelView;
 
-    private NavBarTintController mColorAdaptionController;
+    private NavBarTintController mTintController;
     private boolean mAssistantAvailable;
     private NavigationPrototypeController mPrototypeController;
     private NavigationGestureAction[] mDefaultGestureMap;
@@ -309,9 +310,9 @@
         @Override
         public void onColorAdaptChanged(boolean enabled) {
             if (enabled) {
-                mColorAdaptionController.start();
+                mTintController.start();
             } else {
-                mColorAdaptionController.stop();
+                mTintController.stop();
             }
         }
 
@@ -442,15 +443,15 @@
         mPrototypeController = new NavigationPrototypeController(context);
         mPrototypeController.register();
         mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
-        mColorAdaptionController = new NavBarTintController(this, getLightTransitionsController());
+        mTintController = new NavBarTintController(this, getLightTransitionsController());
 
         IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
         filter.addDataScheme("package");
         context.registerReceiver(mOverlaysChangedReceiver, filter);
     }
 
-    public NavBarTintController getColorAdaptionController() {
-        return mColorAdaptionController;
+    public NavBarTintController getTintController() {
+        return mTintController;
     }
 
     public BarTransitions getBarTransitions() {
@@ -476,7 +477,7 @@
     @Override
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
-        mColorAdaptionController.onDraw();
+        mTintController.onDraw();
     }
 
     private void updateNavigationGestures() {
@@ -557,6 +558,17 @@
         return super.onTouchEvent(event);
     }
 
+    void onBarTransition(int newMode) {
+        if (newMode == MODE_OPAQUE) {
+            // If the nav bar background is opaque, stop auto tinting since we know the icons are
+            // showing over a dark background
+            mTintController.stop();
+            getLightTransitionsController().setIconsDark(false /* dark */, true /* animate */);
+        } else {
+            mTintController.start();
+        }
+    }
+
     private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) {
         if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) {
             switch (event.getActionMasked()) {
@@ -978,9 +990,9 @@
 
         // Color adaption is tied with showing home handle, only avaliable if visible
         if (visible) {
-            mColorAdaptionController.start();
+            mTintController.start();
         } else {
-            mColorAdaptionController.stop();
+            mTintController.stop();
         }
     }
 
@@ -1206,6 +1218,19 @@
             reorient();
             notifyVerticalChangedListener(newVertical);
         }
+
+        if (QuickStepContract.isGesturalMode(getContext())) {
+            // Update the nav bar background to match the height of the visible nav bar
+            int height = mIsVertical
+                    ? getResources().getDimensionPixelSize(
+                            com.android.internal.R.dimen.navigation_bar_height_landscape)
+                    : getResources().getDimensionPixelSize(
+                            com.android.internal.R.dimen.navigation_bar_height);
+            int frameHeight = getResources().getDimensionPixelSize(
+                    com.android.internal.R.dimen.navigation_bar_frame_height);
+            mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h));
+        }
+
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
@@ -1232,9 +1257,9 @@
         }
 
         if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
-            mColorAdaptionController.start();
+            mTintController.start();
         } else {
-            mColorAdaptionController.stop();
+            mTintController.stop();
         }
     }
 
@@ -1417,7 +1442,7 @@
             mGestureHelper.dump(pw);
         }
         mRecentsOnboarding.dump(pw);
-        mColorAdaptionController.dump(pw);
+        mTintController.dump(pw);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
index 81a425c..7dc71f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
@@ -57,6 +57,7 @@
         mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor);
         mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor);
         mPaint.setAntiAlias(true);
+        setFocusable(false);
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
index 267468f..f03c234 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
@@ -27,10 +27,13 @@
 import android.widget.TextView;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -38,12 +41,15 @@
 public final class BubbleClockControllerTest extends SysuiTestCase {
 
     private BubbleClockController mClockController;
+    @Mock SysuiColorExtractor mMockColorExtractor;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
         Resources res = getContext().getResources();
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
-        mClockController = new BubbleClockController(res, layoutInflater);
+        mClockController = new BubbleClockController(res, layoutInflater, mMockColorExtractor);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
index 0659b4f..26fa62b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
@@ -27,10 +27,13 @@
 import android.widget.TextView;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -38,12 +41,16 @@
 public final class StretchAnalogClockControllerTest extends SysuiTestCase {
 
     private StretchAnalogClockController mClockController;
+    @Mock SysuiColorExtractor mMockColorExtractor;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
         Resources res = getContext().getResources();
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
-        mClockController = new StretchAnalogClockController(res, layoutInflater);
+        mClockController = new StretchAnalogClockController(res, layoutInflater,
+                mMockColorExtractor);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt
new file mode 100644
index 0000000..d9ef7fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard.clock
+
+import android.content.Context
+import com.google.common.truth.Truth.assertThat
+
+import android.graphics.Canvas
+import android.graphics.Color
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ViewPreviewerTest : SysuiTestCase() {
+
+    private lateinit var previewer: ViewPreviewer
+    private lateinit var view: View
+
+    @Before
+    fun setUp() {
+        previewer = ViewPreviewer()
+        view = TestView(context)
+    }
+
+    @Test
+    fun testCreatePreview() {
+        val width = 100
+        val height = 100
+        // WHEN a preview image is created
+        val bitmap = previewer.createPreview(view, width, height)
+        // THEN the bitmap has the expected width and height
+        assertThat(bitmap.height).isEqualTo(height)
+        assertThat(bitmap.width).isEqualTo(width)
+        assertThat(bitmap.getPixel(0, 0)).isEqualTo(Color.RED)
+    }
+
+    class TestView(context: Context) : View(context) {
+        override fun onDraw(canvas: Canvas?) {
+            super.onDraw(canvas)
+            canvas?.drawColor(Color.RED)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index cde3398..392c677 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -237,6 +237,18 @@
     }
 
     @Test
+    public void screenOff_softBlanks() throws Exception {
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        mScreen.transitionTo(DOZE_AOD, DOZE);
+        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+
+        mScreen.transitionTo(DOZE, DOZE_AOD);
+        mSensor.sendSensorEvent(2);
+        assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+    }
+
+    @Test
     public void pausingAod_unblanksAfterSensor() throws Exception {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index fd31013..47933ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -194,7 +194,7 @@
                                         VPN_PACKAGE),
                      mFooterText.getText());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
 
         // Same situation, but with organization name set
         when(mSecurityController.getDeviceOwnerOrganizationName())
@@ -220,7 +220,7 @@
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns),
                      mFooterText.getText());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
 
         // Same situation, but with organization name set
         when(mSecurityController.getDeviceOwnerOrganizationName())
@@ -243,7 +243,7 @@
 
         TestableLooper.get(this).processAllMessages();
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
                 mFooterText.getText());
     }
@@ -294,7 +294,7 @@
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
-        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_vpns),
                      mFooterText.getText());
     }
@@ -306,7 +306,7 @@
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
-        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(
                              R.string.quick_settings_disclosure_managed_profile_named_vpn,
                              VPN_PACKAGE_2),
@@ -320,7 +320,7 @@
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
-        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_vpn,
                                         VPN_PACKAGE),
                      mFooterText.getText());
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index 72ce9c4..989470f 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -29,6 +29,7 @@
 import android.text.Html;
 import android.text.Html.ImageGetter;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.View;
 import android.widget.Button;
 import android.widget.TextView;
@@ -111,8 +112,16 @@
     @Override
     public Drawable getDrawable(String source) {
         // Should only reach this when fetching the VPN icon for the warning string.
-        Drawable icon = getDrawable(R.drawable.ic_vpn_dialog);
+        final Drawable icon = getDrawable(R.drawable.ic_vpn_dialog);
         icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
+
+        final TypedValue tv = new TypedValue();
+        if (getTheme().resolveAttribute(android.R.attr.textColorPrimary, tv, true)) {
+            icon.setTint(getColor(tv.resourceId));
+        } else {
+            Log.w(TAG, "Unable to resolve theme color");
+        }
+
         return icon;
     }
 
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index f52b94f..ce39a70 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7158,6 +7158,10 @@
 
     // OPEN: Settings > System > Aware > Info dialog
     DIALOG_AWARE_STATUS = 1701;
+
+    // Open: Settings > app > bubble settings > confirmation dialog
+    DIALOG_APP_BUBBLE_SETTINGS = 1702;
+
     // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 1ce0c52..af77df6 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1752,6 +1752,9 @@
 
     // Firmware generated an alert
     TYPE_FIRMWARE_ALERT = 4;
+
+    // IP Manager lost reachability to network neighbors
+    TYPE_IP_REACHABILITY_LOST = 5;
   }
 
   // What event triggered WifiIsUnusableEvent.
@@ -2012,6 +2015,10 @@
 
   // Whether the primary registered cell of current entry is same as that of previous entry
   optional bool is_same_registered_cell = 33;
+
+  // The device mobility state
+  optional DeviceMobilityStatePnoScanStats.DeviceMobilityState
+          device_mobility_state = 34;
 }
 
 message WifiUsabilityStats {
@@ -2041,6 +2048,9 @@
 
     // Firmware generated an alert
     TYPE_FIRMWARE_ALERT = 4;
+
+    // IP Manager lost reachability to network neighbors
+    TYPE_IP_REACHABILITY_LOST = 5;
   }
 
   // The current wifi usability state
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 2ded1e5..be58389 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -323,16 +323,24 @@
                 });
         mPackageManager.addOnPermissionsChangeListener(
                 uid -> {
-                    synchronized (mLock) {
-                        onPermissionsChangedLocked();
-                    }
+                    // listener invoked on ui thread, move to our thread to reduce risk of blocking
+                    // ui thread
+                    mHandler.post(() -> {
+                        synchronized (mLock) {
+                            onPermissionsChangedLocked();
+                        }
+                    });
                 });
 
         mActivityManager.addOnUidImportanceListener(
                 (uid, importance) -> {
-                    synchronized (mLock) {
-                        onUidImportanceChangedLocked(uid, importance);
-                    }
+                    // listener invoked on ui thread, move to our thread to reduce risk of blocking
+                    // ui thread
+                    mHandler.post(() -> {
+                        synchronized (mLock) {
+                            onUidImportanceChangedLocked(uid, importance);
+                        }
+                    });
                 },
                 FOREGROUND_IMPORTANCE_CUTOFF);
         mContext.getContentResolver().registerContentObserver(
@@ -394,9 +402,13 @@
                 LocalServices.getService(PowerManagerInternal.class);
         localPowerManager.registerLowPowerModeObserver(ServiceType.LOCATION,
                 state -> {
-                    synchronized (mLock) {
-                        onBatterySaverModeChangedLocked(state.locationMode);
-                    }
+                    // listener invoked on ui thread, move to our thread to reduce risk of blocking
+                    // ui thread
+                    mHandler.post(() -> {
+                        synchronized (mLock) {
+                            onBatterySaverModeChangedLocked(state.locationMode);
+                        }
+                    });
                 });
 
         new PackageMonitor() {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 1a842f7..b3a667a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3947,6 +3947,23 @@
                 case "ak.alizandro.smartaudiobookplayer": // b/129084042
                 case "com.campmobile.snow": // b/128803870
                 case "com.qnap.qfile": // b/126374406
+                case "com.google.android.apps.photos": // b/125506293
+                case "com.facebook.mlite": // b/126561155
+                case "com.ss.android.ugc.trill": // b/126610656
+                case "com.instagram.android": // b/127526615
+                case "com.facebook.orca": // b/128255453
+                case "org.videolan.vlc": // b/128391743
+                case "vStudio.Android.Camera360": // b/128882110
+                case "com.twitter.android": // b/128948908
+                case "com.tumblr": // b/129022664
+                case "com.sina.weibo": // b/129029018
+                case "com.kwai.video": // b/129037235
+                case "com.fotoable.photocollage": // b/129236353
+                case "com.xvideostudio.videoeditor": // b/129247146
+                case "app.buzz.share": // b/129304005
+                case "com.ss.android.article.topbuzzvideo.en": // b/129303979
+                case "com.linecorp.b612.android": // b/129318512
+                case "com.google.android.GoogleCamera": // b/128326994
                     return true;
             }
         }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 73e0439..1c99316 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -79,6 +79,7 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.OptionalInt;
+import java.util.stream.Collectors;
 
 /**
  * Since phone process can be restarted, this class provides a centralized place
@@ -260,8 +261,7 @@
     static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
                         | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
-                        | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
+                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
 
     static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
@@ -822,7 +822,10 @@
                         }
                     }
                     if ((events & PhoneStateListener
-                            .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) {
+                            .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0
+                            && TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(
+                                    r.context, r.callerPid, r.callerUid, r.callingPackage,
+                            "listen_active_data_subid_change")) {
                         try {
                             r.callback.onActiveDataSubIdChanged(mActiveDataSubId);
                         } catch (RemoteException ex) {
@@ -1764,12 +1767,23 @@
             log("notifyActiveDataSubIdChanged: activeDataSubId=" + activeDataSubId);
         }
 
+        // Create a copy to prevent the IPC call while checking carrier privilege under the lock.
+        List<Record> copiedRecords;
         synchronized (mRecords) {
-            mActiveDataSubId = activeDataSubId;
+            copiedRecords = new ArrayList<>(mRecords);
+        }
+        mActiveDataSubId = activeDataSubId;
 
-            for (Record r : mRecords) {
-                if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)) {
+        // Filter the record that does not listen to this change or does not have the permission.
+        copiedRecords = copiedRecords.stream().filter(r -> r.matchPhoneStateListenerEvent(
+                PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)
+                && TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(
+                        mContext, r.callerPid, r.callerUid, r.callingPackage,
+                "notifyActiveDataSubIdChanged")).collect(Collectors.toCollection(ArrayList::new));
+
+        synchronized (mRecords) {
+            for (Record r : copiedRecords) {
+                if (mRecords.contains(r)) {
                     try {
                         r.callback.onActiveDataSubIdChanged(activeDataSubId);
                     } catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 8ccb6e2..9325d25 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -307,7 +307,6 @@
                     }
 
                     cancelJobToUpdateAdbKeyStore();
-                    mAdbKeyStore = null;
                     mConnectedKey = null;
                     break;
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 1751856..b759dd4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -20,8 +20,10 @@
 
 import android.app.ActivityThread;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Handler;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.OnPropertyChangedListener;
@@ -38,6 +40,7 @@
  * Settings constants that can modify the activity manager's behavior.
  */
 final class ActivityManagerConstants extends ContentObserver {
+    private static final String TAG = "ActivityManagerConstants";
 
     // Key names stored in the settings value.
     private static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time";
@@ -272,6 +275,16 @@
     // memory trimming.
     public int CUR_TRIM_CACHED_PROCESSES;
 
+    private static final long MIN_AUTOMATIC_HEAP_DUMP_PSS_THRESHOLD_BYTES = 100 * 1024; // 100 KB
+
+    private final boolean mSystemServerAutomaticHeapDumpEnabled;
+
+    /** Package to report to when the memory usage exceeds the limit. */
+    private final String mSystemServerAutomaticHeapDumpPackageName;
+
+    /** Byte limit for dump heap monitoring. */
+    private long mSystemServerAutomaticHeapDumpPssThresholdBytes;
+
     private static final Uri ACTIVITY_MANAGER_CONSTANTS_URI = Settings.Global.getUriFor(
                 Settings.Global.ACTIVITY_MANAGER_CONSTANTS);
 
@@ -286,6 +299,9 @@
                 Settings.Global.getUriFor(
                         Settings.Global.BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST);
 
+    private static final Uri ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI =
+            Settings.Global.getUriFor(Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS);
+
     private final OnPropertyChangedListener mOnDeviceConfigChangedListener =
             new OnPropertyChangedListener() {
                 @Override
@@ -296,9 +312,17 @@
                 }
             };
 
-    public ActivityManagerConstants(ActivityManagerService service, Handler handler) {
+    ActivityManagerConstants(Context context, ActivityManagerService service, Handler handler) {
         super(handler);
         mService = service;
+        mSystemServerAutomaticHeapDumpEnabled = Build.IS_DEBUGGABLE
+                && context.getResources().getBoolean(
+                com.android.internal.R.bool.config_debugEnableAutomaticSystemServerHeapDumps);
+        mSystemServerAutomaticHeapDumpPackageName = context.getPackageName();
+        mSystemServerAutomaticHeapDumpPssThresholdBytes = Math.max(
+                MIN_AUTOMATIC_HEAP_DUMP_PSS_THRESHOLD_BYTES,
+                context.getResources().getInteger(
+                        com.android.internal.R.integer.config_debugSystemServerPssThresholdBytes));
     }
 
     public void start(ContentResolver resolver) {
@@ -308,10 +332,17 @@
         mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_ENABLED_URI, false, this);
         mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST_URI,
                 false, this);
+        if (mSystemServerAutomaticHeapDumpEnabled) {
+            mResolver.registerContentObserver(ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI,
+                    false, this);
+        }
         updateConstants();
         updateActivityStartsLoggingEnabled();
         updateBackgroundActivityStartsEnabled();
         updateBackgroundActivityStartsPackageNamesWhitelist();
+        if (mSystemServerAutomaticHeapDumpEnabled) {
+            updateEnableAutomaticSystemServerHeapDumps();
+        }
         DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 ActivityThread.currentApplication().getMainExecutor(),
                 mOnDeviceConfigChangedListener);
@@ -343,6 +374,8 @@
             updateBackgroundActivityStartsEnabled();
         } else if (BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST_URI.equals(uri)) {
             updateBackgroundActivityStartsPackageNamesWhitelist();
+        } else if (ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI.equals(uri)) {
+            updateEnableAutomaticSystemServerHeapDumps();
         }
     }
 
@@ -450,6 +483,24 @@
         mPackageNamesWhitelistedForBgActivityStarts = newSet;
     }
 
+    private void updateEnableAutomaticSystemServerHeapDumps() {
+        if (!mSystemServerAutomaticHeapDumpEnabled) {
+            Slog.wtf(TAG,
+                    "updateEnableAutomaticSystemServerHeapDumps called when leak detection "
+                            + "disabled");
+            return;
+        }
+        // Monitoring is on by default, so if the setting hasn't been set by the user,
+        // monitoring should be on.
+        final boolean enabled = Settings.Global.getInt(mResolver,
+                Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS, 1) == 1;
+
+        // Setting the threshold to 0 stops the checking.
+        final long threshold = enabled ? mSystemServerAutomaticHeapDumpPssThresholdBytes : 0;
+        mService.setDumpHeapDebugLimit(null, 0, threshold,
+                mSystemServerAutomaticHeapDumpPackageName);
+    }
+
     private void updateMaxCachedProcesses() {
         String maxCachedProcessesFlag = DeviceConfig.getProperty(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MAX_CACHED_PROCESSES);
@@ -460,7 +511,7 @@
                     : mOverrideMaxCachedProcesses;
         } catch (NumberFormatException e) {
             // Bad flag value from Phenotype, revert to default.
-            Slog.e("ActivityManagerConstants",
+            Slog.e(TAG,
                     "Unable to parse flag for max_cached_processes: " + maxCachedProcessesFlag, e);
             CUR_MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 05ec954..f51589a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2288,7 +2288,8 @@
         mBatteryStatsService = null;
         mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null;
         mHandlerThread = handlerThread;
-        mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null;
+        mConstants = hasHandlerThread
+                ? new ActivityManagerConstants(mContext, this, mHandler) : null;
         final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
         mProcessList.init(this, activeUids);
         mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
@@ -2336,7 +2337,7 @@
         mProcStartHandlerThread.start();
         mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
 
-        mConstants = new ActivityManagerConstants(this, mHandler);
+        mConstants = new ActivityManagerConstants(mContext, this, mHandler);
         final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */);
         mProcessList.init(this, activeUids);
         mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
@@ -8835,7 +8836,6 @@
         mAtmInternal.updateTopComponentForFactoryTest();
 
         retrieveSettings();
-        final int currentUserId = mUserController.getCurrentUserId();
         mUgmInternal.onSystemReady();
 
         final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
@@ -8849,6 +8849,16 @@
         }
 
         if (goingCallback != null) goingCallback.run();
+        // Check the current user here as a user can be started inside goingCallback.run() from
+        // other system services.
+        final int currentUserId = mUserController.getCurrentUserId();
+        Slog.i(TAG, "Current user:" + currentUserId);
+        if (currentUserId != UserHandle.USER_SYSTEM && !mUserController.isSystemUserStarted()) {
+            // User other than system user has started. Make sure that system user is already
+            // started before switching user.
+            throw new RuntimeException("System user not started while current user is:"
+                    + currentUserId);
+        }
         traceLog.traceBegin("ActivityManagerStartApps");
         mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
                 Integer.toString(currentUserId), currentUserId);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 07c9cca..cc4116e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -956,15 +956,26 @@
             final int oldUserId = getCurrentUserId();
             if (oldUserId == userId) {
                 final UserState state = getStartedUserState(userId);
-                if (state != null && state.state == STATE_RUNNING_UNLOCKED) {
-                    // We'll skip all later code, so we must tell listener it's already unlocked.
-                    try {
-                        unlockListener.onFinished(userId, null);
-                    } catch (RemoteException ignore) {
-                        // Ignore.
+                if (state == null) {
+                    Slog.wtf(TAG, "Current user has no UserState");
+                    // continue starting.
+                } else {
+                    if (userId == UserHandle.USER_SYSTEM && state.state == STATE_BOOTING) {
+                        // system user start explicitly requested. should continue starting as it
+                        // is not in running state.
+                    } else {
+                        if (state.state == STATE_RUNNING_UNLOCKED) {
+                            // We'll skip all later code, so we must tell listener it's already
+                            // unlocked.
+                            try {
+                                unlockListener.onFinished(userId, null);
+                            } catch (RemoteException ignore) {
+                                // Ignore.
+                            }
+                        }
+                        return true;
                     }
                 }
-                return true;
             }
 
             if (foreground) {
@@ -1743,6 +1754,24 @@
         return state.state != UserState.STATE_STOPPING && state.state != UserState.STATE_SHUTDOWN;
     }
 
+    /**
+     * Check if system user is already started. Unlike other user, system user is in STATE_BOOTING
+     * even if it is not explicitly started. So isUserRunning cannot give the right state
+     * to check if system user is started or not.
+     * @return true if system user is started.
+     */
+    boolean isSystemUserStarted() {
+        synchronized (mLock) {
+            UserState uss = mStartedUsers.get(UserHandle.USER_SYSTEM);
+            if (uss == null) {
+                return false;
+            }
+            return uss.state == UserState.STATE_RUNNING_LOCKED
+                || uss.state == UserState.STATE_RUNNING_UNLOCKING
+                || uss.state == UserState.STATE_RUNNING_UNLOCKED;
+        }
+    }
+
     UserInfo getCurrentUser() {
         if ((mInjector.checkCallingPermission(INTERACT_ACROSS_USERS)
                 != PackageManager.PERMISSION_GRANTED) && (
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index b8810c8f..2df8982 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -24,6 +24,7 @@
 import android.hardware.radio.ITuner;
 import android.hardware.radio.ITunerCallback;
 import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioTuner;
 import android.hidl.manager.V1_0.IServiceManager;
 import android.hidl.manager.V1_0.IServiceNotification;
 import android.os.IHwBinder.DeathRecipient;
@@ -49,9 +50,17 @@
     @GuardedBy("mLock")
     private final Map<String, Integer> mServiceNameToModuleIdMap = new HashMap<>();
 
+    // Map from module ID to RadioModule created by mServiceListener.onRegistration().
     @GuardedBy("mLock")
     private final Map<Integer, RadioModule> mModules = new HashMap<>();
 
+    // Map from module ID to TunerSession created by openSession().
+    //
+    // Because this service currently implements a 1 AIDL to 1 HAL policy, mTunerSessions is used to
+    // enforce the "aggresive open" policy mandated for IBroadcastRadio.openSession(). In the
+    // future, this solution will be replaced with a multiple-AIDL to 1 HAL implementation.
+    private final Map<Integer, TunerSession> mTunerSessions = new HashMap<>();
+
     private IServiceNotification.Stub mServiceListener = new IServiceNotification.Stub() {
         @Override
         public void onRegistration(String fqName, String serviceName, boolean preexisting) {
@@ -72,6 +81,7 @@
                 }
                 Slog.v(TAG, "loaded broadcast radio module " + moduleId + ": " + serviceName
                         + " (HAL 2.0)");
+                closeTunerSessionLocked(moduleId);
                 mModules.put(moduleId, module);
 
                 if (newService) {
@@ -96,6 +106,7 @@
             synchronized (mLock) {
                 int moduleId = (int) cookie;
                 mModules.remove(moduleId);
+                closeTunerSessionLocked(moduleId);
 
                 for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) {
                     if (entry.getValue() == moduleId) {
@@ -152,16 +163,20 @@
         RadioModule module = null;
         synchronized (mLock) {
             module = mModules.get(moduleId);
-        }
-        if (module == null) {
-            throw new IllegalArgumentException("Invalid module ID");
+            if (module == null) {
+                throw new IllegalArgumentException("Invalid module ID");
+            }
+            closeTunerSessionLocked(moduleId);
         }
 
-        TunerSession session = module.openSession(callback);
-        if (legacyConfig != null) {
-            session.setConfiguration(legacyConfig);
+        TunerSession tunerSession = module.openSession(callback);
+        synchronized (mLock) {
+            mTunerSessions.put(moduleId, tunerSession);
         }
-        return session;
+        if (legacyConfig != null) {
+            tunerSession.setConfiguration(legacyConfig);
+        }
+        return tunerSession;
     }
 
     public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
@@ -183,4 +198,12 @@
         }
         return aggregator;
     }
+
+    private void closeTunerSessionLocked(int moduleId) {
+        TunerSession tunerSession = mTunerSessions.remove(moduleId);
+        if (tunerSession != null) {
+            Slog.d(TAG, "Closing previous TunerSession");
+            tunerSession.close(RadioTuner.ERROR_HARDWARE_FAILURE);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 9833507..05ca144 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -17,6 +17,7 @@
 package com.android.server.broadcastradio.hal2;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Bitmap;
 import android.hardware.broadcastradio.V2_0.ConfigFlag;
 import android.hardware.broadcastradio.V2_0.ITunerSession;
@@ -58,8 +59,22 @@
 
     @Override
     public void close() {
+        close(null);
+    }
+
+    /**
+     * Closes the TunerSession. If error is non-null, the client's onError() callback is invoked
+     * first with the specified error, see {@link
+     * android.hardware.radio.RadioTuner.Callback#onError}.
+     *
+     * @param error Optional error to send to client before session is closed.
+     */
+    public void close(@Nullable Integer error) {
         synchronized (mLock) {
             if (mIsClosed) return;
+            if (error != null) {
+                TunerCallback.dispatch(() -> mCallback.mClientCb.onError(error));
+            }
             mIsClosed = true;
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 9986809..b140c1b 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -409,21 +409,25 @@
     }
 
     private int setWifiTethering(final boolean enable) {
-        int rval = TETHER_ERROR_MASTER_ERROR;
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mPublicSync) {
-                mWifiTetherRequested = enable;
                 final WifiManager mgr = getWifiManager();
+                if (mgr == null) {
+                    mLog.e("setWifiTethering: failed to get WifiManager!");
+                    return TETHER_ERROR_SERVICE_UNAVAIL;
+                }
                 if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) ||
                     (!enable && mgr.stopSoftAp())) {
-                    rval = TETHER_ERROR_NO_ERROR;
+                    mWifiTetherRequested = enable;
+                    return TETHER_ERROR_NO_ERROR;
                 }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        return rval;
+
+        return TETHER_ERROR_MASTER_ERROR;
     }
 
     private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
@@ -942,6 +946,11 @@
     public int setUsbTethering(boolean enable) {
         if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
         UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+        if (usbManager == null) {
+            mLog.e("setUsbTethering: failed to get UsbManager!");
+            return TETHER_ERROR_SERVICE_UNAVAIL;
+        }
+
         synchronized (mPublicSync) {
             usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS
                     : UsbManager.FUNCTION_NONE);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index a2882de..9e2fd4e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -74,7 +74,15 @@
     @GuardedBy("mLock")
     private boolean mSystemAudioControlFeatureEnabled;
 
-    private boolean mTvSystemAudioModeSupport;
+    /**
+     * Indicates if the TV that the current device is connected to supports System Audio Mode or not
+     *
+     * <p>If the current device has no information on this, keep mTvSystemAudioModeSupport null
+     *
+     * <p>The boolean will be reset to null every time when the current device goes to standby
+     * or loses its physical address.
+     */
+    private Boolean mTvSystemAudioModeSupport = null;
 
     // Whether ARC is available or not. "true" means that ARC is established between TV and
     // AVR as audio receiver.
@@ -321,7 +329,7 @@
     @ServiceThreadOnly
     protected void onStandby(boolean initiatedByCec, int standbyAction) {
         assertRunOnServiceThread();
-        mTvSystemAudioModeSupport = false;
+        mTvSystemAudioModeSupport = null;
         // Record the last state of System Audio Control before going to standby
         synchronized (mLock) {
             mService.writeStringSystemProperty(
@@ -1029,12 +1037,11 @@
      * <p>The result of the query may be cached until Audio device type is put in standby or loses
      * its physical address.
      */
-    // TODO(amyjojo): making mTvSystemAudioModeSupport null originally and fix the logic.
     void queryTvSystemAudioModeSupport(TvSystemAudioModeSupportedCallback callback) {
-        if (!mTvSystemAudioModeSupport) {
+        if (mTvSystemAudioModeSupport == null) {
             addAndStartAction(new DetectTvSystemAudioModeSupportAction(this, callback));
         } else {
-            callback.onResult(true);
+            callback.onResult(mTvSystemAudioModeSupport);
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 4cc08d8..4ed24ec 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1291,13 +1291,11 @@
                 lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL,
                         stats.naturalImportance);
             }
-            // Log Assistant override if it was itself overridden by System. Since System can't be
-            // overridden, it never needs logging.
-            if (mImportanceExplanationCode == MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM
-                    && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
-                lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST,
+        }
+        // Log Assistant override if present, whether or not importance calculation is complete.
+        if (mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
+            lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST,
                         mAssistantImportance);
-            }
         }
         return lm;
     }
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 37dd63a..13ff873 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -422,8 +422,6 @@
                         final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
                         if (oi != null) {
                             mImpl.onOverlayPackageUpgrading(packageName, userId);
-                        } else {
-                            mImpl.onTargetPackageUpgrading(packageName, userId);
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 15ed063..a3d6380 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -22,7 +22,6 @@
 import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
 import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
 import static android.content.om.OverlayInfo.STATE_OVERLAY_UPGRADING;
-import static android.content.om.OverlayInfo.STATE_TARGET_UPGRADING;
 
 import static com.android.server.om.OverlayManagerService.DEBUG;
 import static com.android.server.om.OverlayManagerService.TAG;
@@ -30,12 +29,15 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.om.OverlayInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -54,8 +56,14 @@
  * @see OverlayManagerService
  */
 final class OverlayManagerServiceImpl {
-    // Flags to use in conjunction with updateState.
+
+    /**
+     * @deprecated Not used. See {@link android.content.om.OverlayInfo#STATE_TARGET_UPGRADING}.
+     */
+    @Deprecated
     private static final int FLAG_TARGET_IS_UPGRADING = 1 << 0;
+
+    // Flags to use in conjunction with updateState.
     private static final int FLAG_OVERLAY_IS_UPGRADING = 1 << 1;
 
     private final PackageManagerHelper mPackageManager;
@@ -247,9 +255,7 @@
             Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
         }
 
-        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
-            mListener.onOverlaysChanged(packageName, userId);
-        }
+        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
     void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
@@ -257,16 +263,7 @@
             Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAllOverlaysForTarget(packageName, userId, 0);
-    }
-
-    void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
-        if (DEBUG) {
-            Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId="
-                    + userId);
-        }
-
-        updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING);
+        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
     void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) {
@@ -274,7 +271,7 @@
             Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAllOverlaysForTarget(packageName, userId, 0);
+        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
     void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
@@ -282,22 +279,27 @@
             Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
         }
 
-        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
-            mListener.onOverlaysChanged(packageName, userId);
-        }
+        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
     /**
      * Update the state of any overlays for this target.
-     *
-     * Returns true if the system should refresh the app's overlay paths (i.e.
-     * if the settings were modified for this target, or there is at least one
-     * enabled framework overlay).
      */
-    private boolean updateAllOverlaysForTarget(@NonNull final String targetPackageName,
+    private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
             final int userId, final int flags) {
+        final List<OverlayInfo> ois = new ArrayList<>();
+
+        // Framework overlays added first because order matters when resolving a resource
+        if (!"android".equals(targetPackageName)) {
+            ois.addAll(mSettings.getOverlaysForTarget("android", userId));
+        }
+
+        // Then add the targeted, non-framework overlays which have higher priority
+        ois.addAll(mSettings.getOverlaysForTarget(targetPackageName, userId));
+
+        final List<String> enabledBaseCodePaths = new ArrayList<>(ois.size());
+
         boolean modified = false;
-        final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
         final int n = ois.size();
         for (int i = 0; i < n; i++) {
             final OverlayInfo oi = ois.get(i);
@@ -313,13 +315,35 @@
                     Slog.e(TAG, "failed to update settings", e);
                     modified |= mSettings.remove(oi.packageName, userId);
                 }
+
+                if (oi.isEnabled() && overlayPackage.applicationInfo != null) {
+                    enabledBaseCodePaths.add(overlayPackage.applicationInfo.getBaseCodePath());
+                }
             }
         }
 
-        // check for enabled framework overlays
-        modified = modified || !getEnabledOverlayPackageNames("android", userId).isEmpty();
+        if (!modified) {
+            PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, userId);
+            ApplicationInfo appInfo = packageInfo == null ? null : packageInfo.applicationInfo;
+            String[] resourceDirs = appInfo == null ? null : appInfo.resourceDirs;
 
-        return modified;
+            // If the lists aren't the same length, the enabled overlays have changed
+            if (ArrayUtils.size(resourceDirs) != enabledBaseCodePaths.size()) {
+                modified = true;
+            } else if (resourceDirs != null) {
+                // If any element isn't equal, an overlay or the order of overlays has changed
+                for (int index = 0; index < resourceDirs.length; index++) {
+                    if (!resourceDirs[index].equals(enabledBaseCodePaths.get(index))) {
+                        modified = true;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (modified) {
+            mListener.onOverlaysChanged(targetPackageName, userId);
+        }
     }
 
     void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
@@ -670,10 +694,6 @@
             @Nullable final PackageInfo overlayPackage, final int userId, final int flags)
             throws OverlayManagerSettings.BadKeyException {
 
-        if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) {
-            return STATE_TARGET_UPGRADING;
-        }
-
         if ((flags & FLAG_OVERLAY_IS_UPGRADING) != 0) {
             return STATE_OVERLAY_UPGRADING;
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index afa5ae9..ce3c452 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -396,6 +396,11 @@
         } finally {
             IoUtils.closeQuietly(fis);
         }
+        // After all of the sessions were loaded, they are ready to be sealed and validated
+        for (int i = 0; i < mSessions.size(); ++i) {
+            PackageInstallerSession session = mSessions.valueAt(i);
+            session.sealAndValidateIfNecessary();
+        }
     }
 
     @GuardedBy("mSessions")
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f1d4524..5d539a4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -231,6 +231,8 @@
     @GuardedBy("mLock")
     private boolean mSealed = false;
     @GuardedBy("mLock")
+    private boolean mShouldBeSealed = false;
+    @GuardedBy("mLock")
     private boolean mCommitted = false;
     @GuardedBy("mLock")
     private boolean mRelinquished = false;
@@ -430,6 +432,7 @@
         this.updatedMillis = createdMillis;
         this.stageDir = stageDir;
         this.stageCid = stageCid;
+        this.mShouldBeSealed = sealed;
         if (childSessionIds != null) {
             for (int childSessionId : childSessionIds) {
                 mChildSessionIds.put(childSessionId, 0);
@@ -450,16 +453,6 @@
         mStagedSessionErrorCode = stagedSessionErrorCode;
         mStagedSessionErrorMessage =
                 stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
-        if (sealed) {
-            synchronized (mLock) {
-                try {
-                    sealAndValidateLocked();
-                } catch (PackageManagerException | IOException e) {
-                    destroyInternal();
-                    throw new IllegalArgumentException(e);
-                }
-            }
-        }
     }
 
     public SessionInfo generateInfo() {
@@ -932,6 +925,8 @@
             @NonNull IntentSender statusReceiver, boolean forTransfer) {
         Preconditions.checkNotNull(statusReceiver);
 
+        List<PackageInstallerSession> childSessions = getChildSessions();
+
         final boolean wasSealed;
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
@@ -963,7 +958,7 @@
             wasSealed = mSealed;
             if (!mSealed) {
                 try {
-                    sealAndValidateLocked();
+                    sealAndValidateLocked(childSessions);
                 } catch (IOException e) {
                     throw new IllegalArgumentException(e);
                 } catch (PackageManagerException e) {
@@ -994,21 +989,91 @@
         return true;
     }
 
+    /** Return a list of child sessions or null if the session is not multipackage
+     *
+     * <p> This method is handy to prevent potential deadlocks (b/123391593)
+     */
+    private @Nullable List<PackageInstallerSession> getChildSessions() {
+        List<PackageInstallerSession> childSessions = null;
+        if (isMultiPackage()) {
+            final int[] childSessionIds = getChildSessionIds();
+            childSessions = new ArrayList<>(childSessionIds.length);
+            for (int childSessionId : childSessionIds) {
+                childSessions.add(mSessionProvider.getSession(childSessionId));
+            }
+        }
+        return childSessions;
+    }
+
+    /**
+     * Assert multipackage install has consistent sessions.
+     *
+     * @throws PackageManagerException if child sessions don't match parent session
+     *                                  in respect to staged and enable rollback parameters.
+     */
+    @GuardedBy("mLock")
+    private void assertMultiPackageConsistencyLocked(
+            @NonNull List<PackageInstallerSession> childSessions) throws PackageManagerException {
+        for (PackageInstallerSession childSession : childSessions) {
+            // It might be that the parent session is loaded before all of it's child sessions are,
+            // e.g. when reading sessions from XML. Those sessions will be null here, and their
+            // conformance with the multipackage params will be checked when they're loaded.
+            if (childSession == null) {
+                continue;
+            }
+            assertConsistencyWithLocked(childSession);
+        }
+    }
+
+    /**
+     * Assert consistency with the given session.
+     *
+     * @throws PackageManagerException if other sessions doesn't match this session
+     *                                  in respect to staged and enable rollback parameters.
+     */
+    @GuardedBy("mLock")
+    private void assertConsistencyWithLocked(PackageInstallerSession other)
+            throws PackageManagerException {
+        // Session groups must be consistent wrt to isStaged parameter. Non-staging session
+        // cannot be grouped with staging sessions.
+        if (this.params.isStaged != other.params.isStaged) {
+            throw new PackageManagerException(
+                PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY,
+                "Multipackage Inconsistency: session " + other.sessionId
+                    + " and session " + sessionId
+                    + " have inconsistent staged settings");
+        }
+        if (this.params.getEnableRollback() != other.params.getEnableRollback()) {
+            throw new PackageManagerException(
+                PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY,
+                "Multipackage Inconsistency: session " + other.sessionId
+                    + " and session " + sessionId
+                    + " have inconsistent rollback settings");
+        }
+    }
+
     /**
      * Seal the session to prevent further modification and validate the contents of it.
      *
      * <p>The session will be sealed after calling this method even if it failed.
      *
+     * @param childSessions the child sessions of a multipackage that will be checked for
+     *                      consistency. Can be null if session is not multipackage.
      * @throws PackageManagerException if the session was sealed but something went wrong. If the
      *                                 session was sealed this is the only possible exception.
      */
     @GuardedBy("mLock")
-    private void sealAndValidateLocked() throws PackageManagerException, IOException {
+    private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
+            throws PackageManagerException, IOException {
         assertNoWriteFileTransfersOpenLocked();
         assertPreparedAndNotDestroyedLocked("sealing of session");
 
         mSealed = true;
 
+        if (childSessions != null) {
+            assertMultiPackageConsistencyLocked(childSessions);
+        }
+
         if (params.isStaged) {
             final PackageInstallerSession activeSession = mStagingManager.getActiveSession();
             final boolean anotherSessionAlreadyInProgress =
@@ -1048,6 +1113,38 @@
         }
     }
 
+    /**
+     * If session should be sealed, then it's sealed to prevent further modification
+     * and then it's validated.
+     *
+     * If the session was sealed but something went wrong then it's destroyed.
+     *
+     * <p> This is meant to be called after all of the sessions are loaded and added to
+     * PackageInstallerService
+     */
+    void sealAndValidateIfNecessary() {
+        synchronized (mLock) {
+            if (!mShouldBeSealed) {
+                return;
+            }
+        }
+        List<PackageInstallerSession> childSessions = getChildSessions();
+        synchronized (mLock) {
+            try {
+                sealAndValidateLocked(childSessions);
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            } catch (PackageManagerException e) {
+                Slog.e(TAG, "Package not valid", e);
+                // Session is sealed but could not be verified, we need to destroy it.
+                destroyInternal();
+                // Dispatch message to remove session from PackageInstallerService
+                dispatchSessionFinished(
+                        e.error, ExceptionUtils.getCompleteMessage(e), null);
+            }
+        }
+    }
+
     /** Update the timestamp of when the staged session last changed state */
     public void markUpdated() {
         synchronized (mLock) {
@@ -1076,12 +1173,14 @@
             throw new SecurityException("Can only transfer sessions that use public options");
         }
 
+        List<PackageInstallerSession> childSessions = getChildSessions();
+
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("transfer");
 
             try {
-                sealAndValidateLocked();
+                sealAndValidateLocked(childSessions);
             } catch (IOException e) {
                 throw new IllegalStateException(e);
             } catch (PackageManagerException e) {
@@ -1132,14 +1231,7 @@
         // outside of the lock, because reading the child
         // sessions with the lock held could lead to deadlock
         // (b/123391593).
-        List<PackageInstallerSession> childSessions = null;
-        if (isMultiPackage()) {
-            final int[] childSessionIds = getChildSessionIds();
-            childSessions = new ArrayList<>(childSessionIds.length);
-            for (int childSessionId : childSessionIds) {
-                childSessions.add(mSessionProvider.getSession(childSessionId));
-            }
-        }
+        List<PackageInstallerSession> childSessions = getChildSessions();
 
         try {
             synchronized (mLock) {
@@ -1965,15 +2057,6 @@
                             + " does not exist"),
                     false, true).rethrowAsRuntimeException();
         }
-        // Session groups must be consistent wrt to isStaged parameter. Non-staging session
-        // cannot be grouped with staging sessions.
-        if (this.params.isStaged ^ childSession.params.isStaged) {
-            throw new RemoteException("Unable to add child.",
-                    new PackageManagerException("Child session " + childSessionId
-                            + " and parent session " + this.sessionId + " do not have consistent"
-                            + " staging session settings."),
-                    false, true);
-        }
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("addChildSessionId");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3833afc..3cab9e5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -20272,6 +20272,11 @@
         return mContext.getString(R.string.config_defaultTextClassifierPackage);
     }
 
+    @Override
+    public String getAttentionServicePackageName() {
+        return mContext.getString(R.string.config_defaultAttentionService);
+    }
+
     private @Nullable String getDocumenterPackageName() {
         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
         intent.addCategory(Intent.CATEGORY_OPENABLE);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 096335e..6a1f223 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4383,6 +4383,7 @@
             ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION",
             ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE",
             ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, "ALLOW_AUDIO_PLAYBACK_CAPTURE",
+            ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX, "ALLOW_EXTERNAL_STORAGE_SANDBOX",
             ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND",
             ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
             ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e6e2c76..1833200 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -740,6 +740,14 @@
                     LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
         }
 
+        // Atthention Service
+        String attentionServicePackageName =
+                mContext.getPackageManager().getAttentionServicePackageName();
+        if (!TextUtils.isEmpty(attentionServicePackageName)) {
+            grantPermissionsToSystemPackage(attentionServicePackageName, userId,
+                    CAMERA_PERMISSIONS);
+        }
+
         // There is no real "marker" interface to identify the shared storage backup, it is
         // hardcoded in BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE.
         grantSystemFixedPermissionsToSystemPackage("com.android.sharedstoragebackup", userId,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d4d752f..88109d3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2223,6 +2223,11 @@
 
     @Override
     public boolean canBeHiddenByKeyguardLw(WindowState win) {
+
+        // Keyguard visibility of window from activities are determined over activity visibility.
+        if (win.getAppToken() != null) {
+            return false;
+        }
         switch (win.getAttrs().type) {
             case TYPE_STATUS_BAR:
             case TYPE_NAVIGATION_BAR:
@@ -2236,19 +2241,30 @@
     }
 
     private boolean shouldBeHiddenByKeyguard(WindowState win, WindowState imeTarget) {
+        final LayoutParams attrs = win.getAttrs();
 
-        // Keyguard visibility of window from activities are determined over activity visibility.
-        if (win.getAppToken() != null) {
-            return false;
+        boolean hideDockDivider = attrs.type == TYPE_DOCK_DIVIDER
+                && !mWindowManagerInternal.isStackVisibleLw(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        if (hideDockDivider) {
+            return true;
         }
 
-        final LayoutParams attrs = win.getAttrs();
+        // If AOD is showing, the IME should be hidden. However, sometimes the AOD is considered
+        // hidden because it's in the process of hiding, but it's still being shown on screen.
+        // In that case, we want to continue hiding the IME until the windows have completed
+        // drawing. This way, we know that the IME can be safely shown since the other windows are
+        // now shown.
+        final boolean hideIme = win.isInputMethodWindow()
+                && (mAodShowing || !mDefaultDisplayPolicy.isWindowManagerDrawComplete());
+        if (hideIme) {
+            return true;
+        }
+
         final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleLw()
                 && (imeTarget.canShowWhenLocked() || !canBeHiddenByKeyguardLw(imeTarget));
 
         // Show IME over the keyguard if the target allows it
-        boolean allowWhenLocked = (win.isInputMethodWindow() || imeTarget == this)
-                && showImeOverKeyguard;
+        boolean allowWhenLocked = win.isInputMethodWindow() && showImeOverKeyguard;
 
         final boolean isKeyguardShowing = mKeyguardDelegate.isShowing();
 
@@ -2259,17 +2275,7 @@
                     || (attrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0;
         }
 
-        boolean hideDockDivider = attrs.type == TYPE_DOCK_DIVIDER
-                && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        // If AOD is showing, the IME should be hidden. However, sometimes the AOD is considered
-        // hidden because it's in the process of hiding, but it's still being shown on screen.
-        // In that case, we want to continue hiding the IME until the windows have completed
-        // drawing. This way, we know that the IME can be safely shown since the other windows are
-        // now shown.
-        final boolean hideIme = win.isInputMethodWindow()
-                && (mAodShowing || !mDefaultDisplayPolicy.isWindowManagerDrawComplete());
-        return (isKeyguardShowing && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY)
-                || hideDockDivider || hideIme;
+        return isKeyguardShowing && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY;
     }
 
     /** {@inheritDoc} */
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index f23b68e..38bdc62 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -594,7 +594,7 @@
                 boolean forceBackgroundCheck,
                 int locationMode) {
 
-            this.adjustBrightnessFactor = adjustBrightnessFactor;
+            this.adjustBrightnessFactor = Math.min(1, Math.max(0, adjustBrightnessFactor));
             this.advertiseIsEnabled = advertiseIsEnabled;
             this.deferFullBackup = deferFullBackup;
             this.deferKeyValueBackup = deferKeyValueBackup;
@@ -613,7 +613,14 @@
             this.filesForNoninteractive = filesForNoninteractive;
             this.forceAllAppsStandby = forceAllAppsStandby;
             this.forceBackgroundCheck = forceBackgroundCheck;
-            this.locationMode = locationMode;
+
+            if (locationMode < PowerManager.MIN_LOCATION_MODE
+                    || PowerManager.MAX_LOCATION_MODE < locationMode) {
+                Slog.e(TAG, "Invalid location mode: " + locationMode);
+                this.locationMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+            } else {
+                this.locationMode = locationMode;
+            }
 
             mHashCode = Objects.hash(
                     adjustBrightnessFactor,
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 0c9f815..c2a4339 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1719,6 +1719,7 @@
                     final WallpaperData systemWallpaper =
                             getWallpaperSafeLocked(userId, FLAG_SYSTEM);
                     switchWallpaper(systemWallpaper, null);
+                    notifyCallbacksLocked(systemWallpaper);
                 }
 
                 // Make sure that the SELinux labeling of all the relevant files is correct.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5bbabfc..a93bdba 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1102,7 +1102,7 @@
                     ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this)
                             * 1000000L, fullscreen,
                     (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, appInfo.targetSdkVersion,
-                    info.screenOrientation, mRotationAnimationHint, info.configChanges,
+                    info.screenOrientation, mRotationAnimationHint,
                     mLaunchTaskBehind, isAlwaysFocusable());
             if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) {
                 Slog.v(TAG, "addAppToken: "
@@ -1151,11 +1151,11 @@
     AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
             boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
             boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
-            int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
+            int rotationAnimationHint, boolean launchTaskBehind,
             boolean alwaysFocusable) {
         return new AppWindowToken(service, token, mActivityComponent, voiceInteraction, dc,
                 inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
-                rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
+                rotationAnimationHint, launchTaskBehind, alwaysFocusable,
                 this);
     }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index c1b9bba..a53f85d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -153,7 +153,6 @@
 
     /** @see WindowContainer#fillsParent() */
     private boolean mFillsParent;
-    boolean layoutConfigChanges;
     boolean mShowForAllUsers;
     int mTargetSdk;
 
@@ -337,7 +336,7 @@
     AppWindowToken(WindowManagerService service, IApplicationToken token,
             ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
             long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
-            int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
+            int targetSdk, int orientation, int rotationAnimationHint,
             boolean launchTaskBehind, boolean alwaysFocusable,
             ActivityRecord activityRecord) {
         this(service, token, activityComponent, voiceInteraction, dc, fullscreen);
@@ -348,7 +347,6 @@
         mShowForAllUsers = showForAllUsers;
         mTargetSdk = targetSdk;
         mOrientation = orientation;
-        layoutConfigChanges = (configChanges & (CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION)) != 0;
         mLaunchTaskBehind = launchTaskBehind;
         mAlwaysFocusable = alwaysFocusable;
         mRotationAnimationHint = rotationAnimationHint;
@@ -1976,7 +1974,7 @@
         final boolean surfaceReady = w.isDrawnLw()  // Regular case
                 || w.mWinAnimator.mSurfaceDestroyDeferred  // The preserved surface is still ready.
                 || w.isDragResizeChanged();  // Waiting for relayoutWindow to call preserveSurface.
-        final boolean needsLetterbox = w.isLetterboxedAppWindow() && fillsParent() && surfaceReady;
+        final boolean needsLetterbox = surfaceReady && w.isLetterboxedAppWindow() && fillsParent();
         if (needsLetterbox) {
             if (mLetterbox == null) {
                 mLetterbox = new Letterbox(() -> makeChildSurface(null));
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 9bc8462..3bbe28d 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -173,8 +173,9 @@
         }
         final boolean wasVis = mWin.isVisibleLw();
         final boolean wasAnim = mWin.isAnimatingLw();
-        final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnimation())
-                : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnimation());
+        final boolean skipAnim = skipAnimation();
+        final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnim)
+                : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnim);
         mNoAnimationOnNextShow = false;
         final int state = computeStateLw(wasVis, wasAnim, mWin, change);
         final boolean stateChanged = updateStateLw(state);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0c34e25..77055c1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -28,6 +28,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_PRIVATE;
 import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
@@ -655,7 +656,7 @@
         if (DEBUG_LAYOUT && !w.mLayoutAttached) {
             Slog.v(TAG, "1ST PASS " + w + ": gone=" + gone + " mHaveFrame=" + w.mHaveFrame
                     + " mLayoutAttached=" + w.mLayoutAttached
-                    + " screen changed=" + w.isConfigChanged());
+                    + " config reported=" + w.isLastConfigReportedToClient());
             final AppWindowToken atoken = w.mAppToken;
             if (gone) Slog.v(TAG, "  GONE: mViewVisibility=" + w.mViewVisibility
                     + " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.isHidden()
@@ -670,42 +671,34 @@
         // If this view is GONE, then skip it -- keep the current frame, and let the caller know
         // so they can ignore it if they want.  (We do the normal layout for INVISIBLE windows,
         // since that means "perform layout as normal, just don't display").
-        if (!gone || !w.mHaveFrame || w.mLayoutNeeded
-                || ((w.isConfigChanged() || w.setReportResizeHints())
-                && !w.isGoneForLayoutLw() &&
-                ((w.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
-                        (w.mHasSurface && w.mAppToken != null &&
-                                w.mAppToken.layoutConfigChanges)))) {
-            if (!w.mLayoutAttached) {
-                if (mTmpInitial) {
-                    //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
-                    w.resetContentChanged();
-                }
-                if (w.mAttrs.type == TYPE_DREAM) {
-                    // Don't layout windows behind a dream, so that if it does stuff like hide
-                    // the status bar we won't get a bad transition when it goes away.
-                    mTmpWindow = w;
-                }
-                w.mLayoutNeeded = false;
-                w.prelayout();
-                final boolean firstLayout = !w.isLaidOut();
-                getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
-                w.mLayoutSeq = mLayoutSeq;
-
-                // If this is the first layout, we need to initialize the last inset values as
-                // otherwise we'd immediately cause an unnecessary resize.
-                if (firstLayout) {
-                    w.updateLastInsetValues();
-                }
-
-                if (w.mAppToken != null) {
-                    w.mAppToken.layoutLetterbox(w);
-                }
-
-                if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.getFrameLw()
-                        + " mContainingFrame=" + w.getContainingFrame()
-                        + " mDisplayFrame=" + w.getDisplayFrameLw());
+        if ((!gone || !w.mHaveFrame || w.mLayoutNeeded) && !w.mLayoutAttached) {
+            if (mTmpInitial) {
+                w.resetContentChanged();
             }
+            if (w.mAttrs.type == TYPE_DREAM) {
+                // Don't layout windows behind a dream, so that if it does stuff like hide
+                // the status bar we won't get a bad transition when it goes away.
+                mTmpWindow = w;
+            }
+            w.mLayoutNeeded = false;
+            w.prelayout();
+            final boolean firstLayout = !w.isLaidOut();
+            getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
+            w.mLayoutSeq = mLayoutSeq;
+
+            // If this is the first layout, we need to initialize the last inset values as
+            // otherwise we'd immediately cause an unnecessary resize.
+            if (firstLayout) {
+                w.updateLastInsetValues();
+            }
+
+            if (w.mAppToken != null) {
+                w.mAppToken.layoutLetterbox(w);
+            }
+
+            if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.getFrameLw()
+                    + " mContainingFrame=" + w.getContainingFrame()
+                    + " mDisplayFrame=" + w.getDisplayFrameLw());
         }
     };
 
@@ -3094,7 +3087,7 @@
 
     /** Updates the layer assignment of windows on this display. */
     void assignWindowLayers(boolean setLayoutNeeded) {
-        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
         assignChildLayers(getPendingTransaction());
         if (setLayoutNeeded) {
             setLayoutNeeded();
@@ -3105,7 +3098,7 @@
         // prepareSurfaces. This allows us to synchronize Z-ordering changes with
         // the hiding and showing of surfaces.
         scheduleAnimation();
-        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
     // TODO: This should probably be called any time a visual change is made to the hierarchy like
@@ -3659,9 +3652,14 @@
             // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think it is animating.
             pendingLayoutChanges = 0;
 
-            mDisplayPolicy.beginPostLayoutPolicyLw();
-            forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
-            pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyPostLayoutPolicy");
+            try {
+                mDisplayPolicy.beginPostLayoutPolicyLw();
+                forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
+                pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
+            } finally {
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+            }
             if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
                     "after finishPostLayoutPolicyLw", pendingLayoutChanges);
                 mInsetsStateController.onPostLayout();
@@ -3670,7 +3668,13 @@
         mTmpApplySurfaceChangesTransactionState.reset();
 
         mTmpRecoveringMemory = recoveringMemory;
-        forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
+
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
+        try {
+            forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        }
         prepareSurfaces();
 
         mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
@@ -3720,11 +3724,6 @@
         out.set(left, top, left + width, top + height);
     }
 
-    @Override
-    public void getBounds(Rect out) {
-        calculateBounds(mDisplayInfo, out);
-    }
-
     private void getBounds(Rect out, int orientation) {
         getBounds(out);
 
@@ -3746,6 +3745,15 @@
     }
 
     void performLayout(boolean initial, boolean updateInputWindows) {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
+        try {
+            performLayoutNoTrace(initial, updateInputWindows);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+
+    private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
         if (!isLayoutNeeded()) {
             return;
         }
@@ -3755,13 +3763,14 @@
         final int dh = mDisplayInfo.logicalHeight;
         if (DEBUG_LAYOUT) {
             Slog.v(TAG, "-------------------------------------");
-            Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
+            Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw
+                    + " dh=" + dh);
         }
 
         mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                 calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
-        // TODO: Not sure if we really need to set the rotation here since we are updating from the
-        // display info above...
+        // TODO: Not sure if we really need to set the rotation here since we are updating from
+        // the display info above...
         mDisplayFrames.mRotation = mRotation;
         mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
 
@@ -4801,20 +4810,25 @@
 
     @Override
     void prepareSurfaces() {
-        final ScreenRotationAnimation screenRotationAnimation =
-                mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
-        if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
-            screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
-            mPendingTransaction.setMatrix(mWindowingLayer,
-                    mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
-                    mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
-            mPendingTransaction.setPosition(mWindowingLayer,
-                    mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
-            mPendingTransaction.setAlpha(mWindowingLayer,
-                    screenRotationAnimation.getEnterTransformation().getAlpha());
-        }
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
+        try {
+            final ScreenRotationAnimation screenRotationAnimation =
+                    mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+            if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
+                screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
+                mPendingTransaction.setMatrix(mWindowingLayer,
+                        mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
+                        mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
+                mPendingTransaction.setPosition(mWindowingLayer,
+                        mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
+                mPendingTransaction.setAlpha(mWindowingLayer,
+                        screenRotationAnimation.getEnterTransformation().getAlpha());
+            }
 
-        super.prepareSurfaces();
+            super.prepareSurfaces();
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        }
     }
 
     void assignStackOrdering() {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 95d8944..1888e94 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -103,13 +103,16 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.localLOGV;
 
+import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.Px;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.StatusBarManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.PixelFormat;
@@ -255,6 +258,9 @@
     private int[] mNavigationBarHeightForRotationInCarMode = new int[4];
     private int[] mNavigationBarWidthForRotationInCarMode = new int[4];
 
+    /** Cached value of {@link ScreenShapeHelper#getWindowOutsetBottomPx} */
+    @Px private int mWindowOutsetBottom;
+
     private final StatusBarController mStatusBarController = new StatusBarController();
 
     private final BarController mNavigationBarController = new BarController("NavigationBar",
@@ -731,6 +737,11 @@
         return true;
     }
 
+    private boolean hasStatusBarServicePermission(int pid, int uid) {
+        return mContext.checkPermission(permission.STATUS_BAR_SERVICE, pid, uid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     /**
      * Sanitize the layout parameters coming from a client.  Allows the policy
      * to do things like ensure that windows of a specific type can't take
@@ -740,7 +751,7 @@
      * are modified in-place.
      */
     public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
-            boolean hasStatusBarServicePermission) {
+            int callingPid, int callingUid) {
 
         final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
         if (mScreenDecorWindows.contains(win)) {
@@ -748,7 +759,7 @@
                 // No longer has the flag set, so remove from the set.
                 mScreenDecorWindows.remove(win);
             }
-        } else if (isScreenDecor && hasStatusBarServicePermission) {
+        } else if (isScreenDecor && hasStatusBarServicePermission(callingPid, callingUid)) {
             mScreenDecorWindows.add(win);
         }
 
@@ -1159,7 +1170,7 @@
 
         final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
         if (useOutsets) {
-            int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
+            int outset = mWindowOutsetBottom;
             if (outset > 0) {
                 if (displayRotation == Surface.ROTATION_0) {
                     outOutsets.bottom += outset;
@@ -1479,12 +1490,13 @@
         }
         // apply any navigation bar insets
         sTmpRect.setEmpty();
-        mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
+        final WindowFrames windowFrames = mStatusBar.getWindowFrames();
+        windowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
                 displayFrames.mUnrestricted /* displayFrame */,
                 displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
                 displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
                 displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
-        mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+        windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
 
         // Let the status bar determine its size.
         mStatusBar.computeFrameLw();
@@ -1524,8 +1536,9 @@
                     "dock=%s content=%s cur=%s", dockFrame.toString(),
                     displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
 
-            if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
-                    && !mStatusBarController.wasRecentlyTranslucent()) {
+            if (!statusBarTranslucent && !mStatusBarController.wasRecentlyTranslucent()
+                    && !mStatusBar.isAnimatingLw()) {
+
                 // If the opaque status bar is currently requested to be visible, and not in the
                 // process of animating on or off, then we can tell the app that it is covered by
                 // it.
@@ -2180,7 +2193,7 @@
             final Rect osf = windowFrames.mOutsetFrame;
             osf.set(cf.left, cf.top, cf.right, cf.bottom);
             windowFrames.setHasOutsets(true);
-            int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
+            int outset = mWindowOutsetBottom;
             if (outset > 0) {
                 int rotation = displayFrames.mRotation;
                 if (rotation == Surface.ROTATION_0) {
@@ -2336,7 +2349,7 @@
         }
 
         // Voice interaction overrides both top fullscreen and top docked.
-        if (affectsSystemUi && win.getAttrs().type == TYPE_VOICE_INTERACTION) {
+        if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION) {
             if (mTopFullscreenOpaqueWindowState == null) {
                 mTopFullscreenOpaqueWindowState = win;
                 if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
@@ -2599,6 +2612,7 @@
         // EXPERIMENT END
 
         updateConfigurationAndScreenSizeDependentBehaviors();
+        mWindowOutsetBottom = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
     }
 
     void updateConfigurationAndScreenSizeDependentBehaviors() {
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index ab95e4b..8dae016 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
@@ -43,6 +44,9 @@
 
     final SurfaceControl mInputSurface;
     Rect mTmpClipRect = new Rect();
+    private final Rect mTmpRect = new Rect();
+    private final Point mOldPosition = new Point();
+    private final Rect mOldWindowCrop = new Rect();
 
     InputConsumerImpl(WindowManagerService service, IBinder token, String name,
             InputChannel inputChannel, int clientPid, UserHandle clientUser, int displayId) {
@@ -112,16 +116,22 @@
     }
 
     void layout(SurfaceControl.Transaction t, int dw, int dh) {
-        t.setPosition(mInputSurface, 0, 0);
-
-        mTmpClipRect.set(0, 0, dw, dh);
-        t.setWindowCrop(mInputSurface, mTmpClipRect);
+        mTmpRect.set(0, 0, dw, dh);
+        layout(t, mTmpRect);
     }
 
     void layout(SurfaceControl.Transaction t, Rect r) {
-        t.setPosition(mInputSurface, r.left, r.top);
         mTmpClipRect.set(0, 0, r.width(), r.height());
+
+        if (mOldPosition.equals(r.left, r.top) && mOldWindowCrop.equals(mTmpClipRect)) {
+            return;
+        }
+
+        t.setPosition(mInputSurface, r.left, r.top);
         t.setWindowCrop(mInputSurface, mTmpClipRect);
+
+        mOldPosition.set(r.left, r.top);
+        mOldWindowCrop.set(mTmpClipRect);
     }
 
     void hide(SurfaceControl.Transaction t) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 5669451..835b9b1 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
@@ -190,8 +191,13 @@
     }
 
     void layoutInputConsumers(int dw, int dh) {
-        for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
-            mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
+        try {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "layoutInputConsumer");
+            for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
+                mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
+            }
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -401,7 +407,7 @@
         final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, null, mDisplayId);
 
         private void updateInputWindows(boolean inDrag) {
-            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
 
             navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION);
             pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
@@ -429,7 +435,7 @@
 
             mDisplayContent.scheduleAnimation();
 
-            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ed5f665..1ca31f1 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
@@ -65,6 +66,7 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.EventLog;
@@ -551,18 +553,26 @@
         return leakedSurface || killedApps;
     }
 
+    void performSurfacePlacement(boolean recoveringMemory) {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
+        try {
+            performSurfacePlacementNoTrace(recoveringMemory);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+
     // "Something has changed!  Let's make it correct now."
     // TODO: Super crazy long method that should be broken down...
-    void performSurfacePlacement(boolean recoveringMemory) {
+    void performSurfacePlacementNoTrace(boolean recoveringMemory) {
         if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "
                 + Debug.getCallers(3));
 
         int i;
-        boolean updateInputWindowsNeeded = false;
 
         if (mWmService.mFocusMayChange) {
             mWmService.mFocusMayChange = false;
-            updateInputWindowsNeeded = mWmService.updateFocusedWindowLocked(
+            mWmService.updateFocusedWindowLocked(
                     UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
         }
 
@@ -586,6 +596,7 @@
 
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                 ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
         mWmService.openSurfaceTransaction();
         try {
             applySurfaceChangesTransaction(recoveringMemory);
@@ -593,6 +604,7 @@
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
             mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
         }
@@ -621,10 +633,8 @@
 
         if (mWmService.mFocusMayChange) {
             mWmService.mFocusMayChange = false;
-            if (mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
-                    false /*updateInputWindows*/)) {
-                updateInputWindowsNeeded = true;
-            }
+            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+                    false /*updateInputWindows*/);
         }
 
         if (isLayoutNeeded()) {
@@ -679,12 +689,6 @@
             }
         }
 
-        // Finally update all input windows now that the window changes have stabilized.
-        forAllDisplays(dc -> {
-            dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
-            dc.updateSystemGestureExclusion();
-        });
-
         mWmService.setHoldScreenLocked(mHoldScreen);
         if (!mWmService.mDisplayFrozen) {
             final int brightness = mScreenBrightness < 0 || mScreenBrightness > 1.0f
@@ -710,7 +714,7 @@
 
         if (mWmService.mWaitingForDrawnCallback != null
                 || (mOrientationChangeComplete && !isLayoutNeeded()
-                        && !mUpdateRotation)) {
+                && !mUpdateRotation)) {
             mWmService.checkDrawnWindowsLocked();
         }
 
@@ -742,12 +746,11 @@
             mChildren.get(displayNdx).checkCompleteDeferredRemoval();
         }
 
-        if (updateInputWindowsNeeded) {
-            forAllDisplays(dc -> {
-                dc.getInputMonitor().updateInputWindowsLw(false /*force*/);
-            });
-        }
-        forAllDisplays(DisplayContent::updateTouchExcludeRegion);
+        forAllDisplays(dc -> {
+            dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
+            dc.updateSystemGestureExclusion();
+            dc.updateTouchExcludeRegion();
+        });
 
         // Check to see if we are now in a state where the screen should
         // be enabled, because the window obscured flags have changed.
@@ -776,7 +779,7 @@
                 }
             }
 
-            if (!curDisplay.isAppAnimating() && curDisplay.mAppTransition.isRunning()) {
+            if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppAnimating()) {
                 // We have finished the animation of an app transition. To do this, we have
                 // delayed a lot of operations like showing and hiding apps, moving apps in
                 // Z-order, etc.
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 20a874b..9fe4760 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -244,8 +244,6 @@
     void calculateOutsets() {
         if (mHasOutsets) {
             InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets);
-        } else {
-            mOutsets.setEmpty();
         }
     }
 
@@ -373,7 +371,13 @@
      * Sets whether the frame has outsets.
      */
     public void setHasOutsets(boolean hasOutsets) {
+        if (mHasOutsets == hasOutsets) {
+            return;
+        }
         mHasOutsets = hasOutsets;
+        if (!hasOutsets) {
+            mOutsets.setEmpty();
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index d3f3711..9d80425 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -414,7 +414,7 @@
         OnHardKeyboardStatusChangeListener listener);
 
     /** Returns true if a stack in the windowing mode is currently visible. */
-    public abstract boolean isStackVisible(int windowingMode);
+    public abstract boolean isStackVisibleLw(int windowingMode);
 
     /**
      * Requests the window manager to resend the windows for accessibility.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9e421c1..20d02ee 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1392,11 +1392,9 @@
                 return WindowManagerGlobal.ADD_INVALID_DISPLAY;
             }
 
-            final boolean hasStatusBarServicePermission =
-                    mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
-                            == PackageManager.PERMISSION_GRANTED;
             final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
-            displayPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);
+            displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
+                    Binder.getCallingUid());
             win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
 
             res = displayPolicy.prepareAddWindowLw(win, attrs);
@@ -1932,6 +1930,11 @@
         }
     }
 
+    private boolean hasStatusBarPermission(int pid, int uid) {
+        return mContext.checkPermission(permission.STATUS_BAR, pid, uid)
+                        == PackageManager.PERMISSION_GRANTED;
+    }
+
     public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewVisibility, int flags,
             long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
@@ -1940,13 +1943,8 @@
             SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
         int result = 0;
         boolean configChanged;
-        final boolean hasStatusBarPermission =
-                mContext.checkCallingOrSelfPermission(permission.STATUS_BAR)
-                        == PackageManager.PERMISSION_GRANTED;
-        final boolean hasStatusBarServicePermission =
-                mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
-                        == PackageManager.PERMISSION_GRANTED;
-
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
         long origId = Binder.clearCallingIdentity();
         final int displayId;
         synchronized (mGlobalLock) {
@@ -1973,13 +1971,13 @@
             int attrChanges = 0;
             int flagChanges = 0;
             if (attrs != null) {
-                displayPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);
+                displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
                 // if they don't have the permission, mask out the status bar bits
                 if (seq == win.mSeq) {
                     int systemUiVisibility = attrs.systemUiVisibility
                             | attrs.subtreeSystemUiVisibility;
                     if ((systemUiVisibility & DISABLE_MASK) != 0) {
-                        if (!hasStatusBarPermission) {
+                        if (!hasStatusBarPermission(pid, uid)) {
                             systemUiVisibility &= ~DISABLE_MASK;
                         }
                     }
@@ -2050,7 +2048,6 @@
                             && viewVisibility == View.VISIBLE;
             boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
                     || becameVisible;
-            final boolean isDefaultDisplay = win.isDefaultDisplay();
             boolean focusMayChange = win.mViewVisibility != viewVisibility
                     || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
                     || (!win.mRelayoutCalled);
@@ -7215,11 +7212,9 @@
         }
 
         @Override
-        public boolean isStackVisible(int windowingMode) {
-            synchronized (mGlobalLock) {
-                final DisplayContent dc = getDefaultDisplayContentLocked();
-                return dc.isStackVisible(windowingMode);
-            }
+        public boolean isStackVisibleLw(int windowingMode) {
+            final DisplayContent dc = getDefaultDisplayContentLocked();
+            return dc.isStackVisible(windowingMode);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 486b0da..11288d2 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -308,6 +308,9 @@
      */
     private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
 
+    /** @see #isLastConfigReportedToClient() */
+    private boolean mLastConfigReportedToClient;
+
     private final Configuration mTempConfiguration = new Configuration();
 
     /**
@@ -1201,7 +1204,7 @@
         }
 
         boolean didFrameInsetsChange = setReportResizeHints();
-        boolean configChanged = isConfigChanged();
+        boolean configChanged = !isLastConfigReportedToClient();
         if (DEBUG_CONFIGURATION && configChanged) {
             Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration());
         }
@@ -1781,9 +1784,18 @@
         return getDisplayContent().getBounds().equals(getBounds());
     }
 
-    /** Returns true if last applied config was not yet requested by client. */
-    boolean isConfigChanged() {
-        return !getLastReportedConfiguration().equals(getConfiguration());
+    /**
+     * @return {@code true} if last applied config was reported to the client already, {@code false}
+     *         otherwise.
+     */
+    boolean isLastConfigReportedToClient() {
+        return mLastConfigReportedToClient;
+    }
+
+    @Override
+    void onMergedOverrideConfigurationChanged() {
+        super.onMergedOverrideConfigurationChanged();
+        mLastConfigReportedToClient = false;
     }
 
     void onWindowReplacementTimeout() {
@@ -2299,11 +2311,6 @@
     void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
         // We need to turn on screen regardless of visibility.
         boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0;
-        boolean allowTheaterMode =
-                mWmService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt(
-                        mWmService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0)
-                        == 0;
-        boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn();
 
         // The screen will turn on if the following conditions are met
         // 1. The window has the flag FLAG_TURN_SCREEN_ON
@@ -2317,6 +2324,11 @@
         // be occurring while turning off the screen. This would lead to the screen incorrectly
         // turning back on.
         if (hasTurnScreenOnFlag) {
+            boolean allowTheaterMode = mWmService.mAllowTheaterModeWakeFromLayout
+                    || Settings.Global.getInt(mWmService.mContext.getContentResolver(),
+                            Settings.Global.THEATER_MODE_ON, 0) == 0;
+            boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn();
+
             if (allowTheaterMode && canTurnScreenOn && !mPowerManagerWrapper.isInteractive()) {
                 if (DEBUG_VISIBILITY || DEBUG_POWER) {
                     Slog.v(TAG, "Relayout window turning screen on: " + this);
@@ -2365,6 +2377,7 @@
 
     void setLastReportedMergedConfiguration(MergedConfiguration config) {
         mLastReportedConfiguration.setTo(config);
+        mLastConfigReportedToClient = true;
     }
 
     void getLastReportedMergedConfiguration(MergedConfiguration config) {
@@ -2512,6 +2525,10 @@
     }
 
     boolean showLw(boolean doAnimation, boolean requestAnim) {
+        if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
+            // Already showing.
+            return false;
+        }
         if (isHiddenFromUserLocked()) {
             return false;
         }
@@ -2532,10 +2549,6 @@
             // This is an alert window that is currently force hidden.
             return false;
         }
-        if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
-            // Already showing.
-            return false;
-        }
         if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
         if (doAnimation) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
@@ -3529,7 +3542,10 @@
     }
 
     void transformClipRectFromScreenToSurfaceSpace(Rect clipRect) {
-         if (mHScale >= 0) {
+        if (mHScale == 1 && mVScale == 1) {
+            return;
+        }
+        if (mHScale >= 0) {
             clipRect.left = (int) (clipRect.left / mHScale);
             clipRect.right = (int) Math.ceil(clipRect.right / mHScale);
         }
@@ -4385,7 +4401,7 @@
         // scale function because we want to round things to make the crop
         // always round to a larger rect to ensure we don't crop too
         // much and hide part of the window that should be seen.
-        if (inSizeCompatMode() && mInvGlobalScale != 1.0f) {
+        if (mInvGlobalScale != 1.0f && inSizeCompatMode()) {
             final float scale = mInvGlobalScale;
             systemDecorRect.left = (int) (systemDecorRect.left * scale - 0.5f);
             systemDecorRect.top = (int) (systemDecorRect.top * scale - 0.5f);
@@ -4440,7 +4456,12 @@
 
         mWinAnimator.mEnteringAnimation = true;
 
-        prepareWindowToDisplayDuringRelayout(wasVisible);
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareToDisplay");
+        try {
+            prepareWindowToDisplayDuringRelayout(wasVisible);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        }
 
         if ((attrChanges & FORMAT_CHANGED) != 0) {
             // If the format can't be changed in place, preserve the old surface until the app draws
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index acb9823..bef0f81 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -56,6 +56,7 @@
     private float mSurfaceY = 0;
     private int mSurfaceW = 0;
     private int mSurfaceH = 0;
+    private Rect mSurfaceCrop = new Rect(0, 0, -1, -1);
 
     // Initialize to the identity matrix.
     private float mLastDsdx = 1;
@@ -171,26 +172,15 @@
         }
     }
 
-    void disconnectInTransaction() {
-        if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
-            Slog.i(TAG, "Disconnecting client: " + this);
-        }
-
-        try {
-            if (mSurfaceControl != null) {
-                mSurfaceControl.disconnect();
-            }
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Error disconnecting surface in: " + this, e);
-        }
-    }
-
     void setCropInTransaction(Rect clipRect, boolean recoveringMemory) {
         if (SHOW_TRANSACTIONS) logSurface(
                 "CROP " + clipRect.toShortString(), null);
         try {
             if (clipRect.width() > 0 && clipRect.height() > 0) {
-                mSurfaceControl.setWindowCrop(clipRect);
+                if (!clipRect.equals(mSurfaceCrop)) {
+                    mSurfaceControl.setWindowCrop(clipRect);
+                    mSurfaceCrop.set(clipRect);
+                }
                 mHiddenForCrop = false;
                 updateVisibility();
             } else {
@@ -212,7 +202,11 @@
                 "CLEAR CROP", null);
         try {
             Rect clipRect = new Rect(0, 0, -1, -1);
+            if (mSurfaceCrop.equals(clipRect)) {
+                return;
+            }
             mSurfaceControl.setWindowCrop(clipRect);
+            mSurfaceCrop.set(clipRect);
         } catch (RuntimeException e) {
             Slog.w(TAG, "Error setting clearing crop of " + this, e);
             if (!recoveringMemory) {
@@ -221,12 +215,6 @@
         }
     }
 
-    void setLayerStackInTransaction(int layerStack) {
-        if (mSurfaceControl != null) {
-            mSurfaceControl.setLayerStack(layerStack);
-        }
-    }
-
     void setPositionInTransaction(float left, float top, boolean recoveringMemory) {
         setPosition(null, left, top, recoveringMemory);
     }
diff --git a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
index 98bad93..3be5d31 100644
--- a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
+++ b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
@@ -81,11 +81,24 @@
      * @hide
      */
     public WmDisplayCutout calculateRelativeTo(Rect frame) {
+        if (mFrameSize == null) {
+            return this;
+        }
+        final int insetRight = mFrameSize.getWidth() - frame.right;
+        final int insetBottom = mFrameSize.getHeight() - frame.bottom;
+        if (frame.left == 0 && frame.top == 0 && insetRight == 0 && insetBottom == 0) {
+            return this;
+        }
+        if (frame.left >= mInner.getSafeInsetLeft()
+                && frame.top >= mInner.getSafeInsetTop()
+                && insetRight >= mInner.getSafeInsetRight()
+                && insetBottom >= mInner.getSafeInsetBottom()) {
+            return NO_CUTOUT;
+        }
         if (mInner.isEmpty()) {
             return this;
         }
-        return inset(frame.left, frame.top,
-                mFrameSize.getWidth() - frame.right, mFrameSize.getHeight() - frame.bottom);
+        return inset(frame.left, frame.top, insetRight, insetBottom);
     }
 
     /**
diff --git a/services/robotests/backup/Android.mk b/services/robotests/backup/Android.mk
index cc59b0c..bd4ebbd 100644
--- a/services/robotests/backup/Android.mk
+++ b/services/robotests/backup/Android.mk
@@ -26,7 +26,7 @@
 LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    bmgrlib \
+    bmgr \
     bu \
     services.backup \
     services.core \
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 04abeca1..f39c716 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -34,7 +34,9 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
@@ -64,6 +66,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
@@ -321,6 +324,14 @@
         verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
     }
 
+    @Test
+    public void testExplicitSystenUserStartInBackground() {
+        setUpUser(UserHandle.USER_SYSTEM, 0);
+        assertFalse(mUserController.isSystemUserStarted());
+        assertTrue(mUserController.startUser(UserHandle.USER_SYSTEM, false, null));
+        assertTrue(mUserController.isSystemUserStarted());
+    }
+
     private void setUpUser(int userId, int flags) {
         UserInfo userInfo = new UserInfo(userId, "User" + userId, flags);
         when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
@@ -417,6 +428,12 @@
         @Override
         void reportCurWakefulnessUsageEvent() {
         }
+
+        @Override
+        boolean isRuntimeRestarted() {
+            // to pass all metrics related calls
+            return true;
+        }
     }
 
     private static class TestHandler extends Handler {
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
index 4b3d9cf..0792414 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
@@ -1148,6 +1148,11 @@
         return null;
     }
 
+    @Override
+    public String getAttentionServicePackageName() throws RemoteException {
+        return null;
+    }
+
     public String getIncidentReportApproverPackageName() throws RemoteException {
         return null;
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index ee09c7e..7c22350 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -477,8 +477,9 @@
         assertEquals(MetricsEvent.IMPORTANCE_EXPLANATION_APP,
                 logMaker.getTaggedData(
                         MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL_EXPLANATION));
-        // This field is only populated if the assistant was itself overridden by the system.
-        assertNull(logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST));
+        // This field is populated whenever mImportanceExplanationCode is.
+        assertEquals(IMPORTANCE_LOW,
+                logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 2ab48a9..5cef38d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -38,6 +38,7 @@
 import android.content.res.Resources;
 import android.graphics.Matrix;
 import android.graphics.RectF;
+import android.os.Binder;
 import android.os.IBinder;
 import android.testing.TestableResources;
 import android.util.Pair;
@@ -98,7 +99,8 @@
     }
 
     void addWindow(WindowState win) {
-        mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, true /* hasStatusBarPermission */);
+        mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
+                Binder.getCallingUid());
         assertEquals(WindowManagerGlobal.ADD_OKAY,
                 mDisplayPolicy.prepareAddWindowLw(win, win.mAttrs));
         win.mHasSurface = true;
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index a15f959..a1c5bbe 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -27,8 +27,8 @@
 
 /**
  * Defines available network information which includes corresponding subscription id,
- * network plmns and corresponding priority to be used for network selection by Alternative Network
- * Service.
+ * network plmns and corresponding priority to be used for network selection by Opportunistic
+ * Network Service when passed through {@link TelephonyManager#updateAvailableNetworks}
  */
 public final class AvailableNetworkInfo implements Parcelable {
 
@@ -55,15 +55,19 @@
 
     /**
      * Priority for the subscription id.
-     * Priorities are in the range of 1 to 3 where 1
-     * has the highest priority.
+     * Priorities are in the range of {@link AvailableNetworkInfo#PRIORITY_LOW} to
+     * {@link AvailableNetworkInfo#PRIORITY_HIGH}
+     * Among all networks available after network scan, subId with highest priority is chosen
+     * for network selection. If there are more than one subId with highest priority then the
+     * network with highest RSRP is chosen.
      */
     private int mPriority;
 
     /**
      * Describes the List of PLMN ids (MCC-MNC) associated with mSubId.
-     * If this entry is left empty, then the platform software will not scan the network
-     * to revalidate the input else platform will scan and verify specified PLMNs are available.
+     * Opportunistic Network Service will scan and verify specified PLMNs are available.
+     * If this entry is left empty, then the Opportunistic Network Service will not scan the network
+     * to validate the network availability.
      */
     private ArrayList<String> mMccMncs;
 
@@ -71,8 +75,8 @@
      * Returns the frequency bands associated with the {@link #getMccMncs() MCC/MNCs}.
      * Opportunistic network service will use these bands to scan.
      *
-     * When no specific bands are specified (empty array or null) CBRS band (B48) will be
-     * used for network scan.
+     * When no specific bands are specified (empty array or null) CBRS band
+     * {@link AccessNetworkConstants.EutranBand.BAND_48} will be used for network scan.
      *
      * See {@link AccessNetworkConstants} for details.
      */
@@ -89,8 +93,12 @@
     }
 
     /**
-     * Return priority for the subscription id. Valid value will be within
-     * [{@link AvailableNetworkInfo#PRIORITY_HIGH}, {@link AvailableNetworkInfo#PRIORITY_LOW}]
+     * Return priority for the subscription id.
+     * Priorities are in the range of {@link AvailableNetworkInfo#PRIORITY_LOW} to
+     * {@link AvailableNetworkInfo#PRIORITY_HIGH}
+     * Among all networks available after network scan, subId with highest priority is chosen
+     * for network selection. If there are more than one subId with highest priority then the
+     * network with highest RSRP is chosen.
      * @return priority level
      */
     public int getPriority() {
@@ -99,8 +107,9 @@
 
     /**
      * Return List of PLMN ids (MCC-MNC) associated with the sub ID.
-     * If this entry is left empty, then the platform software will not scan the network
-     * to revalidate the input.
+     * Opportunistic Network Service will scan and verify specified PLMNs are available.
+     * If this entry is left empty, then the Opportunistic Network Service will not scan the network
+     * to validate the network availability.
      * @return list of PLMN ids
      */
     public @NonNull List<String> getMccMncs() {
@@ -112,6 +121,9 @@
      *
      * The returned value is defined in either of {@link AccessNetworkConstants.GeranBand},
      * {@link AccessNetworkConstants.UtranBand} and {@link AccessNetworkConstants.EutranBand}
+     * See {@link AccessNetworkConstants.AccessNetworkType} for details regarding different network
+     * types. When no specific bands are specified (empty array or null) CBRS band
+     * {@link AccessNetworkConstants.EutranBand#BAND_48} will be used for network scan.
      */
     public @NonNull List<Integer> getBands() {
         return (List<Integer>) mBands.clone();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6aca693..0b44367 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3123,14 +3123,14 @@
         sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
                 "connected_mmwave:None,connected:5G,not_restricted:None,restricted:None");
         sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
+        /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
-        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -118);
-        /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_POOR */
-        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -128);
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -118);
+        /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_GOOD */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 45);
         /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_MODERATE */
-        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 10);
-        /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_POOR */
-        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, -30);
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 10);
         /* Default value is 1024 kbps */
         sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT, 1024);
         /* Default value is 10 seconds */
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 918bf60..373c5d2 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -297,8 +297,11 @@
      *  it could be the current active opportunistic subscription in use, or the
      *  subscription user selected as default data subscription in DSDS mode.
      *
-     *  Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     *  READ_PHONE_STATE}
+     *  Requires Permission: No permission is required to listen, but notification requires
+     *  {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or the calling
+     *  app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges})
+     *  on any active subscription.
+     *
      *  @see #onActiveDataSubscriptionIdChanged
      */
     public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 09046a6..63d427a 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -951,8 +951,7 @@
      * @return associated subscription id
      */
     public int getSubscriptionId() {
-        final int subId = (mSubId == DEFAULT_SUBSCRIPTION_ID)
-                ? getDefaultSmsSubscriptionId() : mSubId;
+        final int subId = getSubIdOrDefault();
         boolean isSmsSimPickActivityNeeded = false;
         final Context context = ActivityThread.currentApplication().getApplicationContext();
         try {
@@ -985,6 +984,17 @@
     }
 
     /**
+     * @return the subscription ID associated with this {@link SmsManager} or the default set by the
+     * user if this instance was created using {@link SmsManager#getDefault}.
+     *
+     * If there is no default set by the user, this method returns
+     * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+     */
+    private int getSubIdOrDefault() {
+        return (mSubId == DEFAULT_SUBSCRIPTION_ID) ? getDefaultSmsSubscriptionId() : mSubId;
+    }
+
+    /**
      * Returns the ISms service, or throws an UnsupportedOperationException if
      * the service does not exist.
      */
@@ -1151,8 +1161,9 @@
         try {
             ISms iSms = getISmsService();
             if (iSms != null) {
-                success = iSms.enableCellBroadcastForSubscriber(
-                        getSubscriptionId(), messageIdentifier, ranType);
+                // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
+                success = iSms.enableCellBroadcastForSubscriber(getSubIdOrDefault(),
+                        messageIdentifier, ranType);
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -1187,8 +1198,9 @@
         try {
             ISms iSms = getISmsService();
             if (iSms != null) {
-                success = iSms.disableCellBroadcastForSubscriber(
-                        getSubscriptionId(), messageIdentifier, ranType);
+                // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
+                success = iSms.disableCellBroadcastForSubscriber(getSubIdOrDefault(),
+                        messageIdentifier, ranType);
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -1230,7 +1242,8 @@
         try {
             ISms iSms = getISmsService();
             if (iSms != null) {
-                success = iSms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(),
+                // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
+                success = iSms.enableCellBroadcastRangeForSubscriber(getSubIdOrDefault(),
                         startMessageId, endMessageId, ranType);
             }
         } catch (RemoteException ex) {
@@ -1273,7 +1286,8 @@
         try {
             ISms iSms = getISmsService();
             if (iSms != null) {
-                success = iSms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(),
+                // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
+                success = iSms.disableCellBroadcastRangeForSubscriber(getSubIdOrDefault(),
                         startMessageId, endMessageId, ranType);
             }
         } catch (RemoteException ex) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e4debd6..6dd1691 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10547,6 +10547,9 @@
     /**
      * Set preferred opportunistic data subscription id.
      *
+     * Switch internet data to preferred opportunistic data subscription id. This api
+     * can result in lose of internet connectivity for short period of time while internet data
+     * is handed over.
      * <p>Requires that the calling app has carrier privileges on both primary and
      * secondary subscriptions (see
      * {@link #hasCarrierPrivileges}), or has permission
@@ -10625,9 +10628,11 @@
      *
      * This api should be called to inform OpportunisticNetwork Service about the availability
      * of a network at the current location. This information will be used by OpportunisticNetwork
-     * service to decide to attach to the network opportunistically. If an empty list is passed,
+     * service to enable modem stack and to attach to the network. If an empty list is passed,
      * it is assumed that no network is available and will result in disabling the modem stack
-     * to save power.
+     * to save power. This api do not switch internet data once network attach is completed.
+     * Use {@link TelephonyManager#setPreferredOpportunisticDataSubscription}
+     * to switch internet data after network attach is complete.
      * Requires that the calling app has carrier privileges on both primary and
      * secondary subscriptions (see {@link #hasCarrierPrivileges}), or has permission
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 8cdf6a2..cc037e3 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.StringDef;
 import android.annotation.SystemApi;
 import android.annotation.WorkerThread;
 import android.content.Context;
@@ -38,25 +39,36 @@
 
 import com.android.internal.telephony.ITelephony;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.Executor;
 
 /**
  * Manages IMS provisioning and configuration parameters, as well as callbacks for apps to listen
  * to changes in these configurations.
  *
- * Note: IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
- * applications and may vary. For compatibility purposes, the first 100 integer values used in
- * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
- * previously defined in the Android framework. Some common constants have been defined in this
- * class to make integrating with other system apps easier. USE WITH CARE!
+ * IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
+ * applications and may vary. It is up to the carrier and OEM applications to ensure that the
+ * correct provisioning keys are being used when integrating with a vendor's ImsService.
  *
- * To avoid collisions, please use String based configurations when possible:
- * {@link #setProvisioningStringValue(int, String)} and {@link #getProvisioningStringValue(int)}.
+ * Note: For compatibility purposes, the integer values [0 - 99] used in
+ * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
+ * previously defined in the Android framework. Please do not redefine new provisioning keys in this
+ * range or it may generate collisions with existing keys. Some common constants have also been
+ * defined in this class to make integrating with other system apps easier.
  * @hide
  */
 @SystemApi
 public class ProvisioningManager {
 
+    /**@hide*/
+    @StringDef(prefix = "STRING_QUERY_RESULT_ERROR_", value = {
+            STRING_QUERY_RESULT_ERROR_GENERIC,
+            STRING_QUERY_RESULT_ERROR_NOT_READY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StringResultError {}
+
     /**
      * The query from {@link #getProvisioningStringValue(int)} has resulted in an unspecified error.
      */
@@ -268,14 +280,13 @@
      * This operation is blocking and should not be performed on the UI thread.
      *
      * @param key A String that represents the provisioning key, which is defined by the OEM.
-     * @return a String value for the provided key, {@code null} if the key doesn't exist, or one
-     * of the following error codes: {@link #STRING_QUERY_RESULT_ERROR_GENERIC},
-     * {@link #STRING_QUERY_RESULT_ERROR_NOT_READY}.
+     * @return a String value for the provided key, {@code null} if the key doesn't exist, or
+     * {@link StringResultError} if there was an error getting the value for the provided key.
      * @throws IllegalArgumentException if the key provided was invalid.
      */
     @WorkerThread
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public @Nullable String getProvisioningStringValue(int key) {
+    public @Nullable @StringResultError String getProvisioningStringValue(int key) {
         try {
             return getITelephony().getImsProvisioningString(mSubId, key);
         } catch (RemoteException e) {
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index d93e582..f5985b4 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -164,6 +164,63 @@
         // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
         // revoked.
         AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        return appOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, uid, callingPackage)
+                == AppOpsManager.MODE_ALLOWED;
+    }
+
+    /**
+     * Check whether the app with the given pid/uid can read phone state, or has carrier
+     * privileges on any active subscription.
+     *
+     * <p>If the app does not have carrier privilege, this method will return {@code false} instead
+     * of throwing a SecurityException. Therefore, the callers cannot tell the difference
+     * between M+ apps which declare the runtime permission but do not have it, and pre-M apps
+     * which declare the static permission but had access revoked via AppOps. Apps in the former
+     * category expect SecurityExceptions; apps in the latter don't. So this method is suitable for
+     * use only if the behavior in both scenarios is meant to be identical.
+     *
+     * @return {@code true} if the app can read phone state or has carrier privilege;
+     *         {@code false} otherwise.
+     */
+    public static boolean checkReadPhoneStateOnAnyActiveSub(
+            Context context, int pid, int uid, String callingPackage, String message) {
+        return checkReadPhoneStateOnAnyActiveSub(context, TELEPHONY_SUPPLIER, pid, uid,
+                    callingPackage, message);
+    }
+
+    @VisibleForTesting
+    public static boolean checkReadPhoneStateOnAnyActiveSub(
+            Context context, Supplier<ITelephony> telephonySupplier, int pid, int uid,
+            String callingPackage, String message) {
+        try {
+            context.enforcePermission(
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
+
+            // SKIP checking for run-time permission since caller has PRIVILEGED permission
+            return true;
+        } catch (SecurityException privilegedPhoneStateException) {
+            try {
+                context.enforcePermission(
+                        android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
+            } catch (SecurityException phoneStateException) {
+                SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
+                        Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+                int[] activeSubIds = sm.getActiveSubscriptionIdList();
+                for (int activeSubId : activeSubIds) {
+                    // If we don't have the runtime permission, but do have carrier privileges, that
+                    // suffices for reading phone state.
+                    if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid)
+                            == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        }
+
+        // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
+        // revoked.
+        AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         return appOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, uid, callingPackage) ==
                 AppOpsManager.MODE_ALLOWED;
     }
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index e17fb47..92f1ddb 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -2314,7 +2314,7 @@
 int DumpManifest(LoadedApk* apk, DumpManifestOptions& options, text::Printer* printer,
                  IDiagnostics* diag) {
   ManifestExtractor extractor(apk, options);
-  return extractor.Dump(printer, diag);
+  return extractor.Dump(printer, diag) ? 0 : 1;
 }
 
 } // namespace aapt
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 78967e4..be227e7 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2190,22 +2190,7 @@
                 key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
             }
         } else {
-            if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
-                key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
-            } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
-                    allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
-                key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
-            } else if (wepKeys[0] != null) {
-                key = SSID + "WEP";
-            } else if (allowedKeyManagement.get(KeyMgmt.OWE)) {
-                key = SSID + KeyMgmt.strings[KeyMgmt.OWE];
-            } else if (allowedKeyManagement.get(KeyMgmt.SAE)) {
-                key = SSID + KeyMgmt.strings[KeyMgmt.SAE];
-            } else if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
-                key = SSID + KeyMgmt.strings[KeyMgmt.SUITE_B_192];
-            } else {
-                key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
-            }
+            key = getSsidAndSecurityTypeString();
             if (!shared) {
                 key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
             }
@@ -2215,6 +2200,30 @@
     }
 
     /** @hide
+     *  return the SSID + security type in String format.
+     */
+    public String getSsidAndSecurityTypeString() {
+        String key;
+        if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+            key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
+        } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)
+                || allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+            key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+        } else if (wepKeys[0] != null) {
+            key = SSID + "WEP";
+        } else if (allowedKeyManagement.get(KeyMgmt.OWE)) {
+            key = SSID + KeyMgmt.strings[KeyMgmt.OWE];
+        } else if (allowedKeyManagement.get(KeyMgmt.SAE)) {
+            key = SSID + KeyMgmt.strings[KeyMgmt.SAE];
+        } else if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
+            key = SSID + KeyMgmt.strings[KeyMgmt.SUITE_B_192];
+        } else {
+            key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
+        }
+        return key;
+    }
+
+    /** @hide
      * get configKey, force calculating the config string
      */
     public String configKey() {
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index d927052..ba9fc78 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -348,4 +348,47 @@
         }
         assertTrue(exceptionThrown);
     }
+
+    /**
+     * Verifies that getSsidAndSecurityTypeString returns the correct String for networks of
+     * various different security types
+     */
+    @Test
+    public void testGetSsidAndSecurityTypeString() {
+        WifiConfiguration config = new WifiConfiguration();
+        final String mSsid = "TestAP";
+        config.SSID = mSsid;
+
+        // Test various combinations
+        config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.WPA_PSK],
+                config.getSsidAndSecurityTypeString());
+
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.WPA_EAP],
+                config.getSsidAndSecurityTypeString());
+
+        config.wepKeys[0] = "TestWep";
+        config.allowedKeyManagement.clear();
+        assertEquals(mSsid + "WEP", config.getSsidAndSecurityTypeString());
+
+        config.wepKeys[0] = null;
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.OWE);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.OWE], config.getSsidAndSecurityTypeString());
+
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.SAE);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.SAE], config.getSsidAndSecurityTypeString());
+
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.SUITE_B_192);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.SUITE_B_192],
+                config.getSsidAndSecurityTypeString());
+
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.NONE], config.getSsidAndSecurityTypeString());
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 600abc9..fa17db1 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1382,4 +1382,60 @@
             r.run();
         }
     }
+
+    /**
+     * Test behavior of isEnhancedOpenSupported
+     * @throws Exception
+     */
+    @Test
+    public void testIsEnhancedOpenSupported() throws Exception {
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(WifiManager.WIFI_FEATURE_OWE));
+        assertTrue(mWifiManager.isEnhancedOpenSupported());
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(~WifiManager.WIFI_FEATURE_OWE));
+        assertFalse(mWifiManager.isEnhancedOpenSupported());
+    }
+
+    /**
+     * Test behavior of isWpa3SaeSupported
+     * @throws Exception
+     */
+    @Test
+    public void testIsWpa3SaeSupported() throws Exception {
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(WifiManager.WIFI_FEATURE_WPA3_SAE));
+        assertTrue(mWifiManager.isWpa3SaeSupported());
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WPA3_SAE));
+        assertFalse(mWifiManager.isWpa3SaeSupported());
+    }
+
+    /**
+     * Test behavior of isWpa3SuiteBSupported
+     * @throws Exception
+     */
+    @Test
+    public void testIsWpa3SuiteBSupported() throws Exception {
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(WifiManager.WIFI_FEATURE_WPA3_SUITE_B));
+        assertTrue(mWifiManager.isWpa3SuiteBSupported());
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WPA3_SUITE_B));
+        assertFalse(mWifiManager.isWpa3SuiteBSupported());
+    }
+
+    /**
+     * Test behavior of isEasyConnectSupported
+     * @throws Exception
+     */
+    @Test
+    public void testIsEasyConnectSupported() throws Exception {
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(WifiManager.WIFI_FEATURE_DPP));
+        assertTrue(mWifiManager.isEasyConnectSupported());
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(~WifiManager.WIFI_FEATURE_DPP));
+        assertFalse(mWifiManager.isEasyConnectSupported());
+    }
 }