Merge "Add config to use lower of IPv4/IPv6 MTU values" into rvc-qpr-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index aeed188..36ccaf9 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1120,6 +1120,10 @@
             if (isDeviceProvisioningPackage(packageName)) {
                 return STANDBY_BUCKET_EXEMPTED;
             }
+
+            if (mInjector.isWellbeingPackage(packageName)) {
+                return STANDBY_BUCKET_WORKING_SET;
+            }
         }
 
         // Check this last, as it can be the most expensive check
@@ -1929,6 +1933,7 @@
          */
         @GuardedBy("mPowerWhitelistedApps")
         private final ArraySet<String> mPowerWhitelistedApps = new ArraySet<>();
+        private String mWellbeingApp = null;
 
         Injector(Context context, Looper looper) {
             mContext = context;
@@ -1962,6 +1967,9 @@
                 if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
                     mAutoRestrictedBucketDelayMs = 12 * ONE_HOUR;
                 }
+
+                final PackageManager packageManager = mContext.getPackageManager();
+                mWellbeingApp = packageManager.getWellbeingPackageName();
             }
             mBootPhase = phase;
         }
@@ -2006,6 +2014,14 @@
             }
         }
 
+        /**
+         * Returns {@code true} if the supplied package is the wellbeing app. Otherwise,
+         * returns {@code false}.
+         */
+        boolean isWellbeingPackage(String packageName) {
+            return mWellbeingApp != null && mWellbeingApp.equals(packageName);
+        }
+
         void updatePowerWhitelistCache() {
             try {
                 // Don't call out to DeviceIdleController with the lock held.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8d64661..b47d44d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3250,12 +3250,6 @@
         sendMessage(H.CLEAN_UP_CONTEXT, cci);
     }
 
-    @Override
-    public void handleFixedRotationAdjustments(@NonNull IBinder token,
-            @Nullable FixedRotationAdjustments fixedRotationAdjustments) {
-        handleFixedRotationAdjustments(token, fixedRotationAdjustments, null /* overrideConfig */);
-    }
-
     /**
      * Applies the rotation adjustments to override display information in resources belong to the
      * provided token. If the token is activity token, the adjustments also apply to application
@@ -3265,51 +3259,39 @@
      * @param fixedRotationAdjustments The information to override the display adjustments of
      *                                 corresponding resources. If it is null, the exiting override
      *                                 will be cleared.
-     * @param overrideConfig The override configuration of activity. It is used to override
-     *                       application configuration. If it is non-null, it means the token is
-     *                       confirmed as activity token. Especially when launching new activity,
-     *                       {@link #mActivities} hasn't put the new token.
      */
-    private void handleFixedRotationAdjustments(@NonNull IBinder token,
-            @Nullable FixedRotationAdjustments fixedRotationAdjustments,
-            @Nullable Configuration overrideConfig) {
-        // The element of application configuration override is set only if the application
-        // adjustments are needed, because activity already has its own override configuration.
-        final Configuration[] appConfigOverride;
-        final Consumer<DisplayAdjustments> override;
-        if (fixedRotationAdjustments != null) {
-            appConfigOverride = new Configuration[1];
-            override = displayAdjustments -> {
-                displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
-                if (appConfigOverride[0] != null) {
-                    displayAdjustments.getConfiguration().updateFrom(appConfigOverride[0]);
-                }
-            };
-        } else {
-            appConfigOverride = null;
-            override = null;
-        }
+    @Override
+    public void handleFixedRotationAdjustments(@NonNull IBinder token,
+            @Nullable FixedRotationAdjustments fixedRotationAdjustments) {
+        final Consumer<DisplayAdjustments> override = fixedRotationAdjustments != null
+                ? displayAdjustments -> displayAdjustments
+                        .setFixedRotationAdjustments(fixedRotationAdjustments)
+                : null;
         if (!mResourcesManager.overrideTokenDisplayAdjustments(token, override)) {
             // No resources are associated with the token.
             return;
         }
-        if (overrideConfig == null) {
-            final ActivityClientRecord r = mActivities.get(token);
-            if (r == null) {
-                // It is not an activity token. Nothing to do for application.
-                return;
-            }
-            overrideConfig = r.overrideConfig;
-        }
-        if (appConfigOverride != null) {
-            appConfigOverride[0] = overrideConfig;
+        if (mActivities.get(token) == null) {
+            // Nothing to do for application if it is not an activity token.
+            return;
         }
 
-        // Apply the last override to application resources for compatibility. Because the Resources
-        // of Display can be from application, e.g.
-        //    applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId)
-        // and the deprecated usage:
-        //    applicationContext.getSystemService(WindowManager.class).getDefaultDisplay();
+        overrideApplicationDisplayAdjustments(token, override);
+    }
+
+    /**
+     * Applies the last override to application resources for compatibility. Because the Resources
+     * of Display can be from application, e.g.
+     *   applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId)
+     * and the deprecated usage:
+     *   applicationContext.getSystemService(WindowManager.class).getDefaultDisplay();
+     *
+     * @param token The owner and target of the override.
+     * @param override The display adjustments override for application resources. If it is null,
+     *                 the override of the token will be removed and pop the last one to use.
+     */
+    private void overrideApplicationDisplayAdjustments(@NonNull IBinder token,
+            @Nullable Consumer<DisplayAdjustments> override) {
         final Consumer<DisplayAdjustments> appOverride;
         if (mActiveRotationAdjustments == null) {
             mActiveRotationAdjustments = new ArrayList<>(2);
@@ -3542,8 +3524,13 @@
         // The rotation adjustments must be applied before creating the activity, so the activity
         // can get the adjusted display info during creation.
         if (r.mPendingFixedRotationAdjustments != null) {
-            handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments,
-                    r.overrideConfig);
+            // The adjustments should have been set by handleLaunchActivity, so the last one is the
+            // override for activity resources.
+            if (mActiveRotationAdjustments != null && !mActiveRotationAdjustments.isEmpty()) {
+                mResourcesManager.overrideTokenDisplayAdjustments(r.token,
+                        mActiveRotationAdjustments.get(
+                                mActiveRotationAdjustments.size() - 1).second);
+            }
             r.mPendingFixedRotationAdjustments = null;
         }
 
@@ -3582,6 +3569,13 @@
             mProfiler.startProfiling();
         }
 
+        if (r.mPendingFixedRotationAdjustments != null) {
+            // The rotation adjustments must be applied before handling configuration, so process
+            // level display metrics can be adjusted.
+            overrideApplicationDisplayAdjustments(r.token, adjustments ->
+                    adjustments.setFixedRotationAdjustments(r.mPendingFixedRotationAdjustments));
+        }
+
         // Make sure we are running with the most recent config.
         handleConfigurationChanged(null, null);
 
@@ -5777,7 +5771,15 @@
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
                     + config);
 
-            mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
+            final Resources appResources = mInitialApplication.getResources();
+            if (appResources.hasOverrideDisplayAdjustments()) {
+                // The value of Display#getRealSize will be adjusted by FixedRotationAdjustments,
+                // but Display#getSize refers to DisplayAdjustments#mConfiguration. So the rotated
+                // configuration also needs to set to the adjustments for consistency.
+                appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
+            }
+            mResourcesManager.applyConfigurationToResourcesLocked(config, compat,
+                    appResources.getDisplayAdjustments());
             updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
                     mResourcesManager.getConfiguration().getLocales());
 
@@ -7390,7 +7392,8 @@
                 // We need to apply this change to the resources immediately, because upon returning
                 // the view hierarchy will be informed about it.
                 if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
-                        null /* compat */)) {
+                        null /* compat */,
+                        mInitialApplication.getResources().getDisplayAdjustments())) {
                     updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
                             mResourcesManager.getConfiguration().getLocales());
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c51a846..7effbb3 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -187,16 +187,6 @@
     private static final String XATTR_INODE_CODE_CACHE = "user.inode_code_cache";
 
     /**
-     * Special intent extra that critical system apps can use to hide the notification for a
-     * foreground service. This extra should be placed in the intent passed into {@link
-     * #startForegroundService(Intent)}.
-     *
-     * @hide
-     */
-    private static final String EXTRA_HIDDEN_FOREGROUND_SERVICE =
-            "android.intent.extra.HIDDEN_FOREGROUND_SERVICE";
-
-    /**
      * Map from package name, to preference name, to cached preferences.
      */
     @GuardedBy("ContextImpl.class")
@@ -1707,12 +1697,9 @@
         try {
             validateServiceIntent(service);
             service.prepareToLeaveProcess(this);
-            final boolean hideForegroundNotification = requireForeground
-                    && service.getBooleanExtra(EXTRA_HIDDEN_FOREGROUND_SERVICE, false);
             ComponentName cn = ActivityManager.getService().startService(
                     mMainThread.getApplicationThread(), service,
                     service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
-                    hideForegroundNotification,
                     getOpPackageName(), getAttributionTag(), user.getIdentifier());
             if (cn != null) {
                 if (cn.getPackageName().equals("!")) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f98e263..2fe7eea 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -155,8 +155,7 @@
     boolean refContentProvider(in IBinder connection, int stableDelta, int unstableDelta);
     PendingIntent getRunningServiceControlPanel(in ComponentName service);
     ComponentName startService(in IApplicationThread caller, in Intent service,
-            in String resolvedType, boolean requireForeground,
-            boolean hideForegroundNotification, in String callingPackage,
+            in String resolvedType, boolean requireForeground, in String callingPackage,
             in String callingFeatureId, int userId);
     @UnsupportedAppUsage
     int stopService(in IApplicationThread caller, in Intent service,
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 4cba6ea..273336d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -40,7 +40,6 @@
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.LruCache;
 import android.util.Pair;
 import android.util.Slog;
 import android.view.Display;
@@ -63,7 +62,6 @@
 import java.util.Objects;
 import java.util.WeakHashMap;
 import java.util.function.Consumer;
-import java.util.function.Predicate;
 
 /** @hide */
 public class ResourcesManager {
@@ -130,17 +128,30 @@
         }
     }
 
-    private static final boolean ENABLE_APK_ASSETS_CACHE = false;
-
     /**
-     * The ApkAssets we are caching and intend to hold strong references to.
+     * Loads {@link ApkAssets} and caches them to prevent their garbage collection while the
+     * instance is alive and reachable.
      */
-    private final LruCache<ApkKey, ApkAssets> mLoadedApkAssets =
-            (ENABLE_APK_ASSETS_CACHE) ? new LruCache<>(3) : null;
+    private class ApkAssetsSupplier {
+        final ArrayMap<ApkKey, ApkAssets> mLocalCache = new ArrayMap<>();
+
+        /**
+         * Retrieves the {@link ApkAssets} corresponding to the specified key, caches the ApkAssets
+         * within this instance, and inserts the loaded ApkAssets into the {@link #mCachedApkAssets}
+         * cache.
+         */
+        ApkAssets load(final ApkKey apkKey) throws IOException {
+            ApkAssets apkAssets = mLocalCache.get(apkKey);
+            if (apkAssets == null) {
+                apkAssets = loadApkAssets(apkKey);
+                mLocalCache.put(apkKey, apkAssets);
+            }
+            return apkAssets;
+        }
+    }
 
     /**
-     * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't
-     * in our LRU cache. Bonus resources :)
+     * The ApkAssets that are being referenced in the wild that we can reuse.
      */
     private final ArrayMap<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>();
 
@@ -338,49 +349,78 @@
         return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap";
     }
 
-    private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay)
-            throws IOException {
-        final ApkKey newKey = new ApkKey(path, sharedLib, overlay);
-        ApkAssets apkAssets = null;
-        if (mLoadedApkAssets != null) {
-            apkAssets = mLoadedApkAssets.get(newKey);
-            if (apkAssets != null && apkAssets.isUpToDate()) {
-                return apkAssets;
-            }
-        }
+    private @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException {
+        ApkAssets apkAssets;
 
         // Optimistically check if this ApkAssets exists somewhere else.
-        final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(newKey);
-        if (apkAssetsRef != null) {
-            apkAssets = apkAssetsRef.get();
-            if (apkAssets != null && apkAssets.isUpToDate()) {
-                if (mLoadedApkAssets != null) {
-                    mLoadedApkAssets.put(newKey, apkAssets);
+        synchronized (this) {
+            final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(key);
+            if (apkAssetsRef != null) {
+                apkAssets = apkAssetsRef.get();
+                if (apkAssets != null && apkAssets.isUpToDate()) {
+                    return apkAssets;
+                } else {
+                    // Clean up the reference.
+                    mCachedApkAssets.remove(key);
                 }
-
-                return apkAssets;
-            } else {
-                // Clean up the reference.
-                mCachedApkAssets.remove(newKey);
             }
         }
 
         // We must load this from disk.
-        if (overlay) {
-            apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path), 0 /*flags*/);
+        if (key.overlay) {
+            apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(key.path),
+                    0 /*flags*/);
         } else {
-            apkAssets = ApkAssets.loadFromPath(path, sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
+            apkAssets = ApkAssets.loadFromPath(key.path,
+                    key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
         }
 
-        if (mLoadedApkAssets != null) {
-            mLoadedApkAssets.put(newKey, apkAssets);
+        synchronized (this) {
+            mCachedApkAssets.put(key, new WeakReference<>(apkAssets));
         }
 
-        mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets));
         return apkAssets;
     }
 
     /**
+     * Retrieves a list of apk keys representing the ApkAssets that should be loaded for
+     * AssetManagers mapped to the {@param key}.
+     */
+    private static @NonNull ArrayList<ApkKey> extractApkKeys(@NonNull final ResourcesKey key) {
+        final ArrayList<ApkKey> apkKeys = new ArrayList<>();
+
+        // resDir can be null if the 'android' package is creating a new Resources object.
+        // This is fine, since each AssetManager automatically loads the 'android' package
+        // already.
+        if (key.mResDir != null) {
+            apkKeys.add(new ApkKey(key.mResDir, false /*sharedLib*/, false /*overlay*/));
+        }
+
+        if (key.mSplitResDirs != null) {
+            for (final String splitResDir : key.mSplitResDirs) {
+                apkKeys.add(new ApkKey(splitResDir, false /*sharedLib*/, false /*overlay*/));
+            }
+        }
+
+        if (key.mLibDirs != null) {
+            for (final String libDir : key.mLibDirs) {
+                // Avoid opening files we know do not have resources, like code-only .jar files.
+                if (libDir.endsWith(".apk")) {
+                    apkKeys.add(new ApkKey(libDir, true /*sharedLib*/, false /*overlay*/));
+                }
+            }
+        }
+
+        if (key.mOverlayDirs != null) {
+            for (final String idmapPath : key.mOverlayDirs) {
+                apkKeys.add(new ApkKey(idmapPath, false /*sharedLib*/, true /*overlay*/));
+            }
+        }
+
+        return apkKeys;
+    }
+
+    /**
      * Creates an AssetManager from the paths within the ResourcesKey.
      *
      * This can be overridden in tests so as to avoid creating a real AssetManager with
@@ -391,64 +431,38 @@
     @VisibleForTesting
     @UnsupportedAppUsage
     protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
+        return createAssetManager(key, /* apkSupplier */ null);
+    }
+
+    /**
+     * Variant of {@link #createAssetManager(ResourcesKey)} that attempts to load ApkAssets
+     * from an {@link ApkAssetsSupplier} if non-null; otherwise ApkAssets are loaded using
+     * {@link #loadApkAssets(ApkKey)}.
+     */
+    private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
+            @Nullable ApkAssetsSupplier apkSupplier) {
         final AssetManager.Builder builder = new AssetManager.Builder();
 
-        // resDir can be null if the 'android' package is creating a new Resources object.
-        // This is fine, since each AssetManager automatically loads the 'android' package
-        // already.
-        if (key.mResDir != null) {
+        final ArrayList<ApkKey> apkKeys = extractApkKeys(key);
+        for (int i = 0, n = apkKeys.size(); i < n; i++) {
+            final ApkKey apkKey = apkKeys.get(i);
             try {
-                builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/,
-                        false /*overlay*/));
+                builder.addApkAssets(
+                        (apkSupplier != null) ? apkSupplier.load(apkKey) : loadApkAssets(apkKey));
             } catch (IOException e) {
-                Log.e(TAG, "failed to add asset path " + key.mResDir);
-                return null;
-            }
-        }
-
-        if (key.mSplitResDirs != null) {
-            for (final String splitResDir : key.mSplitResDirs) {
-                try {
-                    builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/,
-                            false /*overlay*/));
-                } catch (IOException e) {
-                    Log.e(TAG, "failed to add split asset path " + splitResDir);
+                if (apkKey.overlay) {
+                    Log.w(TAG, String.format("failed to add overlay path '%s'", apkKey.path), e);
+                } else if (apkKey.sharedLib) {
+                    Log.w(TAG, String.format(
+                            "asset path '%s' does not exist or contains no resources",
+                            apkKey.path), e);
+                } else {
+                    Log.e(TAG, String.format("failed to add asset path '%s'", apkKey.path), e);
                     return null;
                 }
             }
         }
 
-        if (key.mLibDirs != null) {
-            for (final String libDir : key.mLibDirs) {
-                if (libDir.endsWith(".apk")) {
-                    // Avoid opening files we know do not have resources,
-                    // like code-only .jar files.
-                    try {
-                        builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/,
-                                false /*overlay*/));
-                    } catch (IOException e) {
-                        Log.w(TAG, "Asset path '" + libDir +
-                                "' does not exist or contains no resources.");
-
-                        // continue.
-                    }
-                }
-            }
-        }
-
-        if (key.mOverlayDirs != null) {
-            for (final String idmapPath : key.mOverlayDirs) {
-                try {
-                    builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
-                            true /*overlay*/));
-                } catch (IOException e) {
-                    Log.w(TAG, "failed to add overlay path " + idmapPath);
-
-                    // continue.
-                }
-            }
-        }
-
         if (key.mLoaders != null) {
             for (final ResourcesLoader loader : key.mLoaders) {
                 builder.addLoader(loader);
@@ -481,24 +495,6 @@
 
             pw.println("ResourcesManager:");
             pw.increaseIndent();
-            if (mLoadedApkAssets != null) {
-                pw.print("cached apks: total=");
-                pw.print(mLoadedApkAssets.size());
-                pw.print(" created=");
-                pw.print(mLoadedApkAssets.createCount());
-                pw.print(" evicted=");
-                pw.print(mLoadedApkAssets.evictionCount());
-                pw.print(" hit=");
-                pw.print(mLoadedApkAssets.hitCount());
-                pw.print(" miss=");
-                pw.print(mLoadedApkAssets.missCount());
-                pw.print(" max=");
-                pw.print(mLoadedApkAssets.maxSize());
-            } else {
-                pw.print("cached apks: 0 [cache disabled]");
-            }
-            pw.println();
-
             pw.print("total apks: ");
             pw.println(countLiveReferences(mCachedApkAssets.values()));
 
@@ -534,11 +530,12 @@
         return config;
     }
 
-    private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
+    private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key,
+            @Nullable ApkAssetsSupplier apkSupplier) {
         final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
         daj.setCompatibilityInfo(key.mCompatInfo);
 
-        final AssetManager assets = createAssetManager(key);
+        final AssetManager assets = createAssetManager(key, apkSupplier);
         if (assets == null) {
             return null;
         }
@@ -576,9 +573,18 @@
      */
     private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
             @NonNull ResourcesKey key) {
+        return findOrCreateResourcesImplForKeyLocked(key, /* apkSupplier */ null);
+    }
+
+    /**
+     * Variant of {@link #findOrCreateResourcesImplForKeyLocked(ResourcesKey)} that attempts to
+     * load ApkAssets from a {@link ApkAssetsSupplier} when creating a new ResourcesImpl.
+     */
+    private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
+            @NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
         ResourcesImpl impl = findResourcesImplForKeyLocked(key);
         if (impl == null) {
-            impl = createResourcesImpl(key);
+            impl = createResourcesImpl(key, apkSupplier);
             if (impl != null) {
                 mResourceImpls.put(key, new WeakReference<>(impl));
             }
@@ -767,7 +773,7 @@
             }
 
             // Now request an actual Resources object.
-            return createResources(token, key, classLoader);
+            return createResources(token, key, classLoader, /* apkSupplier */ null);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
@@ -811,18 +817,45 @@
     }
 
     /**
+     * Creates an {@link ApkAssetsSupplier} and loads all the ApkAssets required by the {@param key}
+     * into the supplier. This should be done while the lock is not held to prevent performing I/O
+     * while holding the lock.
+     */
+    private @NonNull ApkAssetsSupplier createApkAssetsSupplierNotLocked(@NonNull ResourcesKey key) {
+        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                "ResourcesManager#createApkAssetsSupplierNotLocked");
+        try {
+            final ApkAssetsSupplier supplier = new ApkAssetsSupplier();
+            final ArrayList<ApkKey> apkKeys = extractApkKeys(key);
+            for (int i = 0, n = apkKeys.size(); i < n; i++) {
+                final ApkKey apkKey = apkKeys.get(i);
+                try {
+                    supplier.load(apkKey);
+                } catch (IOException e) {
+                    Log.w(TAG, String.format("failed to preload asset path '%s'", apkKey.path), e);
+                }
+            }
+            return supplier;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
+    }
+
+    /**
      * Creates a Resources object set with a ResourcesImpl object matching the given key.
      *
      * @param activityToken The Activity this Resources object should be associated with.
      * @param key The key describing the parameters of the ResourcesImpl object.
      * @param classLoader The classloader to use for the Resources object.
      *                    If null, {@link ClassLoader#getSystemClassLoader()} is used.
+     * @param apkSupplier The apk assets supplier to use when creating a new ResourcesImpl object.
      * @return A Resources object that gets updated when
      *         {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
      *         is called.
      */
     private @Nullable Resources createResources(@Nullable IBinder activityToken,
-            @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
+            @NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
+            @Nullable ApkAssetsSupplier apkSupplier) {
         synchronized (this) {
             if (DEBUG) {
                 Throwable here = new Throwable();
@@ -830,7 +863,7 @@
                 Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
             }
 
-            ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key);
+            ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
             if (resourcesImpl == null) {
                 return null;
             }
@@ -899,7 +932,10 @@
                 rebaseKeyForActivity(activityToken, key);
             }
 
-            return createResources(activityToken, key, classLoader);
+            // Preload the ApkAssets required by the key to prevent performing heavy I/O while the
+            // ResourcesManager lock is held.
+            final ApkAssetsSupplier assetsSupplier = createApkAssetsSupplierNotLocked(key);
+            return createResources(activityToken, key, classLoader, assetsSupplier);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
@@ -970,7 +1006,13 @@
                     final ResourcesKey newKey = rebaseActivityOverrideConfig(resources, oldConfig,
                             overrideConfig, displayId);
                     if (newKey != null) {
-                        updateActivityResources(resources, newKey, false);
+                        final ResourcesImpl resourcesImpl =
+                                findOrCreateResourcesImplForKeyLocked(newKey);
+                        if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
+                            // Set the ResourcesImpl, updating it for all users of this Resources
+                            // object.
+                            resources.setImpl(resourcesImpl);
+                        }
                     }
                 }
             }
@@ -1025,34 +1067,22 @@
         return newKey;
     }
 
-    private void updateActivityResources(Resources resources, ResourcesKey newKey,
-            boolean hasLoader) {
-        final ResourcesImpl resourcesImpl;
-
-        if (hasLoader) {
-            // Loaders always get new Impls because they cannot be shared
-            resourcesImpl = createResourcesImpl(newKey);
-        } else {
-            resourcesImpl = findOrCreateResourcesImplForKeyLocked(newKey);
-        }
-
-        if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
-            // Set the ResourcesImpl, updating it for all users of this Resources
-            // object.
-            resources.setImpl(resourcesImpl);
-        }
-    }
-
     @TestApi
     public final boolean applyConfigurationToResources(@NonNull Configuration config,
             @Nullable CompatibilityInfo compat) {
         synchronized(this) {
-            return applyConfigurationToResourcesLocked(config, compat);
+            return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
         }
     }
 
     public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
-                                                             @Nullable CompatibilityInfo compat) {
+            @Nullable CompatibilityInfo compat) {
+        return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
+    }
+
+    /** Applies the global configuration to the managed resources. */
+    public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
+            @Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) {
         try {
             Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
                     "ResourcesManager#applyConfigurationToResourcesLocked");
@@ -1076,6 +1106,11 @@
                         | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
             }
 
+            if (adjustments != null) {
+                // Currently the only case where the adjustment takes effect is to simulate placing
+                // an app in a rotated display.
+                adjustments.adjustGlobalAppMetrics(defaultDisplayMetrics);
+            }
             Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
 
             ApplicationPackageManager.configurationChanged();
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 7c6eff1..06d1b74 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -510,6 +510,9 @@
     }
 
     /**
+     * Activating night mode for the current user
+     *
+     * @return {@code true} if the change is successful
      * @hide
      */
     public boolean setNightModeActivated(boolean active) {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index f257326..2138f53 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -785,4 +785,6 @@
     List<String> getMimeGroup(String packageName, String group);
 
     boolean isAutoRevokeWhitelisted(String packageName);
+
+    void grantImplicitAccess(int queryingUid, String visibleAuthority);
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ea4a2a0..8a7214d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8011,6 +8011,20 @@
                 "getMimeGroup not implemented in subclass");
     }
 
+    /**
+     * Grants implicit visibility of the package that provides an authority to a querying UID.
+     *
+     * @throws SecurityException when called by a package other than the contacts provider
+     * @hide
+     */
+    public void grantImplicitAccess(int queryingUid, String visibleAuthority) {
+        try {
+            ActivityThread.getPackageManager().grantImplicitAccess(queryingUid, visibleAuthority);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     // Some of the flags don't affect the query result, but let's be conservative and cache
     // each combination of flags separately.
 
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4f0c84e..e0195e4 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -16,11 +16,11 @@
 
 package android.inputmethodservice;
 
+import static android.graphics.Color.TRANSPARENT;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
 import static android.view.WindowInsets.Type.navigationBars;
-import static android.view.WindowInsets.Type.statusBars;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -69,7 +69,6 @@
 import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
 import android.view.Window;
-import android.view.WindowInsets;
 import android.view.WindowInsets.Side;
 import android.view.WindowManager;
 import android.view.animation.AnimationUtils;
@@ -1203,25 +1202,22 @@
                 Context.LAYOUT_INFLATER_SERVICE);
         mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
                 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
-        mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
+        mWindow.getWindow().getAttributes().setFitInsetsTypes(navigationBars());
         mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
         mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true);
 
-        // IME layout should always be inset by navigation bar, no matter its current visibility,
-        // unless automotive requests it. Automotive devices may request the navigation bar to be
-        // hidden when the IME shows up (controlled via config_automotiveHideNavBarForKeyboard)
-        // in order to maximize the visible screen real estate. When this happens, the IME window
-        // should animate from the bottom of the screen to reduce the jank that happens from the
-        // lack of synchronization between the bottom system window and the IME window.
+        // Our window will extend into the status bar area no matter the bar is visible or not.
+        // We don't want the ColorView to be visible when status bar is shown.
+        mWindow.getWindow().setStatusBarColor(TRANSPARENT);
+
+        // Automotive devices may request the navigation bar to be hidden when the IME shows up
+        // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the visible
+        // screen real estate. When this happens, the IME window should animate from the bottom of
+        // the screen to reduce the jank that happens from the lack of synchronization between the
+        // bottom system window and the IME window.
         if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) {
             mWindow.getWindow().setDecorFitsSystemWindows(false);
         }
-        mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener(
-                (v, insets) -> v.onApplyWindowInsets(
-                        new WindowInsets.Builder(insets).setInsets(
-                                navigationBars(),
-                                insets.getInsetsIgnoringVisibility(navigationBars()))
-                                .build()));
 
         // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
         // by default (but IME developers can opt this out later if they want a new behavior).
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index bde3327..018bb2c 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -135,8 +135,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8b45ba9..b07dabd 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8980,6 +8980,22 @@
         };
 
         /**
+         * How long Assistant handles have enabled in milliseconds.
+         *
+         * @hide
+         */
+        public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS =
+                "reminder_exp_learning_time_elapsed";
+
+        /**
+         * How many times the Assistant has been triggered using the touch gesture.
+         *
+         * @hide
+         */
+        public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT =
+                "reminder_exp_learning_event_count";
+
+        /**
          * These entries are considered common between the personal and the managed profile,
          * since the managed profile doesn't get to change them.
          */
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index c726bee..7c01f7a8 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -130,14 +130,16 @@
         w = metrics.noncompatWidthPixels;
         metrics.noncompatWidthPixels = metrics.noncompatHeightPixels;
         metrics.noncompatHeightPixels = w;
+    }
 
-        float x = metrics.xdpi;
-        metrics.xdpi = metrics.ydpi;
-        metrics.ydpi = x;
-
-        x = metrics.noncompatXdpi;
-        metrics.noncompatXdpi = metrics.noncompatYdpi;
-        metrics.noncompatYdpi = x;
+    /** Adjusts global display metrics that is available to applications. */
+    public void adjustGlobalAppMetrics(@NonNull DisplayMetrics metrics) {
+        final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
+        if (rotationAdjustments == null) {
+            return;
+        }
+        metrics.noncompatWidthPixels = metrics.widthPixels = rotationAdjustments.mAppWidth;
+        metrics.noncompatHeightPixels = metrics.heightPixels = rotationAdjustments.mAppHeight;
     }
 
     /** Returns the adjusted cutout if available. Otherwise the original cutout is returned. */
@@ -178,7 +180,7 @@
 
     /**
      * An application can be launched in different rotation than the real display. This class
-     * provides the information to adjust the values returned by {@link #Display}.
+     * provides the information to adjust the values returned by {@link Display}.
      * @hide
      */
     public static class FixedRotationAdjustments implements Parcelable {
@@ -186,12 +188,24 @@
         @Surface.Rotation
         final int mRotation;
 
+        /**
+         * The rotated {@link DisplayInfo#appWidth}. The value cannot be simply swapped according
+         * to rotation because it minus the region of screen decorations.
+         */
+        final int mAppWidth;
+
+        /** The rotated {@link DisplayInfo#appHeight}. */
+        final int mAppHeight;
+
         /** Non-null if the device has cutout. */
         @Nullable
         final DisplayCutout mRotatedDisplayCutout;
 
-        public FixedRotationAdjustments(@Surface.Rotation int rotation, DisplayCutout cutout) {
+        public FixedRotationAdjustments(@Surface.Rotation int rotation, int appWidth, int appHeight,
+                DisplayCutout cutout) {
             mRotation = rotation;
+            mAppWidth = appWidth;
+            mAppHeight = appHeight;
             mRotatedDisplayCutout = cutout;
         }
 
@@ -199,6 +213,8 @@
         public int hashCode() {
             int hash = 17;
             hash = hash * 31 + mRotation;
+            hash = hash * 31 + mAppWidth;
+            hash = hash * 31 + mAppHeight;
             hash = hash * 31 + Objects.hashCode(mRotatedDisplayCutout);
             return hash;
         }
@@ -210,12 +226,14 @@
             }
             final FixedRotationAdjustments other = (FixedRotationAdjustments) o;
             return mRotation == other.mRotation
+                    && mAppWidth == other.mAppWidth && mAppHeight == other.mAppHeight
                     && Objects.equals(mRotatedDisplayCutout, other.mRotatedDisplayCutout);
         }
 
         @Override
         public String toString() {
             return "FixedRotationAdjustments{rotation=" + Surface.rotationToString(mRotation)
+                    + " appWidth=" + mAppWidth + " appHeight=" + mAppHeight
                     + " cutout=" + mRotatedDisplayCutout + "}";
         }
 
@@ -227,12 +245,16 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(mRotation);
+            dest.writeInt(mAppWidth);
+            dest.writeInt(mAppHeight);
             dest.writeTypedObject(
                     new DisplayCutout.ParcelableWrapper(mRotatedDisplayCutout), flags);
         }
 
         private FixedRotationAdjustments(Parcel in) {
             mRotation = in.readInt();
+            mAppWidth = in.readInt();
+            mAppHeight = in.readInt();
             final DisplayCutout.ParcelableWrapper cutoutWrapper =
                     in.readTypedObject(DisplayCutout.ParcelableWrapper.CREATOR);
             mRotatedDisplayCutout = cutoutWrapper != null ? cutoutWrapper.get() : null;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 0b43547..8137883 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -76,6 +76,10 @@
             ITYPE_BOTTOM_GESTURES,
             ITYPE_LEFT_GESTURES,
             ITYPE_RIGHT_GESTURES,
+            ITYPE_TOP_MANDATORY_GESTURES,
+            ITYPE_BOTTOM_MANDATORY_GESTURES,
+            ITYPE_LEFT_MANDATORY_GESTURES,
+            ITYPE_RIGHT_MANDATORY_GESTURES,
             ITYPE_TOP_TAPPABLE_ELEMENT,
             ITYPE_BOTTOM_TAPPABLE_ELEMENT,
             ITYPE_LEFT_DISPLAY_CUTOUT,
@@ -104,20 +108,27 @@
     public static final int ITYPE_BOTTOM_GESTURES = 4;
     public static final int ITYPE_LEFT_GESTURES = 5;
     public static final int ITYPE_RIGHT_GESTURES = 6;
-    public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 7;
-    public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 8;
 
-    public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 9;
-    public static final int ITYPE_TOP_DISPLAY_CUTOUT = 10;
-    public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 11;
-    public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 12;
+    /** Additional gesture inset types that map into {@link Type.MANDATORY_SYSTEM_GESTURES}. */
+    public static final int ITYPE_TOP_MANDATORY_GESTURES = 7;
+    public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8;
+    public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
+    public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;
+
+    public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 11;
+    public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 12;
+
+    public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 13;
+    public static final int ITYPE_TOP_DISPLAY_CUTOUT = 14;
+    public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 15;
+    public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 16;
 
     /** Input method window. */
-    public static final int ITYPE_IME = 13;
+    public static final int ITYPE_IME = 17;
 
     /** Additional system decorations inset type. */
-    public static final int ITYPE_CLIMATE_BAR = 14;
-    public static final int ITYPE_EXTRA_NAVIGATION_BAR = 15;
+    public static final int ITYPE_CLIMATE_BAR = 18;
+    public static final int ITYPE_EXTRA_NAVIGATION_BAR = 19;
 
     static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
     public static final int SIZE = LAST_TYPE + 1;
@@ -493,6 +504,10 @@
                 return Type.IME;
             case ITYPE_TOP_GESTURES:
             case ITYPE_BOTTOM_GESTURES:
+            case ITYPE_TOP_MANDATORY_GESTURES:
+            case ITYPE_BOTTOM_MANDATORY_GESTURES:
+            case ITYPE_LEFT_MANDATORY_GESTURES:
+            case ITYPE_RIGHT_MANDATORY_GESTURES:
                 return Type.MANDATORY_SYSTEM_GESTURES;
             case ITYPE_LEFT_GESTURES:
             case ITYPE_RIGHT_GESTURES:
@@ -552,6 +567,14 @@
                 return "ITYPE_LEFT_GESTURES";
             case ITYPE_RIGHT_GESTURES:
                 return "ITYPE_RIGHT_GESTURES";
+            case ITYPE_TOP_MANDATORY_GESTURES:
+                return "ITYPE_TOP_MANDATORY_GESTURES";
+            case ITYPE_BOTTOM_MANDATORY_GESTURES:
+                return "ITYPE_BOTTOM_MANDATORY_GESTURES";
+            case ITYPE_LEFT_MANDATORY_GESTURES:
+                return "ITYPE_LEFT_MANDATORY_GESTURES";
+            case ITYPE_RIGHT_MANDATORY_GESTURES:
+                return "ITYPE_RIGHT_MANDATORY_GESTURES";
             case ITYPE_TOP_TAPPABLE_ELEMENT:
                 return "ITYPE_TOP_TAPPABLE_ELEMENT";
             case ITYPE_BOTTOM_TAPPABLE_ELEMENT:
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index ffeeb80..8fd2f07f 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -99,7 +99,7 @@
      * Defines the duration in milliseconds a user needs to hold down the
      * appropriate buttons (power + volume down) to trigger the screenshot chord.
      */
-    private static final int SCREENSHOT_CHORD_KEY_TIMEOUT = 500;
+    private static final int SCREENSHOT_CHORD_KEY_TIMEOUT = 0;
 
     /**
      * Defines the duration in milliseconds a user needs to hold down the
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4a56761..3763728 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1981,11 +1981,7 @@
             mCompatibleVisibilityInfo.globalVisibility =
                     (mCompatibleVisibilityInfo.globalVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE)
                             | (mAttachInfo.mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
-            if (mDispatchedSystemUiVisibility != mCompatibleVisibilityInfo.globalVisibility) {
-                mHandler.removeMessages(MSG_DISPATCH_SYSTEM_UI_VISIBILITY);
-                mHandler.sendMessage(mHandler.obtainMessage(
-                        MSG_DISPATCH_SYSTEM_UI_VISIBILITY, mCompatibleVisibilityInfo));
-            }
+            dispatchDispatchSystemUiVisibilityChanged(mCompatibleVisibilityInfo);
             if (mAttachInfo.mKeepScreenOn != oldScreenOn
                     || mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
                     || mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
@@ -2039,9 +2035,30 @@
             info.globalVisibility |= systemUiFlag;
             info.localChanges &= ~systemUiFlag;
         }
-        if (mDispatchedSystemUiVisibility != info.globalVisibility) {
+        dispatchDispatchSystemUiVisibilityChanged(info);
+    }
+
+    /**
+     * If the system is forcing showing any system bar, the legacy low profile flag should be
+     * cleared for compatibility.
+     *
+     * @param showTypes {@link InsetsType types} shown by the system.
+     * @param fromIme {@code true} if the invocation is from IME.
+     */
+    private void clearLowProfileModeIfNeeded(@InsetsType int showTypes, boolean fromIme) {
+        final SystemUiVisibilityInfo info = mCompatibleVisibilityInfo;
+        if ((showTypes & Type.systemBars()) != 0 && !fromIme
+                && (info.globalVisibility & SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+            info.globalVisibility &= ~SYSTEM_UI_FLAG_LOW_PROFILE;
+            info.localChanges |= SYSTEM_UI_FLAG_LOW_PROFILE;
+            dispatchDispatchSystemUiVisibilityChanged(info);
+        }
+    }
+
+    private void dispatchDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) {
+        if (mDispatchedSystemUiVisibility != args.globalVisibility) {
             mHandler.removeMessages(MSG_DISPATCH_SYSTEM_UI_VISIBILITY);
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, info));
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args));
         }
     }
 
@@ -5008,6 +5025,7 @@
                                 String.format("Calling showInsets(%d,%b) on window that no longer"
                                         + " has views.", msg.arg1, msg.arg2 == 1));
                     }
+                    clearLowProfileModeIfNeeded(msg.arg1, msg.arg2 == 1);
                     mInsetsController.show(msg.arg1, msg.arg2 == 1);
                     break;
                 }
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 1a90035..24538c5 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -33,7 +33,6 @@
 /**
  * Interface to control windows that generate insets.
  *
- * TODO(118118435): Needs more information and examples once the API is more baked.
  */
 public interface WindowInsetsController {
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index fe77478..76c2f2e 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -273,8 +273,6 @@
     private int mLastNumberOfChildren = -1;
 
     private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
-    // TODO: Update to handle landscape instead of using static value
-    private static final int MAX_RANKED_TARGETS = 4;
 
     private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
     private final Set<Pair<ComponentName, UserHandle>> mServicesRequested = new HashSet<>();
@@ -951,7 +949,7 @@
         updateStickyContentPreview();
         if (shouldShowStickyContentPreview()
                 || mChooserMultiProfilePagerAdapter
-                        .getCurrentRootAdapter().getContentPreviewRowCount() != 0) {
+                        .getCurrentRootAdapter().getSystemRowCount() != 0) {
             logActionShareWithPreview();
         }
         return postRebuildListInternal(rebuildCompleted);
@@ -1316,13 +1314,14 @@
             ViewGroup parent) {
         ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(
                 R.layout.chooser_grid_preview_image, parent, false);
+        ViewGroup imagePreview = contentPreviewLayout.findViewById(R.id.content_preview_image_area);
 
         final ViewGroup actionRow =
                 (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row);
         //TODO: addActionButton(actionRow, createCopyButton());
         addActionButton(actionRow, createNearbyButton(targetIntent));
 
-        mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, true);
+        mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false);
 
         String action = targetIntent.getAction();
         if (Intent.ACTION_SEND.equals(action)) {
@@ -1342,7 +1341,7 @@
             if (imageUris.size() == 0) {
                 Log.i(TAG, "Attempted to display image preview area with zero"
                         + " available images detected in EXTRA_STREAM list");
-                contentPreviewLayout.setVisibility(View.GONE);
+                imagePreview.setVisibility(View.GONE);
                 return contentPreviewLayout;
             }
 
@@ -2683,7 +2682,7 @@
                 final int bottomInset = mSystemWindowInsets != null
                                             ? mSystemWindowInsets.bottom : 0;
                 int offset = bottomInset;
-                int rowsToShow = gridAdapter.getContentPreviewRowCount()
+                int rowsToShow = gridAdapter.getSystemRowCount()
                         + gridAdapter.getProfileRowCount()
                         + gridAdapter.getServiceTargetRowCount()
                         + gridAdapter.getCallerAndRankedTargetRowCount();
@@ -3283,7 +3282,7 @@
 
         public int getRowCount() {
             return (int) (
-                    getContentPreviewRowCount()
+                    getSystemRowCount()
                             + getProfileRowCount()
                             + getServiceTargetRowCount()
                             + getCallerAndRankedTargetRowCount()
@@ -3295,22 +3294,21 @@
         }
 
         /**
-         * Returns either {@code 0} or {@code 1} depending on whether we want to show the list item
-         * content preview. Not to be confused with the sticky content preview which is above the
-         * personal and work tabs.
+         * Whether the "system" row of targets is displayed.
+         * This area includes the content preview (if present) and action row.
          */
-        public int getContentPreviewRowCount() {
+        public int getSystemRowCount() {
             // For the tabbed case we show the sticky content preview above the tabs,
             // please refer to shouldShowStickyContentPreview
             if (shouldShowTabs()) {
                 return 0;
             }
+
             if (!isSendAction(getTargetIntent())) {
                 return 0;
             }
 
-            if (mHideContentPreview || mChooserListAdapter == null
-                    || mChooserListAdapter.getCount() == 0) {
+            if (mChooserListAdapter == null || mChooserListAdapter.getCount() == 0) {
                 return 0;
             }
 
@@ -3352,7 +3350,7 @@
         @Override
         public int getItemCount() {
             return (int) (
-                    getContentPreviewRowCount()
+                    getSystemRowCount()
                             + getProfileRowCount()
                             + getServiceTargetRowCount()
                             + getCallerAndRankedTargetRowCount()
@@ -3407,7 +3405,7 @@
         public int getItemViewType(int position) {
             int count;
 
-            int countSum = (count = getContentPreviewRowCount());
+            int countSum = (count = getSystemRowCount());
             if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW;
 
             countSum += (count = getProfileRowCount());
@@ -3631,7 +3629,7 @@
         }
 
         int getListPosition(int position) {
-            position -= getContentPreviewRowCount() + getProfileRowCount();
+            position -= getSystemRowCount() + getProfileRowCount();
 
             final int serviceCount = mChooserListAdapter.getServiceTargetCount();
             final int serviceRows = (int) Math.ceil((float) serviceCount
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index eb59f0f..b4cd145 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -390,6 +390,20 @@
     public static final String CHOOSER_TARGET_RANKING_ENABLED = "chooser_target_ranking_enabled";
 
     /**
+     * (float) Weight bonus applied on top sharing shortcuts as per native ranking provided by apps.
+     * Its range need to be 0 ~ 1.
+     */
+    public static final String TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER =
+            "top_native_ranked_sharing_shortcut_booster";
+
+    /**
+     * (float) Weight bonus applied on 2nd top sharing shortcuts as per native ranking provided by
+     * apps. Its range need to be 0 ~ 1.
+     */
+    public static final String NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER =
+            "non_top_native_ranked_sharing_shortcut_booster";
+
+    /**
      * (boolean) Whether to enable user-drag resizing for PIP.
      */
     public static final String PIP_USER_RESIZE = "pip_user_resize";
@@ -409,6 +423,11 @@
      */
     public static final String BACK_GESTURE_SLOP_MULTIPLIER = "back_gesture_slop_multiplier";
 
+    /**
+     * (long) Screenshot keychord delay (how long the buttons must be pressed), in ms
+     */
+    public static final String SCREENSHOT_KEYCHORD_DELAY = "screenshot_keychord_delay";
+
     private SystemUiDeviceConfigFlags() {
     }
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 3bcba75..37f6823 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -224,8 +224,6 @@
                 return "HIDE_DOCKED_STACK_ATTACHED";
             case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION:
                 return "HIDE_RECENTS_ANIMATION";
-            case SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR:
-                return "HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR";
             default:
                 return "Unknown=" + reason;
         }
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index f46626b..4b968b4 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -47,8 +47,7 @@
         SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME,
         SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED,
         SoftInputShowHideReason.HIDE_RECENTS_ANIMATION,
-        SoftInputShowHideReason.HIDE_BUBBLES,
-        SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR})
+        SoftInputShowHideReason.HIDE_BUBBLES})
 public @interface SoftInputShowHideReason {
     /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
     int SHOW_SOFT_INPUT = 0;
@@ -148,17 +147,4 @@
      * switching, or collapsing Bubbles.
      */
     int HIDE_BUBBLES = 19;
-
-    /**
-     * Hide soft input when focusing the same window (e.g. screen turned-off and turn-on) which no
-     * valid focused editor.
-     *
-     * Note: From Android R, the window focus change callback is processed by InputDispatcher,
-     * some focus behavior changes (e.g. There are an activity with a dialog window, after
-     * screen turned-off and turned-on, before Android R the window focus sequence would be
-     * the activity first and then the dialog focused, however, in R the focus sequence would be
-     * only the dialog focused as it's the latest window with input focus) makes we need to hide
-     * soft-input when the same window focused again to align with the same behavior prior to R.
-     */
-    int HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR = 20;
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5a1af84..2ac7e5f 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -151,7 +151,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    static final int VERSION = 186 + (USE_OLD_HISTORY ? 1000 : 0);
+    static final int VERSION = 188 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -13596,6 +13596,7 @@
         mDailyStartTime = in.readLong();
         mNextMinDailyDeadline = in.readLong();
         mNextMaxDailyDeadline = in.readLong();
+        mBatteryTimeToFullSeconds = in.readLong();
 
         mStartCount++;
 
@@ -14086,6 +14087,7 @@
         out.writeLong(mDailyStartTime);
         out.writeLong(mNextMinDailyDeadline);
         out.writeLong(mNextMaxDailyDeadline);
+        out.writeLong(mBatteryTimeToFullSeconds);
 
         mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -14669,6 +14671,7 @@
         mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mLastWriteTime = in.readLong();
+        mBatteryTimeToFullSeconds = in.readLong();
 
         mRpmStats.clear();
         int NRPMS = in.readInt();
@@ -14861,6 +14864,7 @@
         mDischargeLightDozeCounter.writeToParcel(out);
         mDischargeDeepDozeCounter.writeToParcel(out);
         out.writeLong(mLastWriteTime);
+        out.writeLong(mBatteryTimeToFullSeconds);
 
         out.writeInt(mRpmStats.size());
         for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index b12c5e9..fbbf791 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1094,13 +1094,15 @@
             mLastWindowFlags = attrs.flags;
 
             if (insets != null) {
-                final Insets systemBarInsets = insets.getInsets(WindowInsets.Type.systemBars());
                 final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
                         WindowInsets.Type.systemBars());
-                mLastTopInset = systemBarInsets.top;
-                mLastBottomInset = systemBarInsets.bottom;
-                mLastRightInset = systemBarInsets.right;
-                mLastLeftInset = systemBarInsets.left;
+                final Insets systemInsets = Insets.min(
+                        insets.getInsets(WindowInsets.Type.systemBars()
+                                | WindowInsets.Type.displayCutout()), stableBarInsets);
+                mLastTopInset = systemInsets.top;
+                mLastBottomInset = systemInsets.bottom;
+                mLastRightInset = systemInsets.right;
+                mLastLeftInset = systemInsets.left;
 
                 // Don't animate if the presence of stable insets has changed, because that
                 // indicates that the window was either just added and received them for the
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index acf8cc4..cfd428c 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -119,6 +119,14 @@
     }
     optional Assist assist = 7;
 
+    message AssistHandles {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto learning_time_elapsed_millis = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto learning_event_count = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional AssistHandles assist_handles = 86;
+
     message Autofill {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -596,5 +604,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 85;
+    // Next tag = 87;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d0ca0a8..6a92a83 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5036,6 +5036,10 @@
     <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
                 android:protectionLevel="signature|appPredictor" />
 
+    <!-- @hide Allows an application to create/destroy input consumer. -->
+    <permission android:name="android.permission.INPUT_CONSUMER"
+                android:protectionLevel="signature" />
+
     <!-- Attribution for Country Detector. -->
     <attribution android:tag="CountryDetector" android:label="@string/country_detector"/>
     <!-- Attribution for Location service. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 186af6e..550601a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2256,7 +2256,7 @@
 
     <!-- Amount of time in ms the user needs to press the relevant keys to trigger the
          screenshot chord -->
-    <integer name="config_screenshotChordKeyTimeout">500</integer>
+    <integer name="config_screenshotChordKeyTimeout">0</integer>
 
     <!-- Default width of a vertical scrollbar and height of a horizontal scrollbar.
          Takes effect only if the scrollbar drawables have no intrinsic size. -->
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index f11adef..7d2e32a 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -191,7 +191,7 @@
         PersistableBundle persistableBundle = new PersistableBundle();
         persistableBundle.putInt("k", 4);
         FixedRotationAdjustments fixedRotationAdjustments = new FixedRotationAdjustments(
-                Surface.ROTATION_90, DisplayCutout.NO_CUTOUT);
+                Surface.ROTATION_90, 1920, 1080, DisplayCutout.NO_CUTOUT);
 
         LaunchActivityItem item = LaunchActivityItem.obtain(intent, ident, activityInfo,
                 config(), overrideConfig, compat, referrer, null /* voiceInteractor */,
@@ -351,7 +351,8 @@
         ClientTransaction transaction = ClientTransaction.obtain(new StubAppThread(),
                 null /* activityToken */);
         transaction.addCallback(FixedRotationAdjustmentsItem.obtain(new Binder(),
-                new FixedRotationAdjustments(Surface.ROTATION_270, DisplayCutout.NO_CUTOUT)));
+                new FixedRotationAdjustments(Surface.ROTATION_270, 1920, 1080,
+                        DisplayCutout.NO_CUTOUT)));
 
         writeAndPrepareForReading(transaction);
 
diff --git a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
index 2fc42e9..3cf1722 100644
--- a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
+++ b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
@@ -77,8 +77,10 @@
         final int realRotation = Surface.ROTATION_0;
         final int fixedRotation = Surface.ROTATION_90;
 
-        mDisplayAdjustments.setFixedRotationAdjustments(
-                new FixedRotationAdjustments(fixedRotation, null /* cutout */));
+        final int appWidth = 1080;
+        final int appHeight = 1920;
+        mDisplayAdjustments.setFixedRotationAdjustments(new FixedRotationAdjustments(
+                fixedRotation, appWidth, appHeight, null /* cutout */));
 
         final int w = 1000;
         final int h = 2000;
@@ -95,13 +97,21 @@
         metrics.heightPixels = metrics.noncompatHeightPixels = h;
 
         final DisplayMetrics flippedMetrics = new DisplayMetrics();
-        flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = h;
+        // The physical dpi should not be adjusted.
+        flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = w;
         flippedMetrics.widthPixels = flippedMetrics.noncompatWidthPixels = h;
-        flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = w;
+        flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = h;
         flippedMetrics.heightPixels = flippedMetrics.noncompatHeightPixels = w;
 
         mDisplayAdjustments.adjustMetrics(metrics, realRotation);
 
         assertEquals(flippedMetrics, metrics);
+
+        mDisplayAdjustments.adjustGlobalAppMetrics(metrics);
+
+        assertEquals(appWidth, metrics.widthPixels);
+        assertEquals(appWidth, metrics.noncompatWidthPixels);
+        assertEquals(appHeight, metrics.heightPixels);
+        assertEquals(appHeight, metrics.noncompatHeightPixels);
     }
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 5f34a5e..950666b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -168,6 +168,7 @@
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
         <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS" />
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.PACKAGE_USAGE_STATS"/>
         <permission name="android.permission.PERFORM_CDMA_PROVISIONING"/>
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 6bf6034..b77a249 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -732,7 +732,7 @@
                     mContext.getAttributionTag(), transport.getListenerId());
             if (cancelRemote != null) {
                 transport.register(mContext.getSystemService(AlarmManager.class),
-                        cancellationSignal);
+                        cancellationSignal, cancelRemote);
                 if (cancellationSignal != null) {
                     cancellationSignal.setRemote(cancelRemote);
                 }
@@ -2571,7 +2571,8 @@
         }
 
         public synchronized void register(AlarmManager alarmManager,
-                CancellationSignal cancellationSignal) {
+                CancellationSignal cancellationSignal,
+                ICancellationSignal remoteCancellationSignal) {
             if (mConsumer == null) {
                 return;
             }
@@ -2587,15 +2588,21 @@
             if (cancellationSignal != null) {
                 cancellationSignal.setOnCancelListener(this);
             }
+
+            mRemoteCancellationSignal = remoteCancellationSignal;
         }
 
         @Override
         public void onCancel() {
+            synchronized (this) {
+                mRemoteCancellationSignal = null;
+            }
             remove();
         }
 
         private Consumer<Location> remove() {
             Consumer<Location> consumer;
+            ICancellationSignal cancellationSignal;
             synchronized (this) {
                 mExecutor = null;
                 consumer = mConsumer;
@@ -2605,6 +2612,18 @@
                     mAlarmManager.cancel(this);
                     mAlarmManager = null;
                 }
+
+                // ensure only one cancel event will go through
+                cancellationSignal = mRemoteCancellationSignal;
+                mRemoteCancellationSignal = null;
+            }
+
+            if (cancellationSignal != null) {
+                try {
+                    cancellationSignal.cancel();
+                } catch (RemoteException e) {
+                    // ignore
+                }
             }
 
             return consumer;
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 3c2be5f..dba86d9 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -185,7 +185,7 @@
                 boolean bound = false;
                 try {
                     bound = mContext.bindService(intent, mServiceConnection,
-                            Context.BIND_AUTO_CREATE);
+                            Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES);
                 } catch (Exception ex) {
                     Log.e(TAG, "Failed binding to service " + mServiceComponent);
                 }
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 2c97889..e7295aa 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -25,7 +25,8 @@
     <ViewStub android:id="@+id/notification_panel_stub"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:layout="@layout/notification_panel_container"/>
+              android:layout="@layout/notification_panel_container"
+              android:layout_marginBottom="@dimen/car_bottom_navigation_bar_height"/>
 
     <ViewStub android:id="@+id/keyguard_stub"
               android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index 8359dac..e8ac9e1 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -194,6 +194,12 @@
     <dimen name="car_navigation_bar_width">760dp</dimen>
     <dimen name="car_left_navigation_bar_width">96dp</dimen>
     <dimen name="car_right_navigation_bar_width">96dp</dimen>
+    <!-- In order to change the height of the bottom nav bar, overlay navigation_bar_height in
+         frameworks/base/core/res/res instead. -->
+    <dimen name="car_bottom_navigation_bar_height">@*android:dimen/navigation_bar_height</dimen>
+    <!-- In order to change the height of the top nav bar, overlay status_bar_height in
+         frameworks/base/core/res/res instead. -->
+    <dimen name="car_top_navigation_bar_height">@*android:dimen/status_bar_height</dimen>
 
     <dimen name="car_user_switcher_container_height">420dp</dimen>
     <!-- This must be the negative of car_user_switcher_container_height for the animation. -->
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 218c95c..ec018f9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -21,10 +21,13 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewRootImpl;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -61,7 +64,7 @@
 public class CarKeyguardViewController extends OverlayViewController implements
         KeyguardViewController {
     private static final String TAG = "CarKeyguardViewController";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private final Context mContext;
     private final Handler mHandler;
@@ -75,9 +78,10 @@
     private final DismissCallbackRegistry mDismissCallbackRegistry;
     private final ViewMediatorCallback mViewMediatorCallback;
     private final CarNavigationBarController mCarNavigationBarController;
+    private final InputMethodManager mInputMethodManager;
     // Needed to instantiate mBouncer.
-    private final KeyguardBouncer.BouncerExpansionCallback
-            mExpansionCallback = new KeyguardBouncer.BouncerExpansionCallback() {
+    private final KeyguardBouncer.BouncerExpansionCallback mExpansionCallback =
+            new KeyguardBouncer.BouncerExpansionCallback() {
                 @Override
                 public void onFullyShown() {
                 }
@@ -96,7 +100,8 @@
             };
     private final CarUserManager.UserLifecycleListener mUserLifecycleListener = (e) -> {
         if (e.getEventType() == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING) {
-            revealKeyguardIfBouncerPrepared();
+            UserHandle currentUser = e.getUserHandle();
+            revealKeyguardIfBouncerPrepared(currentUser);
         }
     };
 
@@ -136,6 +141,8 @@
         mDismissCallbackRegistry = dismissCallbackRegistry;
         mViewMediatorCallback = viewMediatorCallback;
         mCarNavigationBarController = carNavigationBarController;
+        // TODO(b/169280588): Inject InputMethodManager instead.
+        mInputMethodManager = mContext.getSystemService(InputMethodManager.class);
 
         registerUserSwitchedListener();
     }
@@ -363,9 +370,9 @@
     }
 
     /**
-     *  Hides Keyguard so that the transitioning Bouncer can be hidden until it is prepared. To be
-     *  called by {@link com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator}
-     *  when a new user is selected.
+     * Hides Keyguard so that the transitioning Bouncer can be hidden until it is prepared. To be
+     * called by {@link com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator}
+     * when a new user is selected.
      */
     public void hideKeyguardToPrepareBouncer() {
         getLayout().setVisibility(View.INVISIBLE);
@@ -376,29 +383,41 @@
         mBouncer = keyguardBouncer;
     }
 
-    private void revealKeyguardIfBouncerPrepared() {
+    private void revealKeyguardIfBouncerPrepared(UserHandle currentUser) {
         int reattemptDelayMillis = 50;
         Runnable revealKeyguard = () -> {
             if (mBouncer == null) {
                 if (DEBUG) {
                     Log.d(TAG, "revealKeyguardIfBouncerPrepared: revealKeyguard request is ignored "
-                                    + "since the Bouncer has not been initialized yet.");
+                            + "since the Bouncer has not been initialized yet.");
                 }
                 return;
             }
             if (!mBouncer.inTransit() || !mBouncer.isSecure()) {
                 getLayout().setVisibility(View.VISIBLE);
+                updateCurrentUserForPasswordEntry(currentUser);
             } else {
                 if (DEBUG) {
                     Log.d(TAG, "revealKeyguardIfBouncerPrepared: Bouncer is not prepared "
                             + "yet so reattempting after " + reattemptDelayMillis + "ms.");
                 }
-                mHandler.postDelayed(this::revealKeyguardIfBouncerPrepared, reattemptDelayMillis);
+                mHandler.postDelayed(() -> revealKeyguardIfBouncerPrepared(currentUser),
+                        reattemptDelayMillis);
             }
         };
         mHandler.post(revealKeyguard);
     }
 
+    private void updateCurrentUserForPasswordEntry(UserHandle currentUser) {
+        EditText passwordEntry = getLayout().findViewById(R.id.passwordEntry);
+        if (passwordEntry != null) {
+            mHandler.post(() -> {
+                mInputMethodManager.restartInput(passwordEntry);
+                passwordEntry.setTextOperationUser(currentUser);
+            });
+        }
+    }
+
     private void notifyKeyguardUpdateMonitor() {
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(mShowing);
         if (mBouncer != null) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
index 694ae6d..2efa2b3d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
@@ -96,9 +96,11 @@
 
         populateMaps();
         readConfigs();
+
         checkEnabledBarsHaveUniqueBarTypes();
         checkSystemBarEnabledForNotificationPanel();
         checkHideBottomBarForKeyboardConfigSync();
+
         setInsetPaddingsForOverlappingCorners();
         sortSystemBarSidesByZOrder();
     }
@@ -153,10 +155,10 @@
         BAR_TITLE_MAP.put(LEFT, "LeftCarSystemBar");
         BAR_TITLE_MAP.put(RIGHT, "RightCarSystemBar");
 
-        BAR_GESTURE_MAP.put(TOP, InsetsState.ITYPE_TOP_GESTURES);
-        BAR_GESTURE_MAP.put(BOTTOM, InsetsState.ITYPE_BOTTOM_GESTURES);
-        BAR_GESTURE_MAP.put(LEFT, InsetsState.ITYPE_LEFT_GESTURES);
-        BAR_GESTURE_MAP.put(RIGHT, InsetsState.ITYPE_RIGHT_GESTURES);
+        BAR_GESTURE_MAP.put(TOP, InsetsState.ITYPE_TOP_MANDATORY_GESTURES);
+        BAR_GESTURE_MAP.put(BOTTOM, InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES);
+        BAR_GESTURE_MAP.put(LEFT, InsetsState.ITYPE_LEFT_MANDATORY_GESTURES);
+        BAR_GESTURE_MAP.put(RIGHT, InsetsState.ITYPE_RIGHT_MANDATORY_GESTURES);
     }
 
     private void readConfigs() {
@@ -170,7 +172,7 @@
                     new SystemBarConfigBuilder()
                             .setSide(TOP)
                             .setGirth(mResources.getDimensionPixelSize(
-                                    com.android.internal.R.dimen.status_bar_height))
+                                    R.dimen.car_top_navigation_bar_height))
                             .setBarType(mResources.getInteger(R.integer.config_topSystemBarType))
                             .setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder))
                             .setHideForKeyboard(mResources.getBoolean(
@@ -184,7 +186,7 @@
                     new SystemBarConfigBuilder()
                             .setSide(BOTTOM)
                             .setGirth(mResources.getDimensionPixelSize(
-                                    com.android.internal.R.dimen.navigation_bar_height))
+                                    R.dimen.car_bottom_navigation_bar_height))
                             .setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType))
                             .setZOrder(
                                     mResources.getInteger(R.integer.config_bottomSystemBarZOrder))
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 7cd559a..fd804c7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.car.notification;
 
-import static android.view.WindowInsets.Type.navigationBars;
-
 import android.app.ActivityManager;
 import android.car.Car;
 import android.car.drivingstate.CarUxRestrictionsManager;
@@ -25,6 +23,8 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.GestureDetector;
@@ -82,6 +82,7 @@
     private final StatusBarStateController mStatusBarStateController;
     private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
     private final NotificationVisibilityLogger mNotificationVisibilityLogger;
+    private final int mNavBarHeight;
 
     private float mInitialBackgroundAlpha;
     private float mBackgroundAlphaDiff;
@@ -138,7 +139,10 @@
         mStatusBarStateController = statusBarStateController;
         mNotificationVisibilityLogger = notificationVisibilityLogger;
 
+        mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.car_bottom_navigation_bar_height);
+
         mCommandQueue.addCallback(this);
+
         // Notification background setup.
         mInitialBackgroundAlpha = (float) mResources.getInteger(
                 R.integer.config_initialNotificationBackgroundAlpha) / 100;
@@ -179,6 +183,27 @@
         }
     }
 
+    @Override
+    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+            boolean showImeSwitcher) {
+        if (mContext.getDisplayId() != displayId) {
+            return;
+        }
+        boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
+        int bottomMargin = isKeyboardVisible ? 0 : mNavBarHeight;
+        ViewGroup container = (ViewGroup) getLayout();
+        if (container == null) {
+            // Notification panel hasn't been inflated before. We shouldn't try to update the layout
+            // params.
+            return;
+        }
+
+        ViewGroup.MarginLayoutParams params =
+                (ViewGroup.MarginLayoutParams) container.getLayoutParams();
+        params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
+        container.setLayoutParams(params);
+    }
+
     // OverlayViewController
 
     @Override
@@ -204,7 +229,7 @@
 
     @Override
     protected int getInsetTypesToFit() {
-        return navigationBars();
+        return 0;
     }
 
     @Override
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 87f3b7a..1ab88ed 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -487,8 +487,8 @@
     <string name="status_unavailable" msgid="5279036186589861608">"অনুপলভ্য"</string>
     <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC র‍্যান্ডমাইজ করা হয়েছে"</string>
     <plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139">
-      <item quantity="one">%1$dটি ডিভাইস কানেক্ট</item>
-      <item quantity="other">%1$dটি ডিভাইস কানেক্ট</item>
+      <item quantity="one">%1$dটি ডিভাইস কানেক্ট রয়েছে</item>
+      <item quantity="other">%1$dটি ডিভাইস কানেক্ট রয়েছে</item>
     </plurals>
     <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"আরও বেশি।"</string>
     <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"আরও কম।"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 904a70e..6e734bc 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -487,7 +487,7 @@
     <string name="status_unavailable" msgid="5279036186589861608">"मौजूद नहीं है"</string>
     <string name="wifi_status_mac_randomized" msgid="466382542497832189">"एमएसी पता रैंडम पर सेट है"</string>
     <plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139">
-      <item quantity="one">%1$d डिवाइस जुड़े हैं</item>
+      <item quantity="one">%1$d डिवाइस जुड़ा है</item>
       <item quantity="other">%1$d डिवाइस जुड़े हैं</item>
     </plurals>
     <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ज़्यादा समय."</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index fec2dd6..d86d88b 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -145,7 +145,7 @@
     <string name="data_usage_ota" msgid="7984667793701597001">"Rendszerfrissítések"</string>
     <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB-megosztás"</string>
     <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Hordozható hotspot"</string>
-    <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth megosztása"</string>
+    <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth-megosztás"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Megosztás"</string>
     <string name="tether_settings_title_all" msgid="8910259483383010470">"Megosztás és hotspot"</string>
     <string name="managed_user_title" msgid="449081789742645723">"Összes munkaalkalmazás"</string>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 2696f5a..a279872 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -55,7 +55,7 @@
   </string-array>
   <string-array name="hdcp_checking_summaries">
     <item msgid="4045840870658484038">"Երբեք չօգտագործել HDCP ստուգումը"</item>
-    <item msgid="8254225038262324761">"Օգտագործել HDCP-ը` միայն DRM-ի բովանդակությունը ստուգելու համար"</item>
+    <item msgid="8254225038262324761">"Օգտագործել HDCP-ը՝ միայն DRM-ի բովանդակությունը ստուգելու համար"</item>
     <item msgid="6421717003037072581">"Միշտ օգտագործել HDCP ստուգումը"</item>
   </string-array>
   <string-array name="bt_hci_snoop_log_entries">
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index ffd95a4..e434cac 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -343,7 +343,7 @@
     <string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"Թարմացվելիս ընդգծել սարքաշարի ծածկույթները կանաչ գույնով"</string>
     <string name="debug_hw_overdraw" msgid="8944851091008756796">"Վրիպազերծել GPU գերազանցումները"</string>
     <string name="disable_overlays" msgid="4206590799671557143">"Կասեցնել HW վրադրումները"</string>
-    <string name="disable_overlays_summary" msgid="1954852414363338166">"Միշտ օգտագործել GPU-ն` էկրանի կազմման համար"</string>
+    <string name="disable_overlays_summary" msgid="1954852414363338166">"Միշտ օգտագործել GPU-ն՝ էկրանի կազմման համար"</string>
     <string name="simulate_color_space" msgid="1206503300335835151">"Նմանակել գունատարածքը"</string>
     <string name="enable_opengl_traces_title" msgid="4638773318659125196">"Ակտիվացնել OpenGL հետքերը"</string>
     <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Անջատել USB աուդիո երթուղումը"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 762d830..ee368fd 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -143,9 +143,9 @@
     <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"हटाइएका एपहरू"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"एपहरू र प्रयोगकर्ताहरू हटाइयो।"</string>
     <string name="data_usage_ota" msgid="7984667793701597001">"प्रणालीसम्बन्धी अद्यावधिकहरू"</string>
-    <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB टेथर गर्दै"</string>
+    <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB टेदर गर्दै"</string>
     <string name="tether_settings_title_wifi" msgid="4803402057533895526">"पोर्टेबल हटस्पट"</string>
-    <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ब्लुटुथ टेथर गर्दै"</string>
+    <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ब्लुटुथ टेदर गर्दै"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"टेदर गर्दै"</string>
     <string name="tether_settings_title_all" msgid="8910259483383010470">"टेदर गर्ने र पोर्टेबल हटस्पट"</string>
     <string name="managed_user_title" msgid="449081789742645723">"कार्य प्रोफाइलका सबै एपहरू"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index d2891a0..c5d70ac 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -143,11 +143,11 @@
     <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Programu zilizoondolewa"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Watumiaji na programu ziilizoondolewa"</string>
     <string name="data_usage_ota" msgid="7984667793701597001">"Masasisho ya mfumo"</string>
-    <string name="tether_settings_title_usb" msgid="3728686573430917722">"Shiriki intaneti kwa USB"</string>
+    <string name="tether_settings_title_usb" msgid="3728686573430917722">"Sambaza mtandao kwa USB"</string>
     <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Intaneti ya kusambazwa"</string>
-    <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Shiriki intaneti kwa Bluetooth"</string>
-    <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Inazuia"</string>
-    <string name="tether_settings_title_all" msgid="8910259483383010470">"Kushiriki na kusambaza intaneti"</string>
+    <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Sambaza mtandao kwa Bluetooth"</string>
+    <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Kusambaza mtandao"</string>
+    <string name="tether_settings_title_all" msgid="8910259483383010470">"Kushiriki na kusambaza mtandao"</string>
     <string name="managed_user_title" msgid="449081789742645723">"Programu zote za kazini"</string>
     <string name="user_guest" msgid="6939192779649870792">"Mgeni"</string>
     <string name="unknown" msgid="3544487229740637809">"Haijulikani"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index defc33e..7468d04 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -143,7 +143,7 @@
     <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"แอปพลิเคชันที่นำออก"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"แอปพลิเคชันและผู้ใช้ที่นำออก"</string>
     <string name="data_usage_ota" msgid="7984667793701597001">"การอัปเดตระบบ"</string>
-    <string name="tether_settings_title_usb" msgid="3728686573430917722">"ปล่อยสัญญาณผ่าน USB"</string>
+    <string name="tether_settings_title_usb" msgid="3728686573430917722">"เชื่อมต่อเน็ตผ่าน USB"</string>
     <string name="tether_settings_title_wifi" msgid="4803402057533895526">"ฮอตสปอตแบบพกพาได้"</string>
     <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ปล่อยสัญญาณบลูทูธ"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"การปล่อยสัญญาณ"</string>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index a311f07..3055104 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -167,6 +167,8 @@
         Settings.Secure.MEDIA_CONTROLS_RESUME,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
         Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
-        Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED
+        Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
+        Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+        Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 9c5b6006..f1846db 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -252,5 +252,8 @@
                 Secure.ACCESSIBILITY_BUTTON_TARGETS,
                 ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
         VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
+        VALIDATORS.put(Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 845c0a3..5977ebe 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1878,6 +1878,15 @@
                 SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE);
         p.end(assistToken);
 
+        final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+                SecureSettingsProto.AssistHandles.LEARNING_TIME_ELAPSED_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+                SecureSettingsProto.AssistHandles.LEARNING_EVENT_COUNT);
+        p.end(assistHandlesToken);
+
         final long autofillToken = p.start(SecureSettingsProto.AUTOFILL);
         dumpSetting(s, p,
                 Settings.Secure.AUTOFILL_SERVICE,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b558541..6e74184 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -113,6 +113,7 @@
     <uses-permission android:name="android.permission.SET_ORIENTATION" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.MONITOR_INPUT" />
+    <uses-permission android:name="android.permission.INPUT_CONSUMER" />
 
     <!-- DreamManager -->
     <uses-permission android:name="android.permission.READ_DREAM_STATE" />
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index ed870f8..170f2c4 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -166,8 +166,7 @@
         android:layout_height="wrap_content"
         android:clickable="true"
         android:maxHeight="@dimen/qs_media_enabled_seekbar_height"
-        android:paddingTop="16dp"
-        android:paddingBottom="16dp"
+        android:paddingVertical="@dimen/qs_media_enabled_seekbar_vertical_padding"
         android:thumbTint="@color/media_primary_text"
         android:progressTint="@color/media_seekbar_progress"
         android:progressBackgroundTint="@color/media_disabled"
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 410edfb..c5a825f 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -979,7 +979,7 @@
     <string name="slice_permission_text_2" msgid="6758906940360746983">"- می‌تواند در <xliff:g id="APP">%1$s</xliff:g> اقدام انجام دهد"</string>
     <string name="slice_permission_checkbox" msgid="4242888137592298523">"به <xliff:g id="APP">%1$s</xliff:g> اجازه داده شود تکه‌هایی از برنامه‌ها نشان دهد"</string>
     <string name="slice_permission_allow" msgid="6340449521277951123">"مجاز"</string>
-    <string name="slice_permission_deny" msgid="6870256451658176895">"رد کردن"</string>
+    <string name="slice_permission_deny" msgid="6870256451658176895">"مجاز نبودن"</string>
     <string name="auto_saver_title" msgid="6873691178754086596">"برای زمان‌بندی «بهینه‌سازی باتری» ضربه بزنید"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"وقتی باتری روبه‌اتمام است، بهینه‌سازی باتری را روشن کنید"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"نه متشکرم"</string>
diff --git a/packages/SystemUI/res/values-h740dp-port/dimens.xml b/packages/SystemUI/res/values-h740dp-port/dimens.xml
new file mode 100644
index 0000000..966066f
--- /dev/null
+++ b/packages/SystemUI/res/values-h740dp-port/dimens.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<resources>
+    <dimen name="qs_tile_height">106dp</dimen>
+    <dimen name="qs_tile_margin_vertical">24dp</dimen>
+
+    <!-- The height of the qs customize header. Should be
+         (qs_panel_padding_top (48dp) +  brightness_mirror_height (48dp) + qs_tile_margin_top (18dp)) -
+         (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp))
+    -->
+    <dimen name="qs_customize_header_min_height">46dp</dimen>
+    <dimen name="qs_tile_margin_top">18dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b7e8581..7594466 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -371,7 +371,7 @@
     <string name="quick_settings_time_label" msgid="3352680970557509303">"시간"</string>
     <string name="quick_settings_user_label" msgid="1253515509432672496">"나"</string>
     <string name="quick_settings_user_title" msgid="8673045967216204537">"사용자"</string>
-    <string name="quick_settings_user_new_user" msgid="3347905871336069666">"새 사용자"</string>
+    <string name="quick_settings_user_new_user" msgid="3347905871336069666">"신규 사용자"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"연결되어 있지 않음"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"네트워크가 연결되지 않음"</string>
@@ -473,7 +473,7 @@
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"현재 사용자: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"프로필 표시"</string>
     <string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string>
-    <string name="user_new_user_name" msgid="2019166282704195789">"새 사용자"</string>
+    <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index b164276..6a16750 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -987,7 +987,7 @@
     <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Penjimat Bateri akan dihidupkan secara automatik setelah kuasa bateri kurang daripada <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="open_saver_setting_action" msgid="2111461909782935190">"Tetapan"</string>
     <string name="auto_saver_okay_action" msgid="7815925750741935386">"OK"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Longgok Tmbunn SysUI"</string>
+    <string name="heap_dump_tile_name" msgid="2464189856478823046">"DumpSys"</string>
     <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"<xliff:g id="APP">%1$s</xliff:g> sedang menggunakan <xliff:g id="TYPES_LIST">%2$s</xliff:g> anda."</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi sedang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda."</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 2d37a0a..439153b 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -987,7 +987,7 @@
     <string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式將會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string>
     <string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string>
     <string name="auto_saver_okay_action" msgid="7815925750741935386">"知道了"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"轉儲 SysUI 堆"</string>
+    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 1434587..eeff9e6 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -987,7 +987,7 @@
     <string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string>
     <string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string>
     <string name="auto_saver_okay_action" msgid="7815925750741935386">"我知道了"</string>
-    <string name="heap_dump_tile_name" msgid="2464189856478823046">"傾印 SysUI 記憶體快照"</string>
+    <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
     <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string>
     <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
     <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5b7bee6..f07627a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -482,20 +482,21 @@
     <!-- The size of the gesture span needed to activate the "pull" notification expansion -->
     <dimen name="pull_span_min">25dp</dimen>
 
-    <dimen name="qs_tile_height">106dp</dimen>
+    <dimen name="qs_tile_height">96dp</dimen>
     <!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 -->
     <dimen name="qs_tile_layout_margin_side">18dp</dimen>
     <dimen name="qs_tile_margin_horizontal">18dp</dimen>
     <dimen name="qs_tile_margin_horizontal_two_line">2dp</dimen>
-    <dimen name="qs_tile_margin_vertical">24dp</dimen>
+    <dimen name="qs_tile_margin_vertical">2dp</dimen>
     <dimen name="qs_tile_margin_top_bottom">12dp</dimen>
     <dimen name="qs_tile_margin_top_bottom_negative">-12dp</dimen>
     <!-- The height of the qs customize header. Should be
-         (qs_panel_padding_top (48dp) +  brightness_mirror_height (48dp) + qs_tile_margin_top (18dp)) -
+         (qs_panel_padding_top (48dp) +  brightness_mirror_height (48dp) + qs_tile_margin_top (0dp)) -
          (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp))
     -->
-    <dimen name="qs_customize_header_min_height">46dp</dimen>
-    <dimen name="qs_tile_margin_top">18dp</dimen>
+    <dimen name="qs_customize_header_min_height">28dp</dimen>
+    <dimen name="qs_tile_margin_top">0dp</dimen>
+    <dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen>
     <dimen name="qs_tile_background_size">44dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
@@ -1295,6 +1296,8 @@
     <dimen name="qs_footer_horizontal_margin">22dp</dimen>
     <dimen name="qs_media_disabled_seekbar_height">1dp</dimen>
     <dimen name="qs_media_enabled_seekbar_height">3dp</dimen>
+    <dimen name="qs_media_enabled_seekbar_vertical_padding">15dp</dimen>
+    <dimen name="qs_media_disabled_seekbar_vertical_padding">16dp</dimen>
 
     <dimen name="magnification_border_size">5dp</dimen>
     <dimen name="magnification_frame_move_short">5dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index a56f6f5..b8e8db5 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -177,6 +177,9 @@
     <item type="id" name="accessibility_action_controls_move_before" />
     <item type="id" name="accessibility_action_controls_move_after" />
 
+    <item type="id" name="accessibility_action_qs_move_to_position" />
+    <item type="id" name="accessibility_action_qs_add_to_position" />
+
     <!-- Accessibility actions for PIP -->
     <item type="id" name="action_pip_resize" />
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 38501eb..77ce39f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2267,23 +2267,26 @@
     <!-- Accessibility action for moving docked stack divider to make the bottom screen full screen [CHAR LIMIT=NONE] -->
     <string name="accessibility_action_divider_bottom_full">Bottom full screen</string>
 
-    <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] -->
-    <string name="accessibility_qs_edit_tile_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>, <xliff:g id="tile_name" example="Wi-Fi">%2$s</xliff:g>. Double tap to edit.</string>
+    <!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_remove_tile_action">remove tile</string>
 
-    <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] -->
-    <string name="accessibility_qs_edit_add_tile_label"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g>. Double tap to add.</string>
+    <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to end" in screen readers [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_add_action">add tile to end</string>
 
-    <!-- Accessibility description of option to move QS tile [CHAR LIMIT=NONE] -->
-    <string name="accessibility_qs_edit_move_tile">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string>
+    <!-- Accessibility action for context menu to move QS tile [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_start_move">Move tile</string>
 
-    <!-- Accessibility description of option to remove QS tile [CHAR LIMIT=NONE] -->
-    <string name="accessibility_qs_edit_remove_tile">Remove <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string>
+    <!-- Accessibility action for context menu to add QS tile [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_start_add">Add tile</string>
 
-    <!-- Accessibility action when QS tile is to be added [CHAR LIMIT=NONE] -->
-    <string name="accessibility_qs_edit_tile_add">Add <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> to position <xliff:g id="position" example="5">%2$d</xliff:g></string>
+    <!-- Accessibility description when QS tile is to be moved, indicating the destination position [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_move_to_position">Move to <xliff:g id="position" example="5">%1$d</xliff:g></string>
 
-    <!-- Accessibility action when QS tile is to be moved [CHAR LIMIT=NONE] -->
-    <string name="accessibility_qs_edit_tile_move">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> to position <xliff:g id="position" example="5">%2$d</xliff:g></string>
+    <!-- Accessibility description when QS tile is to be added, indicating the destination position [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_add_to_position">Add to position <xliff:g id="position" example="5">%1$d</xliff:g></string>
+
+    <!-- Accessibility description indicating the currently selected tile's position. Only used for tiles that are currently in use [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_position">Position <xliff:g id="position" example="5">%1$d</xliff:g></string>
 
     <!-- Accessibility label for window when QS editing is happening [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_quick_settings_edit">Quick settings editor.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 64b35ca..2b9514f 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,8 +16,13 @@
 
 package com.android.systemui.appops;
 
+import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
+
 import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.location.LocationManager;
 import android.media.AudioManager;
@@ -35,6 +40,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
 
@@ -54,7 +60,7 @@
  * NotificationPresenter to be displayed to the user.
  */
 @Singleton
-public class AppOpsControllerImpl implements AppOpsController,
+public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController,
         AppOpsManager.OnOpActiveChangedInternalListener,
         AppOpsManager.OnOpNotedListener, Dumpable {
 
@@ -65,6 +71,7 @@
     private static final String TAG = "AppOpsControllerImpl";
     private static final boolean DEBUG = false;
 
+    private final BroadcastDispatcher mDispatcher;
     private final AppOpsManager mAppOps;
     private final AudioManager mAudioManager;
     private final LocationManager mLocationManager;
@@ -79,6 +86,7 @@
     private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
     private final PermissionFlagsCache mFlagsCache;
     private boolean mListening;
+    private boolean mMicMuted;
 
     @GuardedBy("mActiveItems")
     private final List<AppOpItem> mActiveItems = new ArrayList<>();
@@ -104,8 +112,10 @@
             @Background Looper bgLooper,
             DumpManager dumpManager,
             PermissionFlagsCache cache,
-            AudioManager audioManager
+            AudioManager audioManager,
+            BroadcastDispatcher dispatcher
     ) {
+        mDispatcher = dispatcher;
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mFlagsCache = cache;
         mBGHandler = new H(bgLooper);
@@ -114,6 +124,7 @@
             mCallbacksByCode.put(OPS[i], new ArraySet<>());
         }
         mAudioManager = audioManager;
+        mMicMuted = audioManager.isMicrophoneMute();
         mLocationManager = context.getSystemService(LocationManager.class);
         dumpManager.registerDumpable(TAG, this);
     }
@@ -132,6 +143,8 @@
             mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
             mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
                     mAudioManager.getActiveRecordingConfigurations()));
+            mDispatcher.registerReceiverWithHandler(this,
+                    new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler);
 
         } else {
             mAppOps.stopWatchingActive(this);
@@ -139,6 +152,7 @@
             mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
 
             mBGHandler.removeCallbacksAndMessages(null); // null removes all
+            mDispatcher.unregisterReceiver(this);
             synchronized (mActiveItems) {
                 mActiveItems.clear();
                 mRecordingsByUid.clear();
@@ -466,6 +480,9 @@
     }
 
     private boolean isAnyRecordingPausedLocked(int uid) {
+        if (mMicMuted) {
+            return true;
+        }
         List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid);
         if (configs == null) return false;
         int configsNum = configs.size();
@@ -520,6 +537,12 @@
         }
     };
 
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        mMicMuted = mAudioManager.isMicrophoneMute();
+        updateRecordingPausedStatus();
+    }
+
     protected class H extends Handler {
         H(Looper looper) {
             super(looper);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 8e49d58..0085710 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -26,6 +26,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.Handler;
 import android.provider.Settings;
 
@@ -66,8 +68,10 @@
 @Singleton
 final class AssistHandleReminderExpBehavior implements BehaviorController {
 
-    private static final String LEARNING_TIME_ELAPSED_KEY = "reminder_exp_learning_time_elapsed";
-    private static final String LEARNING_EVENT_COUNT_KEY = "reminder_exp_learning_event_count";
+    private static final Uri LEARNING_TIME_ELAPSED_URI =
+            Settings.Secure.getUriFor(Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS);
+    private static final Uri LEARNING_EVENT_COUNT_URI =
+            Settings.Secure.getUriFor(Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT);
     private static final String LEARNED_HINT_LAST_SHOWN_KEY =
             "reminder_exp_learned_hint_last_shown";
     private static final long DEFAULT_LEARNING_TIME_MS = TimeUnit.DAYS.toMillis(10);
@@ -181,6 +185,7 @@
     private boolean mIsNavBarHidden;
     private boolean mIsLauncherShowing;
     private int mConsecutiveTaskSwitches;
+    @Nullable private ContentObserver mSettingObserver;
 
     /** Whether user has learned the gesture. */
     private boolean mIsLearned;
@@ -248,9 +253,22 @@
         mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver);
 
         mLearningTimeElapsed = Settings.Secure.getLong(
-                context.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, /* default = */ 0);
+                context.getContentResolver(),
+                Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+                /* default = */ 0);
         mLearningCount = Settings.Secure.getInt(
-                context.getContentResolver(), LEARNING_EVENT_COUNT_KEY, /* default = */ 0);
+                context.getContentResolver(),
+                Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+                /* default = */ 0);
+        mSettingObserver = new SettingsObserver(context, mHandler);
+        context.getContentResolver().registerContentObserver(
+                LEARNING_TIME_ELAPSED_URI,
+                /* notifyForDescendants = */ true,
+                mSettingObserver);
+        context.getContentResolver().registerContentObserver(
+                LEARNING_EVENT_COUNT_URI,
+                /* notifyForDescendants = */ true,
+                mSettingObserver);
         mLearnedHintLastShownEpochDay = Settings.Secure.getLong(
                 context.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, /* default = */ 0);
         mLastLearningTimestamp = mClock.currentTimeMillis();
@@ -264,8 +282,20 @@
         if (mContext != null) {
             mBroadcastDispatcher.get().unregisterReceiver(mDefaultHomeBroadcastReceiver);
             mBootCompleteCache.get().removeListener(mBootCompleteListener);
-            Settings.Secure.putLong(mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, 0);
-            Settings.Secure.putInt(mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, 0);
+            mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
+            mSettingObserver = null;
+            // putString to use overrideableByRestore
+            Settings.Secure.putString(
+                    mContext.getContentResolver(),
+                    Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+                    Long.toString(0L),
+                    /* overrideableByRestore = */ true);
+            // putString to use overrideableByRestore
+            Settings.Secure.putString(
+                    mContext.getContentResolver(),
+                    Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+                    Integer.toString(0),
+                    /* overrideableByRestore = */ true);
             Settings.Secure.putLong(mContext.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, 0);
             mContext = null;
         }
@@ -282,8 +312,12 @@
             return;
         }
 
-        Settings.Secure.putLong(
-                mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, ++mLearningCount);
+        // putString to use overrideableByRestore
+        Settings.Secure.putString(
+                mContext.getContentResolver(),
+                Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+                Integer.toString(++mLearningCount),
+                /* overrideableByRestore = */ true);
     }
 
     @Override
@@ -460,8 +494,12 @@
         mIsLearned =
                 mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs();
 
-        mHandler.post(() -> Settings.Secure.putLong(
-                mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed));
+        // putString to use overrideableByRestore
+        mHandler.post(() -> Settings.Secure.putString(
+                mContext.getContentResolver(),
+                Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+                Long.toString(mLearningTimeElapsed),
+                /* overrideableByRestore = */ true));
     }
 
     private void resetConsecutiveTaskSwitches() {
@@ -589,4 +627,32 @@
                 + "="
                 + getShowWhenTaught());
     }
+
+    private final class SettingsObserver extends ContentObserver {
+
+        private final Context mContext;
+
+        SettingsObserver(Context context, Handler handler) {
+            super(handler);
+            mContext = context;
+        }
+
+        @Override
+        public void onChange(boolean selfChange, @Nullable Uri uri) {
+            if (LEARNING_TIME_ELAPSED_URI.equals(uri)) {
+                mLastLearningTimestamp = mClock.currentTimeMillis();
+                mLearningTimeElapsed = Settings.Secure.getLong(
+                        mContext.getContentResolver(),
+                        Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+                        /* default = */ 0);
+            } else if (LEARNING_EVENT_COUNT_URI.equals(uri)) {
+                mLearningCount = Settings.Secure.getInt(
+                        mContext.getContentResolver(),
+                        Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+                        /* default = */ 0);
+            }
+
+            super.onChange(selfChange, uri);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index c6d1286..b71e3ad 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -286,6 +286,15 @@
     }
 
     /**
+     * Sets whether this bubble is considered visually interruptive. Normally pulled from the
+     * {@link NotificationEntry}, this method is purely for testing.
+     */
+    @VisibleForTesting
+    void setVisuallyInterruptiveForTest(boolean visuallyInterruptive) {
+        mIsVisuallyInterruptive = visuallyInterruptive;
+    }
+
+    /**
      * Starts a task to inflate & load any necessary information to display a bubble.
      *
      * @param callback the callback to notify one the bubble is ready to be displayed.
@@ -411,6 +420,7 @@
             } else if (mIntent != null && entry.getBubbleMetadata().getIntent() == null) {
                 // Was an intent bubble now it's a shortcut bubble... still unregister the listener
                 mIntent.unregisterCancelListener(mIntentCancelListener);
+                mIntentActive = false;
                 mIntent = null;
             }
             mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 2948b47..5deae92 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -82,7 +82,6 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dumpable;
-import com.android.systemui.bubbles.animation.StackAnimationController;
 import com.android.systemui.bubbles.dagger.BubbleModule;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.model.SysUiState;
@@ -407,7 +406,8 @@
             if (bubble.getBubbleIntent() == null) {
                 return;
             }
-            if (bubble.isIntentActive()) {
+            if (bubble.isIntentActive()
+                    || mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
                 bubble.setPendingIntentCanceled();
                 return;
             }
@@ -1120,8 +1120,17 @@
         if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
             notif.setInterruption();
         }
-        Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
-        inflateAndAdd(bubble, suppressFlyout, showInShade);
+        if (!notif.getRanking().visuallyInterruptive()
+                && (notif.getBubbleMetadata() != null
+                    && !notif.getBubbleMetadata().getAutoExpandBubble())
+                && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
+            // Update the bubble but don't promote it out of overflow
+            Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
+            b.setEntry(notif);
+        } else {
+            Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
+            inflateAndAdd(bubble, suppressFlyout, showInShade);
+        }
     }
 
     void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index d2dc506..85ea8bc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -277,7 +277,8 @@
         } else {
             // Updates an existing bubble
             bubble.setSuppressFlyout(suppressFlyout);
-            doUpdate(bubble);
+            // If there is no flyout, we probably shouldn't show the bubble at the top
+            doUpdate(bubble, !suppressFlyout /* reorder */);
         }
 
         if (bubble.shouldAutoExpand()) {
@@ -431,12 +432,12 @@
         }
     }
 
-    private void doUpdate(Bubble bubble) {
+    private void doUpdate(Bubble bubble, boolean reorder) {
         if (DEBUG_BUBBLE_DATA) {
             Log.d(TAG, "doUpdate: " + bubble);
         }
         mStateChange.updatedBubble = bubble;
-        if (!isExpanded()) {
+        if (!isExpanded() && reorder) {
             int prevPos = mBubbles.indexOf(bubble);
             mBubbles.remove(bubble);
             mBubbles.add(0, bubble);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/MediaBrowserFactory.java
new file mode 100644
index 0000000..aca033e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaBrowserFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.browse.MediaBrowser;
+import android.os.Bundle;
+
+import javax.inject.Inject;
+
+/**
+ * Testable wrapper around {@link MediaBrowser} constructor
+ */
+public class MediaBrowserFactory {
+    private final Context mContext;
+
+    @Inject
+    public MediaBrowserFactory(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Creates a new MediaBrowser
+     *
+     * @param serviceComponent
+     * @param callback
+     * @param rootHints
+     * @return
+     */
+    public MediaBrowser create(ComponentName serviceComponent,
+            MediaBrowser.ConnectionCallback callback, Bundle rootHints) {
+        return new MediaBrowser(mContext, serviceComponent, callback, rootHints);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 3aa37a2..d8d9bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -174,7 +174,7 @@
         mediaManager.addListener(object : MediaDataManager.Listener {
             override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
                 addOrUpdatePlayer(key, oldKey, data)
-                val canRemove = data.isPlaying?.let { !it } ?: data.isClearable
+                val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
                 if (canRemove && !Utils.useMediaResumption(context)) {
                     // This view isn't playing, let's remove this! This happens e.g when
                     // dismissing/timing out a view. We still have the data around because
@@ -250,13 +250,13 @@
             val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                     ViewGroup.LayoutParams.WRAP_CONTENT)
             newPlayer.view?.player?.setLayoutParams(lp)
-            newPlayer.bind(data)
+            newPlayer.bind(data, key)
             newPlayer.setListening(currentlyExpanded)
             MediaPlayerData.addMediaPlayer(key, data, newPlayer)
             updatePlayerToState(newPlayer, noAnimation = true)
             reorderAllPlayers()
         } else {
-            existingPlayer.bind(data)
+            existingPlayer.bind(data, key)
             MediaPlayerData.addMediaPlayer(key, data, existingPlayer)
             if (visualStabilityManager.isReorderingAllowed) {
                 reorderAllPlayers()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index e55678dc..810cecc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -82,6 +82,7 @@
 
     private Context mContext;
     private PlayerViewHolder mViewHolder;
+    private String mKey;
     private MediaViewController mMediaViewController;
     private MediaSession.Token mToken;
     private MediaController mController;
@@ -206,10 +207,11 @@
     /**
      * Bind this view based on the data given
      */
-    public void bind(@NonNull MediaData data) {
+    public void bind(@NonNull MediaData data, String key) {
         if (mViewHolder == null) {
             return;
         }
+        mKey = key;
         MediaSession.Token token = data.getToken();
         mBackgroundColor = data.getBackgroundColor();
         if (mToken == null || !mToken.equals(token)) {
@@ -359,10 +361,10 @@
 
         // Dismiss
         mViewHolder.getDismiss().setOnClickListener(v -> {
-            if (data.getNotificationKey() != null) {
+            if (mKey != null) {
                 closeGuts();
                 mKeyguardDismissUtil.executeWhenUnlocked(() -> {
-                    mMediaDataManagerLazy.get().dismissMediaData(data.getNotificationKey(),
+                    mMediaDataManagerLazy.get().dismissMediaData(mKey,
                             MediaViewController.GUTS_ANIMATION_DURATION + 100);
                     return true;
                 }, /* requiresShadeOpen */ true);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 1ac3034..936db87 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -28,6 +28,7 @@
 import android.provider.Settings
 import android.service.media.MediaBrowserService
 import android.util.Log
+import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.tuner.TunerService
@@ -47,7 +48,8 @@
     private val context: Context,
     private val broadcastDispatcher: BroadcastDispatcher,
     @Background private val backgroundExecutor: Executor,
-    private val tunerService: TunerService
+    private val tunerService: TunerService,
+    private val mediaBrowserFactory: ResumeMediaBrowserFactory
 ) : MediaDataManager.Listener {
 
     private var useMediaResumption: Boolean = Utils.useMediaResumption(context)
@@ -58,7 +60,8 @@
     private var mediaBrowser: ResumeMediaBrowser? = null
     private var currentUserId: Int = context.userId
 
-    private val userChangeReceiver = object : BroadcastReceiver() {
+    @VisibleForTesting
+    val userChangeReceiver = object : BroadcastReceiver() {
         override fun onReceive(context: Context, intent: Intent) {
             if (Intent.ACTION_USER_UNLOCKED == intent.action) {
                 loadMediaResumptionControls()
@@ -142,7 +145,7 @@
         }
 
         resumeComponents.forEach {
-            val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it)
+            val browser = mediaBrowserFactory.create(mediaBrowserCallback, it)
             browser.findRecentMedia()
         }
     }
@@ -181,14 +184,10 @@
     private fun tryUpdateResumptionList(key: String, componentName: ComponentName) {
         Log.d(TAG, "Testing if we can connect to $componentName")
         mediaBrowser?.disconnect()
-        mediaBrowser = ResumeMediaBrowser(context,
+        mediaBrowser = mediaBrowserFactory.create(
                 object : ResumeMediaBrowser.Callback() {
                     override fun onConnected() {
-                        Log.d(TAG, "yes we can resume with $componentName")
-                        mediaDataManager.setResumeAction(key, getResumeAction(componentName))
-                        updateResumptionList(componentName)
-                        mediaBrowser?.disconnect()
-                        mediaBrowser = null
+                        Log.d(TAG, "Connected to $componentName")
                     }
 
                     override fun onError() {
@@ -197,6 +196,19 @@
                         mediaBrowser?.disconnect()
                         mediaBrowser = null
                     }
+
+                    override fun addTrack(
+                        desc: MediaDescription,
+                        component: ComponentName,
+                        browser: ResumeMediaBrowser
+                    ) {
+                        // Since this is a test, just save the component for later
+                        Log.d(TAG, "Can get resumable media from $componentName")
+                        mediaDataManager.setResumeAction(key, getResumeAction(componentName))
+                        updateResumptionList(componentName)
+                        mediaBrowser?.disconnect()
+                        mediaBrowser = null
+                    }
                 },
                 componentName)
         mediaBrowser?.testConnection()
@@ -233,7 +245,7 @@
     private fun getResumeAction(componentName: ComponentName): Runnable {
         return Runnable {
             mediaBrowser?.disconnect()
-            mediaBrowser = ResumeMediaBrowser(context,
+            mediaBrowser = mediaBrowserFactory.create(
                 object : ResumeMediaBrowser.Callback() {
                     override fun onConnected() {
                         if (mediaBrowser?.token == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 8662aac..dcb7767 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -54,32 +54,35 @@
         if (mediaListeners.containsKey(key)) {
             return
         }
-        // Having an old key means that we're migrating from/to resumption. We should invalidate
-        // the old listener and create a new one.
+        // Having an old key means that we're migrating from/to resumption. We should update
+        // the old listener to make sure that events will be dispatched to the new location.
         val migrating = oldKey != null && key != oldKey
         var wasPlaying = false
         if (migrating) {
-            if (mediaListeners.containsKey(oldKey)) {
-                val oldListener = mediaListeners.remove(oldKey)
-                wasPlaying = oldListener?.playing ?: false
-                oldListener?.destroy()
+            val reusedListener = mediaListeners.remove(oldKey)
+            if (reusedListener != null) {
+                wasPlaying = reusedListener.playing ?: false
                 if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption")
+                reusedListener.mediaData = data
+                reusedListener.key = key
+                mediaListeners[key] = reusedListener
+                if (wasPlaying != reusedListener.playing) {
+                    // If a player becomes active because of a migration, we'll need to broadcast
+                    // its state. Doing it now would lead to reentrant callbacks, so let's wait
+                    // until we're done.
+                    mainExecutor.execute {
+                        if (mediaListeners[key]?.playing == true) {
+                            if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key")
+                            timeoutCallback.invoke(key, false /* timedOut */)
+                        }
+                    }
+                }
+                return
             } else {
                 Log.w(TAG, "Old key $oldKey for player $key doesn't exist. Continuing...")
             }
         }
         mediaListeners[key] = PlaybackStateListener(key, data)
-
-        // If a player becomes active because of a migration, we'll need to broadcast its state.
-        // Doing it now would lead to reentrant callbacks, so let's wait until we're done.
-        if (migrating && mediaListeners[key]?.playing != wasPlaying) {
-            mainExecutor.execute {
-                if (mediaListeners[key]?.playing == true) {
-                    if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key")
-                    timeoutCallback.invoke(key, false /* timedOut */)
-                }
-            }
-        }
     }
 
     override fun onMediaDataRemoved(key: String) {
@@ -91,30 +94,39 @@
     }
 
     private inner class PlaybackStateListener(
-        private val key: String,
+        var key: String,
         data: MediaData
     ) : MediaController.Callback() {
 
         var timedOut = false
         var playing: Boolean? = null
 
+        var mediaData: MediaData = data
+            set(value) {
+                mediaController?.unregisterCallback(this)
+                field = value
+                mediaController = if (field.token != null) {
+                    mediaControllerFactory.create(field.token)
+                } else {
+                    null
+                }
+                mediaController?.registerCallback(this)
+                // Let's register the cancellations, but not dispatch events now.
+                // Timeouts didn't happen yet and reentrant events are troublesome.
+                processState(mediaController?.playbackState, dispatchEvents = false)
+            }
+
         // Resume controls may have null token
-        private val mediaController = if (data.token != null) {
-            mediaControllerFactory.create(data.token)
-        } else {
-            null
-        }
+        private var mediaController: MediaController? = null
         private var cancellation: Runnable? = null
 
         init {
-            mediaController?.registerCallback(this)
-            // Let's register the cancellations, but not dispatch events now.
-            // Timeouts didn't happen yet and reentrant events are troublesome.
-            processState(mediaController?.playbackState, dispatchEvents = false)
+            mediaData = data
         }
 
         fun destroy() {
             mediaController?.unregisterCallback(this)
+            cancellation?.run()
         }
 
         override fun onPlaybackStateChanged(state: PlaybackState?) {
@@ -171,4 +183,4 @@
             cancellation = null
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
index 68b6785..a4d4436 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
@@ -30,6 +30,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.List;
 
 /**
@@ -46,6 +48,7 @@
     private static final String TAG = "ResumeMediaBrowser";
     private final Context mContext;
     private final Callback mCallback;
+    private MediaBrowserFactory mBrowserFactory;
     private MediaBrowser mMediaBrowser;
     private ComponentName mComponentName;
 
@@ -55,10 +58,12 @@
      * @param callback used to report media items found
      * @param componentName Component name of the MediaBrowserService this browser will connect to
      */
-    public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName) {
+    public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName,
+            MediaBrowserFactory browserFactory) {
         mContext = context;
         mCallback = callback;
         mComponentName = componentName;
+        mBrowserFactory = browserFactory;
     }
 
     /**
@@ -74,7 +79,7 @@
         disconnect();
         Bundle rootHints = new Bundle();
         rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
-        mMediaBrowser = new MediaBrowser(mContext,
+        mMediaBrowser = mBrowserFactory.create(
                 mComponentName,
                 mConnectionCallback,
                 rootHints);
@@ -88,17 +93,19 @@
                 List<MediaBrowser.MediaItem> children) {
             if (children.size() == 0) {
                 Log.d(TAG, "No children found for " + mComponentName);
-                return;
-            }
-            // We ask apps to return a playable item as the first child when sending
-            // a request with EXTRA_RECENT; if they don't, no resume controls
-            MediaBrowser.MediaItem child = children.get(0);
-            MediaDescription desc = child.getDescription();
-            if (child.isPlayable() && mMediaBrowser != null) {
-                mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(),
-                        ResumeMediaBrowser.this);
+                mCallback.onError();
             } else {
-                Log.d(TAG, "Child found but not playable for " + mComponentName);
+                // We ask apps to return a playable item as the first child when sending
+                // a request with EXTRA_RECENT; if they don't, no resume controls
+                MediaBrowser.MediaItem child = children.get(0);
+                MediaDescription desc = child.getDescription();
+                if (child.isPlayable() && mMediaBrowser != null) {
+                    mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(),
+                            ResumeMediaBrowser.this);
+                } else {
+                    Log.d(TAG, "Child found but not playable for " + mComponentName);
+                    mCallback.onError();
+                }
             }
             disconnect();
         }
@@ -131,7 +138,7 @@
             Log.d(TAG, "Service connected for " + mComponentName);
             if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
                 String root = mMediaBrowser.getRoot();
-                if (!TextUtils.isEmpty(root)) {
+                if (!TextUtils.isEmpty(root) && mMediaBrowser != null) {
                     mCallback.onConnected();
                     mMediaBrowser.subscribe(root, mSubscriptionCallback);
                     return;
@@ -182,7 +189,7 @@
         disconnect();
         Bundle rootHints = new Bundle();
         rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
-        mMediaBrowser = new MediaBrowser(mContext, mComponentName,
+        mMediaBrowser = mBrowserFactory.create(mComponentName,
                 new MediaBrowser.ConnectionCallback() {
                     @Override
                     public void onConnected() {
@@ -192,7 +199,7 @@
                             return;
                         }
                         MediaSession.Token token = mMediaBrowser.getSessionToken();
-                        MediaController controller = new MediaController(mContext, token);
+                        MediaController controller = createMediaController(token);
                         controller.getTransportControls();
                         controller.getTransportControls().prepare();
                         controller.getTransportControls().play();
@@ -212,6 +219,11 @@
         mMediaBrowser.connect();
     }
 
+    @VisibleForTesting
+    protected MediaController createMediaController(MediaSession.Token token) {
+        return new MediaController(mContext, token);
+    }
+
     /**
      * Get the media session token
      * @return the token, or null if the MediaBrowser is null or disconnected
@@ -235,42 +247,19 @@
 
     /**
      * Used to test if SystemUI is allowed to connect to the given component as a MediaBrowser.
-     * ResumeMediaBrowser.Callback#onError or ResumeMediaBrowser.Callback#onConnected will be called
-     * depending on whether it was successful.
+     * If it can connect, ResumeMediaBrowser.Callback#onConnected will be called. If valid media is
+     * found, then ResumeMediaBrowser.Callback#addTrack will also be called. This allows for more
+     * detailed logging if the service has issues. If it cannot connect, or cannot find valid media,
+     * then ResumeMediaBrowser.Callback#onError will be called.
      * ResumeMediaBrowser#disconnect should be called after this to ensure the connection is closed.
      */
     public void testConnection() {
         disconnect();
-        final MediaBrowser.ConnectionCallback connectionCallback =
-                new MediaBrowser.ConnectionCallback() {
-                    @Override
-                    public void onConnected() {
-                        Log.d(TAG, "connected");
-                        if (mMediaBrowser == null || !mMediaBrowser.isConnected()
-                                || TextUtils.isEmpty(mMediaBrowser.getRoot())) {
-                            mCallback.onError();
-                        } else {
-                            mCallback.onConnected();
-                        }
-                    }
-
-                    @Override
-                    public void onConnectionSuspended() {
-                        Log.d(TAG, "suspended");
-                        mCallback.onError();
-                    }
-
-                    @Override
-                    public void onConnectionFailed() {
-                        Log.d(TAG, "failed");
-                        mCallback.onError();
-                    }
-                };
         Bundle rootHints = new Bundle();
         rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
-        mMediaBrowser = new MediaBrowser(mContext,
+        mMediaBrowser = mBrowserFactory.create(
                 mComponentName,
-                connectionCallback,
+                mConnectionCallback,
                 rootHints);
         mMediaBrowser.connect();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java
new file mode 100644
index 0000000..2261aa5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media;
+
+import android.content.ComponentName;
+import android.content.Context;
+
+import javax.inject.Inject;
+
+/**
+ * Testable wrapper around {@link ResumeMediaBrowser} constructor
+ */
+public class ResumeMediaBrowserFactory {
+    private final Context mContext;
+    private final MediaBrowserFactory mBrowserFactory;
+
+    @Inject
+    public ResumeMediaBrowserFactory(Context context, MediaBrowserFactory browserFactory) {
+        mContext = context;
+        mBrowserFactory = browserFactory;
+    }
+
+    /**
+     * Creates a new ResumeMediaBrowser.
+     *
+     * @param callback will be called on connection or error, and addTrack when media item found
+     * @param componentName component to browse
+     * @return
+     */
+    public ResumeMediaBrowser create(ResumeMediaBrowser.Callback callback,
+            ComponentName componentName) {
+        return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index 1ae54d6..d789501 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -28,10 +28,14 @@
  */
 class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarViewModel.Progress> {
 
-    val seekBarDefaultMaxHeight = holder.seekBar.context.resources
+    val seekBarEnabledMaxHeight = holder.seekBar.context.resources
         .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_height)
     val seekBarDisabledHeight = holder.seekBar.context.resources
         .getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_height)
+    val seekBarEnabledVerticalPadding = holder.seekBar.context.resources
+            .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_vertical_padding)
+    val seekBarDisabledVerticalPadding = holder.seekBar.context.resources
+            .getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_vertical_padding)
 
     /** Updates seek bar views when the data model changes. */
     @UiThread
@@ -39,6 +43,7 @@
         if (!data.enabled) {
             if (holder.seekBar.maxHeight != seekBarDisabledHeight) {
                 holder.seekBar.maxHeight = seekBarDisabledHeight
+                setVerticalPadding(seekBarDisabledVerticalPadding)
             }
             holder.seekBar.setEnabled(false)
             holder.seekBar.getThumb().setAlpha(0)
@@ -51,8 +56,9 @@
         holder.seekBar.getThumb().setAlpha(if (data.seekAvailable) 255 else 0)
         holder.seekBar.setEnabled(data.seekAvailable)
 
-        if (holder.seekBar.maxHeight != seekBarDefaultMaxHeight) {
-            holder.seekBar.maxHeight = seekBarDefaultMaxHeight
+        if (holder.seekBar.maxHeight != seekBarEnabledMaxHeight) {
+            holder.seekBar.maxHeight = seekBarEnabledMaxHeight
+            setVerticalPadding(seekBarEnabledVerticalPadding)
         }
 
         data.duration?.let {
@@ -67,4 +73,11 @@
                     it / DateUtils.SECOND_IN_MILLIS))
         }
     }
+
+    @UiThread
+    fun setVerticalPadding(padding: Int) {
+        val leftPadding = holder.seekBar.paddingLeft
+        val rightPadding = holder.seekBar.paddingRight
+        holder.seekBar.setPadding(leftPadding, padding, rightPadding, padding)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index e6abea7..54df53d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -303,15 +303,20 @@
      * @param animationDurationMs duration in millisecond for the exiting PiP transition
      */
     public void exitPip(int animationDurationMs) {
-        if (!mState.isInPip() || mState == State.EXITING_PIP || mToken == null) {
+        if (!mState.isInPip() || mToken == null) {
             Log.wtf(TAG, "Not allowed to exitPip in current state"
                     + " mState=" + mState + " mToken=" + mToken);
             return;
         }
 
+        final PipWindowConfigurationCompact config = mCompactState.remove(mToken.asBinder());
+        if (config == null) {
+            Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken);
+            return;
+        }
+
         mPipUiEventLoggerLogger.log(
                 PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
-        final PipWindowConfigurationCompact config = mCompactState.remove(mToken.asBinder());
         config.syncWithScreenOrientation(mRequestedOrientation,
                 mPipBoundsHandler.getDisplayRotation());
         final boolean orientationDiffers = config.getRotation()
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 2a83aa0..586399c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -562,8 +562,10 @@
 
                     // TODO: Check if the action drawable has changed before we reload it
                     action.getIcon().loadDrawableAsync(this, d -> {
-                        d.setTint(Color.WHITE);
-                        actionView.setImageDrawable(d);
+                        if (d != null) {
+                            d.setTint(Color.WHITE);
+                            actionView.setImageDrawable(d);
+                        }
                     }, mHandler);
                     actionView.setContentDescription(action.getContentDescription());
                     if (action.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 7cd1c78..44803ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -177,6 +177,16 @@
     }
 
     @Override
+    public void endFakeDrag() {
+        try {
+            super.endFakeDrag();
+        } catch (NullPointerException e) {
+            // Not sure what's going on. Let's log it
+            Log.e(TAG, "endFakeDrag called without velocityTracker", e);
+        }
+    }
+
+    @Override
     public void computeScroll() {
         if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
             if (!isFakeDragging()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index e738cec..bffeb3e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -14,11 +14,8 @@
 
 package com.android.systemui.qs.customize;
 
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
@@ -28,10 +25,11 @@
 import android.view.View.OnClickListener;
 import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.core.view.AccessibilityDelegateCompat;
 import androidx.core.view.ViewCompat;
 import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup;
 import androidx.recyclerview.widget.ItemTouchHelper;
@@ -49,7 +47,6 @@
 import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.tileimpl.QSIconViewImpl;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -78,10 +75,10 @@
     private final List<TileInfo> mTiles = new ArrayList<>();
     private final ItemTouchHelper mItemTouchHelper;
     private final ItemDecoration mDecoration;
-    private final AccessibilityManager mAccessibilityManager;
     private final int mMinNumTiles;
     private int mEditIndex;
     private int mTileDividerIndex;
+    private int mFocusIndex;
     private boolean mNeedsFocus;
     private List<String> mCurrentSpecs;
     private List<TileInfo> mOtherTiles;
@@ -90,17 +87,28 @@
     private Holder mCurrentDrag;
     private int mAccessibilityAction = ACTION_NONE;
     private int mAccessibilityFromIndex;
-    private CharSequence mAccessibilityFromLabel;
     private QSTileHost mHost;
     private final UiEventLogger mUiEventLogger;
+    private final AccessibilityDelegateCompat mAccessibilityDelegate;
+    private RecyclerView mRecyclerView;
 
     public TileAdapter(Context context, UiEventLogger uiEventLogger) {
         mContext = context;
         mUiEventLogger = uiEventLogger;
-        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mItemTouchHelper = new ItemTouchHelper(mCallbacks);
         mDecoration = new TileItemDecoration(context);
         mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
+        mAccessibilityDelegate = new TileAdapterDelegate();
+    }
+
+    @Override
+    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
+        mRecyclerView = recyclerView;
+    }
+
+    @Override
+    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
+        mRecyclerView = null;
     }
 
     public void setHost(QSTileHost host) {
@@ -130,7 +138,6 @@
             // Remove blank tile from last spot
             mTiles.remove(--mEditIndex);
             // Update the tile divider position
-            mTileDividerIndex--;
             notifyDataSetChanged();
         }
         mAccessibilityAction = ACTION_NONE;
@@ -241,14 +248,12 @@
     }
 
     private void setSelectableForHeaders(View view) {
-        if (mAccessibilityManager.isTouchExplorationEnabled()) {
-            final boolean selectable = mAccessibilityAction == ACTION_NONE;
-            view.setFocusable(selectable);
-            view.setImportantForAccessibility(selectable
-                    ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
-                    : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-            view.setFocusableInTouchMode(selectable);
-        }
+        final boolean selectable = mAccessibilityAction == ACTION_NONE;
+        view.setFocusable(selectable);
+        view.setImportantForAccessibility(selectable
+                ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
+                : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+        view.setFocusableInTouchMode(selectable);
     }
 
     @Override
@@ -285,12 +290,11 @@
             holder.mTileView.setVisibility(View.VISIBLE);
             holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
             holder.mTileView.setContentDescription(mContext.getString(
-                    R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel,
-                    position));
+                    R.string.accessibility_qs_edit_tile_add_to_position, position));
             holder.mTileView.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
-                    selectPosition(holder.getAdapterPosition(), v);
+                    selectPosition(holder.getLayoutPosition());
                 }
             });
             focusOnHolder(holder);
@@ -299,54 +303,49 @@
 
         TileInfo info = mTiles.get(position);
 
-        if (position > mEditIndex) {
+        final boolean selectable = 0 < position && position < mEditIndex;
+        if (selectable && mAccessibilityAction == ACTION_ADD) {
             info.state.contentDescription = mContext.getString(
-                    R.string.accessibility_qs_edit_add_tile_label, info.state.label);
-        } else if (mAccessibilityAction == ACTION_ADD) {
+                    R.string.accessibility_qs_edit_tile_add_to_position, position);
+        } else if (selectable && mAccessibilityAction == ACTION_MOVE) {
             info.state.contentDescription = mContext.getString(
-                    R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel, position);
-        } else if (mAccessibilityAction == ACTION_MOVE) {
-            info.state.contentDescription = mContext.getString(
-                    R.string.accessibility_qs_edit_tile_move, mAccessibilityFromLabel, position);
+                    R.string.accessibility_qs_edit_tile_move_to_position, position);
         } else {
-            info.state.contentDescription = mContext.getString(
-                    R.string.accessibility_qs_edit_tile_label, position, info.state.label);
+            info.state.contentDescription = info.state.label;
         }
+        info.state.expandedAccessibilityClassName = "";
+
         holder.mTileView.handleStateChanged(info.state);
         holder.mTileView.setShowAppLabel(position > mEditIndex && !info.isSystem);
+        holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        holder.mTileView.setClickable(true);
+        holder.mTileView.setOnClickListener(null);
+        holder.mTileView.setFocusable(true);
+        holder.mTileView.setFocusableInTouchMode(true);
 
-        if (mAccessibilityManager.isTouchExplorationEnabled()) {
-            final boolean selectable = mAccessibilityAction == ACTION_NONE || position < mEditIndex;
+        if (mAccessibilityAction != ACTION_NONE) {
             holder.mTileView.setClickable(selectable);
             holder.mTileView.setFocusable(selectable);
+            holder.mTileView.setFocusableInTouchMode(selectable);
             holder.mTileView.setImportantForAccessibility(selectable
                     ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
                     : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-            holder.mTileView.setFocusableInTouchMode(selectable);
             if (selectable) {
                 holder.mTileView.setOnClickListener(new OnClickListener() {
                     @Override
                     public void onClick(View v) {
-                        int position = holder.getAdapterPosition();
+                        int position = holder.getLayoutPosition();
                         if (position == RecyclerView.NO_POSITION) return;
                         if (mAccessibilityAction != ACTION_NONE) {
-                            selectPosition(position, v);
-                        } else {
-                            if (position < mEditIndex && canRemoveTiles()) {
-                                showAccessibilityDialog(position, v);
-                            } else if (position < mEditIndex && !canRemoveTiles()) {
-                                startAccessibleMove(position);
-                            } else {
-                                startAccessibleAdd(position);
-                            }
+                            selectPosition(position);
                         }
                     }
                 });
-                if (position == mAccessibilityFromIndex) {
-                    focusOnHolder(holder);
-                }
             }
         }
+        if (position == mFocusIndex) {
+            focusOnHolder(holder);
+        }
     }
 
     private void focusOnHolder(Holder holder) {
@@ -360,9 +359,13 @@
                         int oldLeft, int oldTop, int oldRight, int oldBottom) {
                     holder.mTileView.removeOnLayoutChangeListener(this);
                     holder.mTileView.requestFocus();
+                    if (mAccessibilityAction == ACTION_NONE) {
+                        holder.mTileView.clearFocus();
+                    }
                 }
             });
             mNeedsFocus = false;
+            mFocusIndex = RecyclerView.NO_POSITION;
         }
     }
 
@@ -370,72 +373,77 @@
         return mCurrentSpecs.size() > mMinNumTiles;
     }
 
-    private void selectPosition(int position, View v) {
+    private void selectPosition(int position) {
         if (mAccessibilityAction == ACTION_ADD) {
             // Remove the placeholder.
             mTiles.remove(mEditIndex--);
-            notifyItemRemoved(mEditIndex);
         }
         mAccessibilityAction = ACTION_NONE;
-        move(mAccessibilityFromIndex, position, v);
+        move(mAccessibilityFromIndex, position, false);
+        mFocusIndex = position;
+        mNeedsFocus = true;
         notifyDataSetChanged();
     }
 
-    private void showAccessibilityDialog(final int position, final View v) {
-        final TileInfo info = mTiles.get(position);
-        CharSequence[] options = new CharSequence[] {
-                mContext.getString(R.string.accessibility_qs_edit_move_tile, info.state.label),
-                mContext.getString(R.string.accessibility_qs_edit_remove_tile, info.state.label),
-        };
-        AlertDialog dialog = new Builder(mContext)
-                .setItems(options, new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        if (which == 0) {
-                            startAccessibleMove(position);
-                        } else {
-                            move(position, info.isSystem ? mEditIndex : mTileDividerIndex, v);
-                            notifyItemChanged(mTileDividerIndex);
-                            notifyDataSetChanged();
-                        }
-                    }
-                }).setNegativeButton(android.R.string.cancel, null)
-                .create();
-        SystemUIDialog.setShowForAllUsers(dialog, true);
-        SystemUIDialog.applyFlags(dialog);
-        dialog.show();
-    }
-
     private void startAccessibleAdd(int position) {
         mAccessibilityFromIndex = position;
-        mAccessibilityFromLabel = mTiles.get(position).state.label;
         mAccessibilityAction = ACTION_ADD;
         // Add placeholder for last slot.
         mTiles.add(mEditIndex++, null);
         // Update the tile divider position
         mTileDividerIndex++;
+        mFocusIndex = mEditIndex - 1;
         mNeedsFocus = true;
+        if (mRecyclerView != null) {
+            mRecyclerView.post(() -> mRecyclerView.smoothScrollToPosition(mFocusIndex));
+        }
         notifyDataSetChanged();
     }
 
     private void startAccessibleMove(int position) {
         mAccessibilityFromIndex = position;
-        mAccessibilityFromLabel = mTiles.get(position).state.label;
         mAccessibilityAction = ACTION_MOVE;
+        mFocusIndex = position;
         mNeedsFocus = true;
         notifyDataSetChanged();
     }
 
+    private boolean canRemoveFromPosition(int position) {
+        return canRemoveTiles() && isCurrentTile(position);
+    }
+
+    private boolean isCurrentTile(int position) {
+        return position < mEditIndex;
+    }
+
+    private boolean canAddFromPosition(int position) {
+        return position > mEditIndex;
+    }
+
+    private void addFromPosition(int position) {
+        if (!canAddFromPosition(position)) return;
+        move(position, mEditIndex);
+    }
+
+    private void removeFromPosition(int position) {
+        if (!canRemoveFromPosition(position)) return;
+        TileInfo info = mTiles.get(position);
+        move(position, info.isSystem ? mEditIndex : mTileDividerIndex);
+    }
+
     public SpanSizeLookup getSizeLookup() {
         return mSizeLookup;
     }
 
-    private boolean move(int from, int to, View v) {
+    private boolean move(int from, int to) {
+        return move(from, to, true);
+    }
+
+    private boolean move(int from, int to, boolean notify) {
         if (to == from) {
             return true;
         }
-        CharSequence fromLabel = mTiles.get(from).state.label;
-        move(from, to, mTiles);
+        move(from, to, mTiles, notify);
         updateDividerLocations();
         if (to >= mEditIndex) {
             mUiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, strip(mTiles.get(to)));
@@ -477,9 +485,11 @@
         return spec;
     }
 
-    private <T> void move(int from, int to, List<T> list) {
+    private <T> void move(int from, int to, List<T> list, boolean notify) {
         list.add(to, list.remove(from));
-        notifyItemMoved(from, to);
+        if (notify) {
+            notifyItemMoved(from, to);
+        }
     }
 
     public class Holder extends ViewHolder {
@@ -491,6 +501,8 @@
                 mTileView = (CustomizeTileView) ((FrameLayout) itemView).getChildAt(0);
                 mTileView.setBackground(null);
                 mTileView.getIcon().disableAnimation();
+                mTileView.setTag(this);
+                ViewCompat.setAccessibilityDelegate(mTileView, mAccessibilityDelegate);
             }
         }
 
@@ -527,6 +539,46 @@
                     .setDuration(DRAG_LENGTH)
                     .alpha(.6f);
         }
+
+        boolean canRemove() {
+            return canRemoveFromPosition(getLayoutPosition());
+        }
+
+        boolean canAdd() {
+            return canAddFromPosition(getLayoutPosition());
+        }
+
+        void toggleState() {
+            if (canAdd()) {
+                add();
+            } else {
+                remove();
+            }
+        }
+
+        private void add() {
+            addFromPosition(getLayoutPosition());
+        }
+
+        private void remove() {
+            removeFromPosition(getLayoutPosition());
+        }
+
+        boolean isCurrentTile() {
+            return TileAdapter.this.isCurrentTile(getLayoutPosition());
+        }
+
+        void startAccessibleAdd() {
+            TileAdapter.this.startAccessibleAdd(getLayoutPosition());
+        }
+
+        void startAccessibleMove() {
+            TileAdapter.this.startAccessibleMove(getLayoutPosition());
+        }
+
+        boolean canTakeAccessibleAction() {
+            return mAccessibilityAction == ACTION_NONE;
+        }
     }
 
     private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() {
@@ -648,7 +700,7 @@
                     to == 0 || to == RecyclerView.NO_POSITION) {
                 return false;
             }
-            return move(from, to, target.itemView);
+            return move(from, to);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java
new file mode 100644
index 0000000..1e426ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.customize;
+
+import android.os.Bundle;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.core.view.AccessibilityDelegateCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+
+import com.android.systemui.R;
+
+import java.util.List;
+
+/**
+ * Accessibility delegate for {@link TileAdapter} views.
+ *
+ * This delegate will populate the accessibility info with the proper actions that can be taken for
+ * the different tiles:
+ * <ul>
+ *   <li>Add to end if the tile is not a current tile (by double tap).</li>
+ *   <li>Add to a given position (by context menu). This will let the user select a position.</li>
+ *   <li>Remove, if the tile is a current tile (by double tap).</li>
+ *   <li>Move to a given position (by context menu). This will let the user select a position.</li>
+ * </ul>
+ *
+ * This only handles generating the associated actions. The logic for selecting positions is handled
+ * by {@link TileAdapter}.
+ *
+ * In order for the delegate to work properly, the asociated {@link TileAdapter.Holder} should be
+ * passed along with the view using {@link View#setTag}.
+ */
+class TileAdapterDelegate extends AccessibilityDelegateCompat {
+
+    private static final int MOVE_TO_POSITION_ID = R.id.accessibility_action_qs_move_to_position;
+    private static final int ADD_TO_POSITION_ID = R.id.accessibility_action_qs_add_to_position;
+
+    private TileAdapter.Holder getHolder(View view) {
+        return (TileAdapter.Holder) view.getTag();
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+        super.onInitializeAccessibilityNodeInfo(host, info);
+        TileAdapter.Holder holder = getHolder(host);
+        info.setCollectionItemInfo(null);
+        info.setStateDescription("");
+        if (holder == null || !holder.canTakeAccessibleAction()) {
+            // If there's not a holder (not a regular Tile) or an action cannot be taken
+            // because we are in the middle of an accessibility action, don't create a special node.
+            return;
+        }
+
+        addClickAction(host, info, holder);
+        maybeAddActionAddToPosition(host, info, holder);
+        maybeAddActionMoveToPosition(host, info, holder);
+
+        if (holder.isCurrentTile()) {
+            info.setStateDescription(host.getContext().getString(
+                    R.string.accessibility_qs_edit_position, holder.getLayoutPosition()));
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(View host, int action, Bundle args) {
+        TileAdapter.Holder holder = getHolder(host);
+
+        if (holder == null || !holder.canTakeAccessibleAction()) {
+            // If there's not a holder (not a regular Tile) or an action cannot be taken
+            // because we are in the middle of an accessibility action, perform the default action.
+            return super.performAccessibilityAction(host, action, args);
+        }
+        if (action == AccessibilityNodeInfo.ACTION_CLICK) {
+            holder.toggleState();
+            return true;
+        } else if (action == MOVE_TO_POSITION_ID) {
+            holder.startAccessibleMove();
+            return true;
+        } else if (action == ADD_TO_POSITION_ID) {
+            holder.startAccessibleAdd();
+            return true;
+        } else {
+            return super.performAccessibilityAction(host, action, args);
+        }
+    }
+
+    private void addClickAction(
+            View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) {
+        String clickActionString;
+        if (holder.canAdd()) {
+            clickActionString = host.getContext().getString(
+                    R.string.accessibility_qs_edit_tile_add_action);
+        } else if (holder.canRemove()) {
+            clickActionString = host.getContext().getString(
+                    R.string.accessibility_qs_edit_remove_tile_action);
+        } else {
+            // Remove the default click action if tile can't either be added or removed (for example
+            // if there's the minimum number of tiles)
+            List<AccessibilityNodeInfoCompat.AccessibilityActionCompat> listOfActions =
+                    info.getActionList(); // This is a copy
+            int numActions = listOfActions.size();
+            for (int i = 0; i < numActions; i++) {
+                if (listOfActions.get(i).getId() == AccessibilityNodeInfo.ACTION_CLICK) {
+                    info.removeAction(listOfActions.get(i));
+                }
+            }
+            return;
+        }
+
+        AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+                new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                        AccessibilityNodeInfo.ACTION_CLICK, clickActionString);
+        info.addAction(action);
+    }
+
+    private void maybeAddActionMoveToPosition(
+            View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) {
+        if (holder.isCurrentTile()) {
+            AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+                    new AccessibilityNodeInfoCompat.AccessibilityActionCompat(MOVE_TO_POSITION_ID,
+                            host.getContext().getString(
+                                    R.string.accessibility_qs_edit_tile_start_move));
+            info.addAction(action);
+        }
+    }
+
+    private void maybeAddActionAddToPosition(
+            View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) {
+        if (holder.canAdd()) {
+            AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+                    new AccessibilityNodeInfoCompat.AccessibilityActionCompat(ADD_TO_POSITION_ID,
+                            host.getContext().getString(
+                                    R.string.accessibility_qs_edit_tile_start_add));
+            info.addAction(action);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 2f58272..acd2846 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -60,7 +60,7 @@
     private static final String PATTERN_HOUR_MINUTE = "h:mm a";
     private static final String PATTERN_HOUR_NINUTE_24 = "HH:mm";
 
-    private final ColorDisplayManager mManager;
+    private ColorDisplayManager mManager;
     private final LocationController mLocationController;
     private NightDisplayListener mListener;
     private boolean mIsListening;
@@ -105,6 +105,8 @@
             mListener.setCallback(null);
         }
 
+        mManager = getHost().getUserContext().getSystemService(ColorDisplayManager.class);
+
         // Make a new controller for the new user.
         mListener = new NightDisplayListener(mContext, newUserId, new Handler(Looper.myLooper()));
         if (mIsListening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 7b83c20..f777553 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -50,16 +50,15 @@
     public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
     private final Icon mIcon = ResourceIcon.get(
             com.android.internal.R.drawable.ic_qs_ui_mode_night);
-    private final UiModeManager mUiModeManager;
+    private UiModeManager mUiModeManager;
     private final BatteryController mBatteryController;
     private final LocationController mLocationController;
-
     @Inject
     public UiModeNightTile(QSHost host, ConfigurationController configurationController,
             BatteryController batteryController, LocationController locationController) {
         super(host);
         mBatteryController = batteryController;
-        mUiModeManager = mContext.getSystemService(UiModeManager.class);
+        mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class);
         mLocationController = locationController;
         configurationController.observe(getLifecycle(), this);
         batteryController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index ff7793d..4699ace 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -167,7 +167,7 @@
         peopleHubSubscription = null
         peopleHeaderView = reinflateView(peopleHeaderView, layoutInflater, R.layout.people_strip)
                 .apply {
-                    setOnHeaderClickListener(View.OnClickListener { onGentleHeaderClick() })
+                    setOnHeaderClickListener(View.OnClickListener { onPeopleHeaderClick() })
                 }
         if (ENABLE_SNOOZED_CONVERSATION_HUB) {
             peopleHubSubscription = peopleHubViewAdapter.bindView(peopleHubViewBoundary)
@@ -522,6 +522,15 @@
                 Intent.FLAG_ACTIVITY_SINGLE_TOP)
     }
 
+    private fun onPeopleHeaderClick() {
+        val intent = Intent(Settings.ACTION_CONVERSATION_SETTINGS)
+        activityStarter.startActivity(
+                intent,
+                true,
+                true,
+                Intent.FLAG_ACTIVITY_SINGLE_TOP)
+    }
+
     private fun onClearGentleNotifsClick(v: View) {
         onClearSilentNotifsClickListener?.onClick(v)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 541c784..7447335 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.row.FooterView;
@@ -687,15 +688,27 @@
             AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
         float childrenOnTop = 0.0f;
+
+        int topHunIndex = -1;
+        for (int i = 0; i < childCount; i++) {
+            ExpandableView child = algorithmState.visibleChildren.get(i);
+            if (child instanceof ActivatableNotificationView
+                    && (child.isAboveShelf() || child.showingPulsing())) {
+                topHunIndex = i;
+                break;
+            }
+        }
+
         for (int i = childCount - 1; i >= 0; i--) {
             childrenOnTop = updateChildZValue(i, childrenOnTop,
-                    algorithmState, ambientState);
+                    algorithmState, ambientState, i == topHunIndex);
         }
     }
 
     protected float updateChildZValue(int i, float childrenOnTop,
             StackScrollAlgorithmState algorithmState,
-            AmbientState ambientState) {
+            AmbientState ambientState,
+            boolean shouldElevateHun) {
         ExpandableView child = algorithmState.visibleChildren.get(i);
         ExpandableViewState childViewState = child.getViewState();
         int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
@@ -713,8 +726,7 @@
             }
             childViewState.zTranslation = baseZ
                     + childrenOnTop * zDistanceBetweenElements;
-        } else if (child == ambientState.getTrackedHeadsUpRow()
-                || (i == 0 && (child.isAboveShelf() || child.showingPulsing()))) {
+        } else if (shouldElevateHun) {
             // In case this is a new view that has never been measured before, we don't want to
             // elevate if we are currently expanded more then the notification
             int shelfHeight = ambientState.getShelf() == null ? 0 :
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index c43ad36..82ad00a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Notification;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -359,6 +360,11 @@
         return false;
     }
 
+    private static boolean isOngoingCallNotif(NotificationEntry entry) {
+        return entry.getSbn().isOngoing() && Notification.CATEGORY_CALL.equals(
+                entry.getSbn().getNotification().category);
+    }
+
     /**
      * This represents a notification and how long it is in a heads up mode. It also manages its
      * lifecycle automatically when created.
@@ -391,6 +397,15 @@
                 return 1;
             }
 
+            boolean selfCall = isOngoingCallNotif(mEntry);
+            boolean otherCall = isOngoingCallNotif(headsUpEntry.mEntry);
+
+            if (selfCall && !otherCall) {
+                return -1;
+            } else if (!selfCall && otherCall) {
+                return 1;
+            }
+
             if (remoteInputActive && !headsUpEntry.remoteInputActive) {
                 return -1;
             } else if (!remoteInputActive && headsUpEntry.remoteInputActive) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 8f082c1..ade3290 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -47,6 +47,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
 
 import org.junit.Before;
@@ -82,6 +83,8 @@
     private PackageManager mPackageManager;
     @Mock(stubOnly = true)
     private AudioManager mAudioManager;
+    @Mock()
+    private BroadcastDispatcher mDispatcher;
     @Mock(stubOnly = true)
     private AudioManager.AudioRecordingCallback mRecordingCallback;
     @Mock(stubOnly = true)
@@ -120,7 +123,8 @@
                 mTestableLooper.getLooper(),
                 mDumpManager,
                 mFlagsCache,
-                mAudioManager
+                mAudioManager,
+                mDispatcher
         );
     }
 
@@ -128,12 +132,14 @@
     public void testOnlyListenForFewOps() {
         mController.setListening(true);
         verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController);
+        verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any());
     }
 
     @Test
     public void testStopListening() {
         mController.setListening(false);
         verify(mAppOpsManager, times(1)).stopWatchingActive(mController);
+        verify(mDispatcher, times(1)).unregisterReceiver(mController);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 15828b4..1ad8856 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -999,6 +999,29 @@
         verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
     }
 
+
+    /**
+     * Verifies that when a non visually interruptive update occurs for a bubble in the overflow,
+     * the that bubble does not get promoted from the overflow.
+     */
+    @Test
+    public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() {
+        // Setup
+        mBubbleController.updateBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow2.getEntry());
+        assertTrue(mBubbleController.hasBubbles());
+
+        // Overflow it
+        mBubbleData.dismissBubbleWithKey(mRow.getEntry().getKey(),
+                BubbleController.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse();
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())).isTrue();
+
+        // Test
+        mBubbleController.updateBubble(mRow.getEntry());
+        assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse();
+    }
+
     /**
      * Sets the bubble metadata flags for this entry. These ]flags are normally set by
      * NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 315caee..4bbc41e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -513,6 +513,26 @@
     }
 
     /**
+     * Verifies that when a non visually interruptive update occurs, that the selection does not
+     * change.
+     */
+    @Test
+    public void test_notVisuallyInterruptive_updateBubble_selectionDoesntChange() {
+        // Setup
+        sendUpdatedEntryAtTime(mEntryA1, 1000);
+        sendUpdatedEntryAtTime(mEntryB1, 2000);
+        sendUpdatedEntryAtTime(mEntryB2, 3000);
+        sendUpdatedEntryAtTime(mEntryA2, 4000); // [A2, B2, B1, A1]
+        mBubbleData.setListener(mListener);
+
+        assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA2);
+
+        // Test
+        sendUpdatedEntryAtTime(mEntryB1, 5000, false /* isVisuallyInterruptive */);
+        assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA2);
+    }
+
+    /**
      * Verifies that a request to expand the stack has no effect if there are no bubbles.
      */
     @Test
@@ -883,9 +903,15 @@
     }
 
     private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) {
+        sendUpdatedEntryAtTime(entry, postTime, true /* visuallyInterruptive */);
+    }
+
+    private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime,
+            boolean visuallyInterruptive) {
         setPostTime(entry, postTime);
         // BubbleController calls this:
         Bubble b = mBubbleData.getOrCreateBubble(entry, null /* persistedBubble */);
+        b.setVisuallyInterruptiveForTest(visuallyInterruptive);
         // And then this
         mBubbleData.notificationEntryUpdated(b, false /* suppressFlyout*/,
                 true /* showInShade */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 8a30b00..81139f19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -43,7 +43,6 @@
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.any
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import dagger.Lazy
@@ -53,7 +52,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
 import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
 import org.mockito.Mockito.anyBoolean
@@ -203,7 +201,7 @@
     fun bindWhenUnattached() {
         val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
                 emptyList(), PACKAGE, null, null, device, true, null)
-        player.bind(state)
+        player.bind(state, PACKAGE)
         assertThat(player.isPlaying()).isFalse()
     }
 
@@ -212,7 +210,7 @@
         player.attach(holder)
         val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
                 emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
-        player.bind(state)
+        player.bind(state, PACKAGE)
         assertThat(appName.getText()).isEqualTo(APP)
         assertThat(titleText.getText()).isEqualTo(TITLE)
         assertThat(artistText.getText()).isEqualTo(ARTIST)
@@ -223,7 +221,7 @@
         player.attach(holder)
         val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
                 emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
-        player.bind(state)
+        player.bind(state, PACKAGE)
         val list = ArgumentCaptor.forClass(ColorStateList::class.java)
         verify(view).setBackgroundTintList(list.capture())
         assertThat(list.value).isEqualTo(ColorStateList.valueOf(BG_COLOR))
@@ -234,7 +232,7 @@
         player.attach(holder)
         val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
                 emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
-        player.bind(state)
+        player.bind(state, PACKAGE)
         assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
         assertThat(seamless.isEnabled()).isTrue()
     }
@@ -246,7 +244,7 @@
         player.attach(holder)
         val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
                 emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, true, null)
-        player.bind(state)
+        player.bind(state, PACKAGE)
         verify(expandedSet).setVisibility(seamless.id, View.GONE)
         verify(expandedSet).setVisibility(seamlessFallback.id, View.VISIBLE)
         verify(collapsedSet).setVisibility(seamless.id, View.GONE)
@@ -258,7 +256,7 @@
         player.attach(holder)
         val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
                 emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null)
-        player.bind(state)
+        player.bind(state, PACKAGE)
         assertThat(seamless.isEnabled()).isTrue()
         assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString(
                 com.android.internal.R.string.ext_media_seamless_action))
@@ -270,7 +268,7 @@
         val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
                 emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null,
                 resumption = true)
-        player.bind(state)
+        player.bind(state, PACKAGE)
         assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
         assertThat(seamless.isEnabled()).isFalse()
     }
@@ -322,31 +320,18 @@
 
     @Test
     fun dismissButtonClick() {
+        val mediaKey = "key for dismissal"
         player.attach(holder)
         val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
                 emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null,
                 notificationKey = KEY)
-        player.bind(state)
+        player.bind(state, mediaKey)
 
         dismiss.callOnClick()
         val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java)
         verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean())
 
         captor.value.onDismiss()
-        verify(mediaDataManager).dismissMediaData(eq(KEY), anyLong())
-    }
-
-    @Test
-    fun dismissButtonClick_nullNotificationKey() {
-        player.attach(holder)
-        val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
-                emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null)
-        player.bind(state)
-
-        verify(keyguardDismissUtil, never())
-                .executeWhenUnlocked(
-                        any(ActivityStarter.OnDismissAction::class.java),
-                        ArgumentMatchers.anyBoolean()
-                )
+        verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
new file mode 100644
index 0000000..5d81de6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.SharedPreferences
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.content.pm.ServiceInfo
+import android.graphics.Color
+import android.media.MediaDescription
+import android.media.session.MediaSession
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.After
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+private const val KEY = "TEST_KEY"
+private const val OLD_KEY = "RESUME_KEY"
+private const val APP = "APP"
+private const val BG_COLOR = Color.RED
+private const val PACKAGE_NAME = "PKG"
+private const val CLASS_NAME = "CLASS"
+private const val ARTIST = "ARTIST"
+private const val TITLE = "TITLE"
+private const val USER_ID = 0
+private const val MEDIA_PREFERENCES = "media_control_prefs"
+private const val RESUME_COMPONENTS = "package1/class1:package2/class2:package3/class3"
+
+private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+private fun <T> any(): T = Mockito.any<T>()
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class MediaResumeListenerTest : SysuiTestCase() {
+
+    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock private lateinit var mediaDataManager: MediaDataManager
+    @Mock private lateinit var device: MediaDeviceData
+    @Mock private lateinit var token: MediaSession.Token
+    @Mock private lateinit var tunerService: TunerService
+    @Mock private lateinit var resumeBrowserFactory: ResumeMediaBrowserFactory
+    @Mock private lateinit var resumeBrowser: ResumeMediaBrowser
+    @Mock private lateinit var sharedPrefs: SharedPreferences
+    @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor
+    @Mock private lateinit var mockContext: Context
+    @Mock private lateinit var pendingIntent: PendingIntent
+
+    @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback>
+
+    private lateinit var executor: FakeExecutor
+    private lateinit var data: MediaData
+    private lateinit var resumeListener: MediaResumeListener
+
+    private var originalQsSetting = Settings.Global.getInt(context.contentResolver,
+        Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1)
+    private var originalResumeSetting = Settings.Secure.getInt(context.contentResolver,
+        Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        Settings.Global.putInt(context.contentResolver,
+            Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1)
+        Settings.Secure.putInt(context.contentResolver,
+            Settings.Secure.MEDIA_CONTROLS_RESUME, 1)
+
+        whenever(resumeBrowserFactory.create(capture(callbackCaptor), any()))
+                .thenReturn(resumeBrowser)
+
+        // resume components are stored in sharedpreferences
+        whenever(mockContext.getSharedPreferences(eq(MEDIA_PREFERENCES), anyInt()))
+                .thenReturn(sharedPrefs)
+        whenever(sharedPrefs.getString(any(), any())).thenReturn(RESUME_COMPONENTS)
+        whenever(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
+        whenever(sharedPrefsEditor.putString(any(), any())).thenReturn(sharedPrefsEditor)
+        whenever(mockContext.packageManager).thenReturn(context.packageManager)
+        whenever(mockContext.contentResolver).thenReturn(context.contentResolver)
+
+        executor = FakeExecutor(FakeSystemClock())
+        resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor,
+                tunerService, resumeBrowserFactory)
+        resumeListener.setManager(mediaDataManager)
+        mediaDataManager.addListener(resumeListener)
+
+        data = MediaData(
+                userId = USER_ID,
+                initialized = true,
+                backgroundColor = BG_COLOR,
+                app = APP,
+                appIcon = null,
+                artist = ARTIST,
+                song = TITLE,
+                artwork = null,
+                actions = emptyList(),
+                actionsToShowInCompact = emptyList(),
+                packageName = PACKAGE_NAME,
+                token = token,
+                clickIntent = null,
+                device = device,
+                active = true,
+                notificationKey = KEY,
+                resumeAction = null)
+    }
+
+    @After
+    fun tearDown() {
+        Settings.Global.putInt(context.contentResolver,
+            Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, originalQsSetting)
+        Settings.Secure.putInt(context.contentResolver,
+            Settings.Secure.MEDIA_CONTROLS_RESUME, originalResumeSetting)
+    }
+
+    @Test
+    fun testWhenNoResumption_doesNothing() {
+        Settings.Secure.putInt(context.contentResolver,
+            Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
+
+        // When listener is created, we do NOT register a user change listener
+        val listener = MediaResumeListener(context, broadcastDispatcher, executor, tunerService,
+                resumeBrowserFactory)
+        listener.setManager(mediaDataManager)
+        verify(broadcastDispatcher, never()).registerReceiver(eq(listener.userChangeReceiver),
+            any(), any(), any())
+
+        // When data is loaded, we do NOT execute or update anything
+        listener.onMediaDataLoaded(KEY, OLD_KEY, data)
+        assertThat(executor.numPending()).isEqualTo(0)
+        verify(mediaDataManager, never()).setResumeAction(any(), any())
+    }
+
+    @Test
+    fun testOnLoad_checksForResume_noService() {
+        // When media data is loaded that has not been checked yet, and does not have a MBS
+        resumeListener.onMediaDataLoaded(KEY, null, data)
+
+        // Then we report back to the manager
+        verify(mediaDataManager).setResumeAction(KEY, null)
+    }
+
+    @Test
+    fun testOnLoad_checksForResume_hasService() {
+        // Set up mocks to successfully find a MBS that returns valid media
+        val pm = mock(PackageManager::class.java)
+        whenever(mockContext.packageManager).thenReturn(pm)
+        val resolveInfo = ResolveInfo()
+        val serviceInfo = ServiceInfo()
+        serviceInfo.packageName = PACKAGE_NAME
+        resolveInfo.serviceInfo = serviceInfo
+        resolveInfo.serviceInfo.name = CLASS_NAME
+        val resumeInfo = listOf(resolveInfo)
+        whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
+
+        val description = MediaDescription.Builder().setTitle(TITLE).build()
+        val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+        whenever(resumeBrowser.testConnection()).thenAnswer {
+            callbackCaptor.value.addTrack(description, component, resumeBrowser)
+        }
+
+        // When media data is loaded that has not been checked yet, and does have a MBS
+        val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
+        resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+
+        // Then we test whether the service is valid
+        executor.runAllReady()
+        verify(resumeBrowser).testConnection()
+
+        // And since it is, we report back to the manager
+        verify(mediaDataManager).setResumeAction(eq(KEY), any())
+
+        // But we do not tell it to add new controls
+        verify(mediaDataManager, never())
+                .addResumptionControls(anyInt(), any(), any(), any(), any(), any(), any())
+
+        // Finally, make sure the resume browser disconnected
+        verify(resumeBrowser).disconnect()
+    }
+
+    @Test
+    fun testOnLoad_doesNotCheckAgain() {
+        // When a media data is loaded that has been checked already
+        var dataCopy = data.copy(hasCheckedForResume = true)
+        resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+
+        // Then we should not check it again
+        verify(resumeBrowser, never()).testConnection()
+        verify(mediaDataManager, never()).setResumeAction(KEY, null)
+    }
+
+    @Test
+    fun testOnUserUnlock_loadsTracks() {
+        // Set up mock service to successfully find valid media
+        val description = MediaDescription.Builder().setTitle(TITLE).build()
+        val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+        whenever(resumeBrowser.token).thenReturn(token)
+        whenever(resumeBrowser.appIntent).thenReturn(pendingIntent)
+        whenever(resumeBrowser.findRecentMedia()).thenAnswer {
+            callbackCaptor.value.addTrack(description, component, resumeBrowser)
+        }
+
+        // Make sure broadcast receiver is registered
+        resumeListener.setManager(mediaDataManager)
+        verify(broadcastDispatcher).registerReceiver(eq(resumeListener.userChangeReceiver),
+                any(), any(), any())
+
+        // When we get an unlock event
+        val intent = Intent(Intent.ACTION_USER_UNLOCKED)
+        resumeListener.userChangeReceiver.onReceive(context, intent)
+
+        // Then we should attempt to find recent media for each saved component
+        verify(resumeBrowser, times(3)).findRecentMedia()
+
+        // Then since the mock service found media, the manager should be informed
+        verify(mediaDataManager, times(3)).addResumptionControls(anyInt(),
+                any(), any(), any(), any(), any(), eq(PACKAGE_NAME))
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index 7a8e4f7..f397959 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -23,8 +23,9 @@
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Rule
@@ -33,7 +34,6 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Captor
 import org.mockito.Mock
@@ -63,10 +63,8 @@
 
     @Mock private lateinit var mediaControllerFactory: MediaControllerFactory
     @Mock private lateinit var mediaController: MediaController
-    @Mock private lateinit var executor: DelayableExecutor
+    private lateinit var executor: FakeExecutor
     @Mock private lateinit var timeoutCallback: (String, Boolean) -> Unit
-    @Mock private lateinit var cancellationRunnable: Runnable
-    @Captor private lateinit var timeoutCaptor: ArgumentCaptor<Runnable>
     @Captor private lateinit var mediaCallbackCaptor: ArgumentCaptor<MediaController.Callback>
     @JvmField @Rule val mockito = MockitoJUnit.rule()
     private lateinit var metadataBuilder: MediaMetadata.Builder
@@ -78,7 +76,7 @@
     @Before
     fun setup() {
         `when`(mediaControllerFactory.create(any())).thenReturn(mediaController)
-        `when`(executor.executeDelayed(any(), anyLong())).thenReturn(cancellationRunnable)
+        executor = FakeExecutor(FakeSystemClock())
         mediaTimeoutListener = MediaTimeoutListener(mediaControllerFactory, executor)
         mediaTimeoutListener.timeoutCallback = timeoutCallback
 
@@ -120,7 +118,7 @@
     fun testOnMediaDataLoaded_registersTimeout_whenPaused() {
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
         verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
-        verify(executor).executeDelayed(capture(timeoutCaptor), anyLong())
+        assertThat(executor.numPending()).isEqualTo(1)
         verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
     }
 
@@ -137,6 +135,17 @@
     }
 
     @Test
+    fun testOnMediaDataRemoved_clearsTimeout() {
+        // GIVEN media that is paused
+        mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+        assertThat(executor.numPending()).isEqualTo(1)
+        // WHEN the media is removed
+        mediaTimeoutListener.onMediaDataRemoved(KEY)
+        // THEN the timeout runnable is cancelled
+        assertThat(executor.numPending()).isEqualTo(0)
+    }
+
+    @Test
     fun testOnMediaDataLoaded_migratesKeys() {
         // From not playing
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
@@ -151,7 +160,24 @@
         verify(mediaController).registerCallback(anyObject())
 
         // Enqueues callback
-        verify(executor).execute(anyObject())
+        assertThat(executor.numPending()).isEqualTo(1)
+    }
+
+    @Test
+    fun testOnMediaDataLoaded_migratesKeys_noTimeoutExtension() {
+        // From not playing
+        mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+        clearInvocations(mediaController)
+
+        // Migrate, still not playing
+        val playingState = mock(android.media.session.PlaybackState::class.java)
+        `when`(playingState.state).thenReturn(PlaybackState.STATE_PAUSED)
+        `when`(mediaController.playbackState).thenReturn(playingState)
+        mediaTimeoutListener.onMediaDataLoaded("NEWKEY", KEY, mediaData)
+
+        // The number of queued timeout tasks remains the same. The timeout task isn't cancelled nor
+        // is another scheduled
+        assertThat(executor.numPending()).isEqualTo(1)
     }
 
     @Test
@@ -161,7 +187,7 @@
 
         mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder()
                 .setState(PlaybackState.STATE_PAUSED, 0L, 0f).build())
-        verify(executor).executeDelayed(capture(timeoutCaptor), anyLong())
+        assertThat(executor.numPending()).isEqualTo(1)
     }
 
     @Test
@@ -171,7 +197,7 @@
 
         mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder()
                 .setState(PlaybackState.STATE_PLAYING, 0L, 0f).build())
-        verify(cancellationRunnable).run()
+        assertThat(executor.numPending()).isEqualTo(0)
     }
 
     @Test
@@ -179,10 +205,9 @@
         // Assuming we have a pending timeout
         testOnPlaybackStateChanged_schedulesTimeout_whenPaused()
 
-        clearInvocations(cancellationRunnable)
         mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder()
                 .setState(PlaybackState.STATE_STOPPED, 0L, 0f).build())
-        verify(cancellationRunnable, never()).run()
+        assertThat(executor.numPending()).isEqualTo(1)
     }
 
     @Test
@@ -190,7 +215,10 @@
         // Assuming we're have a pending timeout
         testOnPlaybackStateChanged_schedulesTimeout_whenPaused()
 
-        timeoutCaptor.value.run()
+        with(executor) {
+            advanceClockToNext()
+            runAllReady()
+        }
         verify(timeoutCallback).invoke(eq(KEY), eq(true))
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
new file mode 100644
index 0000000..d26229e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.content.ComponentName
+import android.content.Context
+import android.media.MediaDescription
+import android.media.browse.MediaBrowser
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.service.media.MediaBrowserService
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+private const val PACKAGE_NAME = "package"
+private const val CLASS_NAME = "class"
+private const val TITLE = "song title"
+private const val MEDIA_ID = "media ID"
+private const val ROOT = "media browser root"
+
+private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+private fun <T> any(): T = Mockito.any<T>()
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+public class ResumeMediaBrowserTest : SysuiTestCase() {
+
+    private lateinit var resumeBrowser: TestableResumeMediaBrowser
+    private val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+    private val description = MediaDescription.Builder()
+            .setTitle(TITLE)
+            .setMediaId(MEDIA_ID)
+            .build()
+
+    @Mock lateinit var callback: ResumeMediaBrowser.Callback
+    @Mock lateinit var listener: MediaResumeListener
+    @Mock lateinit var service: MediaBrowserService
+    @Mock lateinit var browserFactory: MediaBrowserFactory
+    @Mock lateinit var browser: MediaBrowser
+    @Mock lateinit var token: MediaSession.Token
+    @Mock lateinit var mediaController: MediaController
+    @Mock lateinit var transportControls: MediaController.TransportControls
+
+    @Captor lateinit var connectionCallback: ArgumentCaptor<MediaBrowser.ConnectionCallback>
+    @Captor lateinit var subscriptionCallback: ArgumentCaptor<MediaBrowser.SubscriptionCallback>
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        whenever(browserFactory.create(any(), capture(connectionCallback), any()))
+                .thenReturn(browser)
+
+        whenever(mediaController.transportControls).thenReturn(transportControls)
+
+        resumeBrowser = TestableResumeMediaBrowser(context, callback, component, browserFactory,
+                mediaController)
+    }
+
+    @Test
+    fun testConnection_connectionFails_callsOnError() {
+        // When testConnection cannot connect to the service
+        setupBrowserFailed()
+        resumeBrowser.testConnection()
+
+        // Then it calls onError
+        verify(callback).onError()
+    }
+
+    @Test
+    fun testConnection_connects_onConnected() {
+        // When testConnection can connect to the service
+        setupBrowserConnection()
+        resumeBrowser.testConnection()
+
+        // Then it calls onConnected
+        verify(callback).onConnected()
+    }
+
+    @Test
+    fun testConnection_noValidMedia_error() {
+        // When testConnection can connect to the service, and does not find valid media
+        setupBrowserConnectionNoResults()
+        resumeBrowser.testConnection()
+
+        // Then it calls onError
+        verify(callback).onError()
+    }
+
+    @Test
+    fun testConnection_hasValidMedia_addTrack() {
+        // When testConnection can connect to the service, and finds valid media
+        setupBrowserConnectionValidMedia()
+        resumeBrowser.testConnection()
+
+        // Then it calls addTrack
+        verify(callback).onConnected()
+        verify(callback).addTrack(eq(description), eq(component), eq(resumeBrowser))
+    }
+
+    @Test
+    fun testFindRecentMedia_connectionFails_error() {
+        // When findRecentMedia is called and we cannot connect
+        setupBrowserFailed()
+        resumeBrowser.findRecentMedia()
+
+        // Then it calls onError
+        verify(callback).onError()
+    }
+
+    @Test
+    fun testFindRecentMedia_noRoot_error() {
+        // When findRecentMedia is called and does not get a valid root
+        setupBrowserConnection()
+        whenever(browser.getRoot()).thenReturn(null)
+        resumeBrowser.findRecentMedia()
+
+        // Then it calls onError
+        verify(callback).onError()
+    }
+
+    @Test
+    fun testFindRecentMedia_connects_onConnected() {
+        // When findRecentMedia is called and we connect
+        setupBrowserConnection()
+        resumeBrowser.findRecentMedia()
+
+        // Then it calls onConnected
+        verify(callback).onConnected()
+    }
+
+    @Test
+    fun testFindRecentMedia_noChildren_error() {
+        // When findRecentMedia is called and we connect, but do not get any results
+        setupBrowserConnectionNoResults()
+        resumeBrowser.findRecentMedia()
+
+        // Then it calls onError
+        verify(callback).onError()
+    }
+
+    @Test
+    fun testFindRecentMedia_notPlayable_error() {
+        // When findRecentMedia is called and we connect, but do not get a playable child
+        setupBrowserConnectionNotPlayable()
+        resumeBrowser.findRecentMedia()
+
+        // Then it calls onError
+        verify(callback).onError()
+    }
+
+    @Test
+    fun testFindRecentMedia_hasValidMedia_addTrack() {
+        // When findRecentMedia is called and we can connect and get playable media
+        setupBrowserConnectionValidMedia()
+        resumeBrowser.findRecentMedia()
+
+        // Then it calls addTrack
+        verify(callback).addTrack(eq(description), eq(component), eq(resumeBrowser))
+    }
+
+    @Test
+    fun testRestart_connectionFails_error() {
+        // When restart is called and we cannot connect
+        setupBrowserFailed()
+        resumeBrowser.restart()
+
+        // Then it calls onError
+        verify(callback).onError()
+    }
+
+    @Test
+    fun testRestart_connects() {
+        // When restart is called and we connect successfully
+        setupBrowserConnection()
+        resumeBrowser.restart()
+
+        // Then it creates a new controller and sends play command
+        verify(transportControls).prepare()
+        verify(transportControls).play()
+
+        // Then it calls onConnected
+        verify(callback).onConnected()
+    }
+
+    /**
+     * Helper function to mock a failed connection
+     */
+    private fun setupBrowserFailed() {
+        whenever(browser.connect()).thenAnswer {
+            connectionCallback.value.onConnectionFailed()
+        }
+    }
+
+    /**
+     * Helper function to mock a successful connection only
+     */
+    private fun setupBrowserConnection() {
+        whenever(browser.connect()).thenAnswer {
+            connectionCallback.value.onConnected()
+        }
+        whenever(browser.isConnected()).thenReturn(true)
+        whenever(browser.getRoot()).thenReturn(ROOT)
+        whenever(browser.sessionToken).thenReturn(token)
+    }
+
+    /**
+     * Helper function to mock a successful connection, but no media results
+     */
+    private fun setupBrowserConnectionNoResults() {
+        setupBrowserConnection()
+        whenever(browser.subscribe(any(), capture(subscriptionCallback))).thenAnswer {
+            subscriptionCallback.value.onChildrenLoaded(ROOT, emptyList())
+        }
+    }
+
+    /**
+     * Helper function to mock a successful connection, but no playable results
+     */
+    private fun setupBrowserConnectionNotPlayable() {
+        setupBrowserConnection()
+
+        val child = MediaBrowser.MediaItem(description, 0)
+
+        whenever(browser.subscribe(any(), capture(subscriptionCallback))).thenAnswer {
+            subscriptionCallback.value.onChildrenLoaded(ROOT, listOf(child))
+        }
+    }
+
+    /**
+     * Helper function to mock a successful connection with playable media
+     */
+    private fun setupBrowserConnectionValidMedia() {
+        setupBrowserConnection()
+
+        val child = MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_PLAYABLE)
+
+        whenever(browser.serviceComponent).thenReturn(component)
+        whenever(browser.subscribe(any(), capture(subscriptionCallback))).thenAnswer {
+            subscriptionCallback.value.onChildrenLoaded(ROOT, listOf(child))
+        }
+    }
+
+    /**
+     * Override so media controller use is testable
+     */
+    private class TestableResumeMediaBrowser(
+        context: Context,
+        callback: Callback,
+        componentName: ComponentName,
+        browserFactory: MediaBrowserFactory,
+        private val fakeController: MediaController
+    ) : ResumeMediaBrowser(context, callback, componentName, browserFactory) {
+
+        override fun createMediaController(token: MediaSession.Token): MediaController {
+            return fakeController
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
new file mode 100644
index 0000000..a5dead0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.customize;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+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)
+public class TileAdapterDelegateTest extends SysuiTestCase {
+
+    private static final int MOVE_TO_POSITION_ID = R.id.accessibility_action_qs_move_to_position;
+    private static final int ADD_TO_POSITION_ID = R.id.accessibility_action_qs_add_to_position;
+    private static final int POSITION_STRING_ID = R.string.accessibility_qs_edit_position;
+
+    @Mock
+    private TileAdapter.Holder mHolder;
+
+    private AccessibilityNodeInfoCompat mInfo;
+    private TileAdapterDelegate mDelegate;
+    private View mView;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mView = new View(mContext);
+        mDelegate = new TileAdapterDelegate();
+        mInfo = AccessibilityNodeInfoCompat.obtain();
+    }
+
+    @Test
+    public void testInfoNoSpecialActionsWhenNoHolder() {
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+        for (AccessibilityNodeInfoCompat.AccessibilityActionCompat action : mInfo.getActionList()) {
+            if (action.getId() == MOVE_TO_POSITION_ID || action.getId() == ADD_TO_POSITION_ID
+                    || action.getId() == AccessibilityNodeInfo.ACTION_CLICK) {
+                fail("It should not have special action " + action.getId());
+            }
+        }
+    }
+
+    @Test
+    public void testInfoNoSpecialActionsWhenCannotStartAccessibleAction() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(false);
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+        for (AccessibilityNodeInfoCompat.AccessibilityActionCompat action : mInfo.getActionList()) {
+            if (action.getId() == MOVE_TO_POSITION_ID || action.getId() == ADD_TO_POSITION_ID
+                    || action.getId() == AccessibilityNodeInfo.ACTION_CLICK) {
+                fail("It should not have special action " + action.getId());
+            }
+        }
+    }
+
+    @Test
+    public void testNoCollectionItemInfo() {
+        mInfo.setCollectionItemInfo(
+                AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(0, 1, 0, 1, false));
+
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+        assertThat(mInfo.getCollectionItemInfo()).isNull();
+    }
+
+    @Test
+    public void testStateDescriptionHasPositionForCurrentTile() {
+        mView.setTag(mHolder);
+        int position = 3;
+        when(mHolder.getLayoutPosition()).thenReturn(position);
+        when(mHolder.isCurrentTile()).thenReturn(true);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+
+        String expectedString = mContext.getString(POSITION_STRING_ID, position);
+
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+        assertThat(mInfo.getStateDescription()).isEqualTo(expectedString);
+    }
+
+    @Test
+    public void testStateDescriptionEmptyForNotCurrentTile() {
+        mView.setTag(mHolder);
+        int position = 3;
+        when(mHolder.getLayoutPosition()).thenReturn(position);
+        when(mHolder.isCurrentTile()).thenReturn(false);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+        assertThat(mInfo.getStateDescription()).isEqualTo("");
+    }
+
+    @Test
+    public void testClickAddAction() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+        when(mHolder.canAdd()).thenReturn(true);
+        when(mHolder.canRemove()).thenReturn(false);
+
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+
+        String expectedString = mContext.getString(R.string.accessibility_qs_edit_tile_add_action);
+        AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+                getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK);
+        assertThat(action.getLabel().toString()).contains(expectedString);
+    }
+
+    @Test
+    public void testClickRemoveAction() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+        when(mHolder.canAdd()).thenReturn(false);
+        when(mHolder.canRemove()).thenReturn(true);
+
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+
+        String expectedString = mContext.getString(
+                R.string.accessibility_qs_edit_remove_tile_action);
+        AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+                getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK);
+        assertThat(action.getLabel().toString()).contains(expectedString);
+    }
+
+    @Test
+    public void testNoClickAction() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+        when(mHolder.canAdd()).thenReturn(false);
+        when(mHolder.canRemove()).thenReturn(false);
+        mInfo.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+
+        AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+                getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK);
+        assertThat(action).isNull();
+    }
+
+    @Test
+    public void testAddToPositionAction() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+        when(mHolder.canAdd()).thenReturn(true);
+
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+        assertThat(getActionForId(mInfo, ADD_TO_POSITION_ID)).isNotNull();
+    }
+
+    @Test
+    public void testNoAddToPositionAction() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+        when(mHolder.canAdd()).thenReturn(false);
+
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+        assertThat(getActionForId(mInfo, ADD_TO_POSITION_ID)).isNull();
+    }
+
+    @Test
+    public void testMoveToPositionAction() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+        when(mHolder.isCurrentTile()).thenReturn(true);
+
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+        assertThat(getActionForId(mInfo, MOVE_TO_POSITION_ID)).isNotNull();
+    }
+
+    @Test
+    public void testNoMoveToPositionAction() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+        when(mHolder.isCurrentTile()).thenReturn(false);
+
+        mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+        assertThat(getActionForId(mInfo, MOVE_TO_POSITION_ID)).isNull();
+    }
+
+    @Test
+    public void testNoInteractionsWhenCannotTakeAccessibleAction() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(false);
+
+        mDelegate.performAccessibilityAction(mView, AccessibilityNodeInfo.ACTION_CLICK, null);
+        mDelegate.performAccessibilityAction(mView, MOVE_TO_POSITION_ID, new Bundle());
+        mDelegate.performAccessibilityAction(mView, ADD_TO_POSITION_ID, new Bundle());
+
+        verify(mHolder, never()).toggleState();
+        verify(mHolder, never()).startAccessibleAdd();
+        verify(mHolder, never()).startAccessibleMove();
+    }
+
+    @Test
+    public void testClickActionTogglesState() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+
+        mDelegate.performAccessibilityAction(mView, AccessibilityNodeInfo.ACTION_CLICK, null);
+
+        verify(mHolder).toggleState();
+    }
+
+    @Test
+    public void testAddToPositionActionStartsAccessibleAdd() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+
+        mDelegate.performAccessibilityAction(mView, ADD_TO_POSITION_ID, null);
+
+        verify(mHolder).startAccessibleAdd();
+    }
+
+    @Test
+    public void testMoveToPositionActionStartsAccessibleMove() {
+        mView.setTag(mHolder);
+        when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+
+        mDelegate.performAccessibilityAction(mView, MOVE_TO_POSITION_ID, null);
+
+        verify(mHolder).startAccessibleMove();
+    }
+
+    private AccessibilityNodeInfoCompat.AccessibilityActionCompat getActionForId(
+            AccessibilityNodeInfoCompat info, int action) {
+        for (AccessibilityNodeInfoCompat.AccessibilityActionCompat a : info.getActionList()) {
+            if (a.getId() == action) {
+                return a;
+            }
+        }
+        return null;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 402a99d..dee6020 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -108,11 +108,7 @@
         return new TestableAlertingNotificationManager();
     }
 
-    protected StatusBarNotification createNewNotification(int id) {
-        Notification.Builder n = new Notification.Builder(mContext, "")
-                .setSmallIcon(R.drawable.ic_person)
-                .setContentTitle("Title")
-                .setContentText("Text");
+    protected StatusBarNotification createNewSbn(int id, Notification.Builder n) {
         return new StatusBarNotification(
                 TEST_PACKAGE_NAME /* pkg */,
                 TEST_PACKAGE_NAME,
@@ -126,6 +122,14 @@
                 0 /* postTime */);
     }
 
+    protected StatusBarNotification createNewNotification(int id) {
+        Notification.Builder n = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text");
+        return createNewSbn(id, n);
+    }
+
     @Before
     public void setUp() {
         mTestHandler = Handler.createAsync(Looper.myLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index fc7d0ce..0e4b053 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -16,12 +16,15 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 
+import android.app.Notification;
 import android.content.Context;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -30,6 +33,7 @@
 
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AlertingNotificationManagerTest;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -84,5 +88,25 @@
         assertTrue("Heads up should live long enough", mLivesPastNormalTime);
         assertFalse(mHeadsUpManager.isAlerting(mEntry.getKey()));
     }
+
+    @Test
+    public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
+        HeadsUpManager.HeadsUpEntry ongoingCall = mHeadsUpManager.new HeadsUpEntry();
+        ongoingCall.setEntry(new NotificationEntryBuilder()
+                .setSbn(createNewSbn(0,
+                        new Notification.Builder(mContext, "")
+                                .setCategory(Notification.CATEGORY_CALL)
+                                .setOngoing(true)))
+                .build());
+
+        HeadsUpManager.HeadsUpEntry activeRemoteInput = mHeadsUpManager.new HeadsUpEntry();
+        activeRemoteInput.setEntry(new NotificationEntryBuilder()
+                .setSbn(createNewNotification(1))
+                .build());
+        activeRemoteInput.remoteInputActive = true;
+
+        assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0);
+        assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0);
+    }
 }
 
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 829fca6..9fc8f0b 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -454,10 +454,14 @@
         public boolean mayObservePackage(String packageName) {
             PackageManager pm = mContext.getPackageManager();
             try {
-                // A package is a Mainline module if this is non-null
+                // A package is a module if this is non-null
                 if (pm.getModuleInfo(packageName, 0) != null) {
                     return true;
                 }
+            } catch (PackageManager.NameNotFoundException ignore) {
+            }
+
+            try {
                 ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
                 return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
             } catch (PackageManager.NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index b5aea75..daae1a1 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -134,6 +134,7 @@
     int mCurUiMode = 0;
     private int mSetUiMode = 0;
     private boolean mHoldingConfiguration = false;
+    private int mCurrentUser;
 
     private Configuration mConfiguration = new Configuration();
     boolean mSystemReady;
@@ -323,6 +324,7 @@
     @Override
     public void onSwitchUser(int userHandle) {
         super.onSwitchUser(userHandle);
+      	mCurrentUser = userHandle;
         getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
         verifySetupWizardCompleted();
     }
@@ -728,16 +730,30 @@
 
         @Override
         public boolean setNightModeActivated(boolean active) {
+            if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+                    != PackageManager.PERMISSION_GRANTED)) {
+                Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
+                return false;
+            }
+            final int user = Binder.getCallingUserHandle().getIdentifier();
+            if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Slog.e(TAG, "Target user is not current user,"
+                        + " INTERACT_ACROSS_USERS permission is required");
+                return false;
+
+            }
             synchronized (mLock) {
-                final int user = UserHandle.getCallingUserId();
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                         unregisterScreenOffEventLocked();
                         mOverrideNightModeOff = !active;
                         mOverrideNightModeOn = active;
-                        mOverrideNightModeUser = user;
-                        persistNightModeOverrides(user);
+                        mOverrideNightModeUser = mCurrentUser;
+                        persistNightModeOverrides(mCurrentUser);
                     } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
                             && active) {
                         mNightMode = UiModeManager.MODE_NIGHT_YES;
@@ -747,7 +763,7 @@
                     }
                     updateConfigurationLocked();
                     applyConfigurationExternallyLocked();
-                    persistNightMode(user);
+                    persistNightMode(mCurrentUser);
                     return true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e1bb4cd..dd0e1f6 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -453,16 +453,16 @@
     }
 
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
-            int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification,
-            String callingPackage, @Nullable String callingFeatureId, final int userId)
+            int callingPid, int callingUid, boolean fgRequired, String callingPackage,
+            @Nullable String callingFeatureId, final int userId)
             throws TransactionTooLargeException {
         return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
-                hideFgNotification, callingPackage, callingFeatureId, userId, false);
+                callingPackage, callingFeatureId, userId, false);
     }
 
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
-            int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification,
-            String callingPackage, @Nullable String callingFeatureId, final int userId,
+            int callingPid, int callingUid, boolean fgRequired, String callingPackage,
+            @Nullable String callingFeatureId, final int userId,
             boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
         if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
                 + " type=" + resolvedType + " args=" + service.getExtras());
@@ -609,7 +609,6 @@
         r.startRequested = true;
         r.delayedStop = false;
         r.fgRequired = fgRequired;
-        r.hideFgNotification = hideFgNotification;
         r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                 service, neededGrants, callingUid));
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 491579a..3b85180 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1438,10 +1438,6 @@
 
     final Injector mInjector;
 
-    /** The package verifier app. */
-    private String mPackageVerifier;
-    private int mPackageVerifierUid = UserHandle.USER_NULL;
-
     static final class ProcessChangeItem {
         static final int CHANGE_ACTIVITIES = 1<<0;
         static final int CHANGE_FOREGROUND_SERVICES = 1<<1;
@@ -2350,18 +2346,6 @@
             if (phase == PHASE_SYSTEM_SERVICES_READY) {
                 mService.mBatteryStatsService.systemServicesReady();
                 mService.mServices.systemServicesReady();
-                mService.mPackageVerifier = ArrayUtils.firstOrNull(
-                        LocalServices.getService(PackageManagerInternal.class).getKnownPackageNames(
-                                PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM));
-                if (mService.mPackageVerifier != null) {
-                    try {
-                        mService.mPackageVerifierUid =
-                                getContext().getPackageManager().getPackageUid(
-                                        mService.mPackageVerifier, UserHandle.USER_SYSTEM);
-                    } catch (NameNotFoundException e) {
-                        Slog.wtf(TAG, "Package manager couldn't get package verifier uid", e);
-                    }
-                }
             } else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
                 mService.startBroadcastObservers();
             } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -10530,7 +10514,7 @@
 
     private void dumpEverything(FileDescriptor fd, PrintWriter pw, String[] args, int opti,
             boolean dumpAll, String dumpPackage, boolean dumpClient, boolean dumpNormalPriority,
-            int dumpAppId) {
+            int dumpAppId, boolean dumpProxies) {
 
         ActiveServices.ServiceDumper sdumper;
 
@@ -10589,7 +10573,7 @@
             }
             sdumper.dumpWithClient();
         }
-        if (dumpPackage == null) {
+        if (dumpPackage == null && dumpProxies) {
             // Intentionally dropping the lock for this, because dumpBinderProxies() will make many
             // outgoing binder calls to retrieve interface descriptors; while that is system code,
             // there is nothing preventing an app from overriding this implementation by talking to
@@ -10998,13 +10982,14 @@
                 // dumpEverything() will take the lock when needed, and momentarily drop
                 // it for dumping client state.
                 dumpEverything(fd, pw, args, opti, dumpAll, dumpPackage, dumpClient,
-                        dumpNormalPriority, dumpAppId);
+                        dumpNormalPriority, dumpAppId, true /* dumpProxies */);
             } else {
                 // Take the lock here, so we get a consistent state for the entire dump;
-                // dumpEverything() will take the lock as well, but that is fine.
+                // dumpEverything() will take the lock as well, which is fine for everything
+                // except dumping proxies, which can take a long time; exclude them.
                 synchronized(this) {
                     dumpEverything(fd, pw, args, opti, dumpAll, dumpPackage, dumpClient,
-                            dumpNormalPriority, dumpAppId);
+                            dumpNormalPriority, dumpAppId, false /* dumpProxies */);
                 }
             }
         }
@@ -15007,8 +14992,8 @@
 
     @Override
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType, boolean requireForeground, boolean hideForegroundNotification,
-            String callingPackage, String callingFeatureId, int userId)
+            String resolvedType, boolean requireForeground, String callingPackage,
+            String callingFeatureId, int userId)
             throws TransactionTooLargeException {
         enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
@@ -15020,27 +15005,17 @@
             throw new IllegalArgumentException("callingPackage cannot be null");
         }
 
-        final int callingUid = Binder.getCallingUid();
-        if (requireForeground && hideForegroundNotification) {
-            if (!UserHandle.isSameApp(callingUid, mPackageVerifierUid)
-                    || !callingPackage.equals(mPackageVerifier)) {
-                throw new IllegalArgumentException(
-                        "Only the package verifier can hide its foreground service notification");
-            }
-            Slog.i(TAG, "Foreground service notification hiding requested by " + callingPackage);
-        }
-
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                 "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
         synchronized(this) {
             final int callingPid = Binder.getCallingPid();
+            final int callingUid = Binder.getCallingUid();
             final long origId = Binder.clearCallingIdentity();
             ComponentName res;
             try {
                 res = mServices.startServiceLocked(caller, service,
                         resolvedType, callingPid, callingUid,
-                        requireForeground, hideForegroundNotification,
-                        callingPackage, callingFeatureId, userId);
+                        requireForeground, callingPackage, callingFeatureId, userId);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -19480,7 +19455,7 @@
                 ComponentName res;
                 try {
                     res = mServices.startServiceLocked(null, service,
-                            resolvedType, -1, uid, fgRequired, false, callingPackage,
+                            resolvedType, -1, uid, fgRequired, callingPackage,
                             callingFeatureId, userId, allowBackgroundActivityStarts);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a512cca..149e3ba 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -654,7 +654,7 @@
         pw.println("Starting service: " + intent);
         pw.flush();
         ComponentName cn = mInterface.startService(null, intent, intent.getType(),
-                asForeground, false, SHELL_PACKAGE_NAME, null, mUserId);
+                asForeground, SHELL_PACKAGE_NAME, null, mUserId);
         if (cn == null) {
             err.println("Error: Not found; no service started.");
             return -1;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 19d5a31..1b65dba 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -104,7 +104,6 @@
     boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
     boolean delayed;        // are we waiting to start this service in the background?
     boolean fgRequired;     // is the service required to go foreground after starting?
-    boolean hideFgNotification; // Hide the fg service notification
     boolean fgWaiting;      // is a timeout for going foreground already scheduled?
     boolean isForeground;   // is service currently in foreground mode?
     int foregroundId;       // Notification ID of last foreground req.
@@ -824,9 +823,6 @@
     }
 
     public void postNotification() {
-        if (hideFgNotification) {
-            return;
-        }
         final int appUid = appInfo.uid;
         final int appPid = app.pid;
         if (foregroundId != 0 && foregroundNoti != null) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 75c2760..7bb3e36 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6490,14 +6490,17 @@
                         if (msg.obj == null) {
                             break;
                         }
-                        // If the app corresponding to this mode death handler object is not
-                        // capturing or playing audio anymore after 3 seconds, remove it
-                        // from the stack. Otherwise, check again in 3 seconds.
+                        // If no other app is currently owning the audio mode and
+                        // the app corresponding to this mode death handler object is still in the
+                        // mode owner stack but not capturing or playing audio after 3 seconds,
+                        // remove it from the stack.
+                        // Otherwise, check again in 3 seconds.
                         SetModeDeathHandler h = (SetModeDeathHandler) msg.obj;
                         if (mSetModeDeathHandlers.indexOf(h) < 0) {
                             break;
                         }
-                        if (mRecordMonitor.isRecordingActiveForUid(h.getUid())
+                        if (getModeOwnerUid() != h.getUid()
+                                || mRecordMonitor.isRecordingActiveForUid(h.getUid())
                                 || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) {
                             sendMsg(mAudioHandler,
                                     MSG_CHECK_MODE_FOR_UID,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 588b7af..7cce78b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -71,7 +71,6 @@
 import android.media.AudioManagerInternal;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -1701,7 +1700,8 @@
 
         Intent intent = new Intent(ACTION_SHOW_INPUT_METHOD_PICKER)
                 .setPackage(mContext.getPackageName());
-        mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+        mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent,
+                PendingIntent.FLAG_IMMUTABLE);
 
         mShowOngoingImeSwitcherForPhones = false;
 
@@ -2530,7 +2530,8 @@
         mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                 com.android.internal.R.string.input_method_binding_label);
         mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
-                mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
+                mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
+                PendingIntent.FLAG_IMMUTABLE));
 
         if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
             mLastBindTime = SystemClock.uptimeMillis();
@@ -3274,9 +3275,6 @@
 
     boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason) {
-        if (mCurClient == null || mCurClient.curSession == null) {
-            return false;
-        }
         if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
                 && (mShowExplicitlyRequested || mShowForced)) {
             if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
@@ -3461,9 +3459,7 @@
         // pre-rendering not supported on low-ram devices.
         cs.shouldPreRenderIme = DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() && !mIsLowRam;
 
-        final boolean sameWindowFocused = mCurFocusedWindow == windowToken;
-        final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
-        if (sameWindowFocused && isTextEditor) {
+        if (mCurFocusedWindow == windowToken) {
             if (DEBUG) {
                 Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
                         + " attribute=" + attribute + ", token = " + windowToken
@@ -3478,7 +3474,6 @@
                     InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
                     null, null, null, -1, null);
         }
-
         mCurFocusedWindow = windowToken;
         mCurFocusedWindowSoftInputMode = softInputMode;
         mCurFocusedWindowClient = cs;
@@ -3496,6 +3491,7 @@
                         == LayoutParams.SOFT_INPUT_ADJUST_RESIZE
                 || mRes.getConfiguration().isLayoutSizeAtLeast(
                         Configuration.SCREENLAYOUT_SIZE_LARGE);
+        final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
 
         // We want to start input before showing the IME, but after closing
         // it.  We want to do this after closing it to help the IME disappear
@@ -3555,11 +3551,9 @@
                 }
                 break;
             case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
-                if (!sameWindowFocused) {
-                    if (DEBUG) Slog.v(TAG, "Window asks to hide input");
-                    hideCurrentInputLocked(mCurFocusedWindow, 0, null,
-                            SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
-                }
+                if (DEBUG) Slog.v(TAG, "Window asks to hide input");
+                hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+                        SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
                 break;
             case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
                 if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
@@ -3584,15 +3578,13 @@
                 if (DEBUG) Slog.v(TAG, "Window asks to always show input");
                 if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
                         unverifiedTargetSdkVersion, startInputFlags)) {
-                    if (!sameWindowFocused) {
-                        if (attribute != null) {
-                            res = startInputUncheckedLocked(cs, inputContext, missingMethods,
-                                    attribute, startInputFlags, startInputReason);
-                            didStart = true;
-                        }
-                        showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
-                                SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE);
+                    if (attribute != null) {
+                        res = startInputUncheckedLocked(cs, inputContext, missingMethods,
+                                attribute, startInputFlags, startInputReason);
+                        didStart = true;
                     }
+                    showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+                            SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE);
                 } else {
                     Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because"
                             + " there is no focused view that also returns true from"
@@ -3603,13 +3595,7 @@
 
         if (!didStart) {
             if (attribute != null) {
-                if (sameWindowFocused) {
-                    hideCurrentInputLocked(mCurFocusedWindow, 0, null,
-                            SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
-                    res = new InputBindResult(
-                            InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
-                            null, null, null, -1, null);
-                } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
+                if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
                         || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
                     res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
                             startInputFlags, startInputReason);
@@ -3623,10 +3609,6 @@
         return res;
     }
 
-    private boolean isImeVisible() {
-        return (mImeWindowVis & InputMethodService.IME_VISIBLE) != 0;
-    }
-
     private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
         // TODO(yukawa): multi-display support.
         final int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index 9dae1b4..6e655ea 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -259,7 +259,7 @@
             return "";
         }
         return String.join(COMPONENT_NAME_USER_ID_DELIM,
-                mComponentName.toString(),
+                mComponentName.flattenToString(),
                 String.valueOf(mUserId),
                 String.valueOf(mComponentType));
     }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index d6557f6..b3eb531 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -96,7 +96,9 @@
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
 import static com.android.internal.util.XmlUtils.readStringAttribute;
+import static com.android.internal.util.XmlUtils.readThisIntArrayXml;
 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntArrayXml;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -229,6 +231,7 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.StatLogger;
+import com.android.internal.util.XmlUtils;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -239,6 +242,7 @@
 import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
@@ -313,7 +317,8 @@
     private static final int VERSION_ADDED_NETWORK_ID = 9;
     private static final int VERSION_SWITCH_UID = 10;
     private static final int VERSION_ADDED_CYCLE = 11;
-    private static final int VERSION_LATEST = VERSION_ADDED_CYCLE;
+    private static final int VERSION_ADDED_NETWORK_TYPES = 12;
+    private static final int VERSION_LATEST = VERSION_ADDED_NETWORK_TYPES;
 
     @VisibleForTesting
     public static final int TYPE_WARNING = SystemMessage.NOTE_NET_WARNING;
@@ -332,6 +337,7 @@
     private static final String TAG_WHITELIST = "whitelist";
     private static final String TAG_RESTRICT_BACKGROUND = "restrict-background";
     private static final String TAG_REVOKED_RESTRICT_BACKGROUND = "revoked-restrict-background";
+    private static final String TAG_XML_UTILS_INT_ARRAY = "int-array";
 
     private static final String ATTR_VERSION = "version";
     private static final String ATTR_RESTRICT_BACKGROUND = "restrictBackground";
@@ -360,6 +366,8 @@
     private static final String ATTR_USAGE_BYTES = "usageBytes";
     private static final String ATTR_USAGE_TIME = "usageTime";
     private static final String ATTR_OWNER_PACKAGE = "ownerPackage";
+    private static final String ATTR_NETWORK_TYPES = "networkTypes";
+    private static final String ATTR_XML_UTILS_NAME = "name";
 
     private static final String ACTION_ALLOW_BACKGROUND =
             "com.android.server.net.action.ALLOW_BACKGROUND";
@@ -2311,13 +2319,25 @@
                         }
 
                         final int subId = readIntAttribute(in, ATTR_SUB_ID);
+                        final String ownerPackage = readStringAttribute(in, ATTR_OWNER_PACKAGE);
+
+                        if (version >= VERSION_ADDED_NETWORK_TYPES) {
+                            final int depth = in.getDepth();
+                            while (XmlUtils.nextElementWithin(in, depth)) {
+                                if (TAG_XML_UTILS_INT_ARRAY.equals(in.getName())
+                                        && ATTR_NETWORK_TYPES.equals(
+                                                readStringAttribute(in, ATTR_XML_UTILS_NAME))) {
+                                    final int[] networkTypes =
+                                            readThisIntArrayXml(in, TAG_XML_UTILS_INT_ARRAY, null);
+                                    builder.setNetworkTypes(networkTypes);
+                                }
+                            }
+                        }
+
                         final SubscriptionPlan plan = builder.build();
                         mSubscriptionPlans.put(subId, ArrayUtils.appendElement(
                                 SubscriptionPlan.class, mSubscriptionPlans.get(subId), plan));
-
-                        final String ownerPackage = readStringAttribute(in, ATTR_OWNER_PACKAGE);
                         mSubscriptionPlansOwner.put(subId, ownerPackage);
-
                     } else if (TAG_UID_POLICY.equals(tag)) {
                         final int uid = readIntAttribute(in, ATTR_UID);
                         final int policy = readIntAttribute(in, ATTR_POLICY);
@@ -2513,6 +2533,9 @@
                     writeIntAttribute(out, ATTR_LIMIT_BEHAVIOR, plan.getDataLimitBehavior());
                     writeLongAttribute(out, ATTR_USAGE_BYTES, plan.getDataUsageBytes());
                     writeLongAttribute(out, ATTR_USAGE_TIME, plan.getDataUsageTime());
+                    try {
+                        writeIntArrayXml(plan.getNetworkTypes(), ATTR_NETWORK_TYPES, out);
+                    } catch (XmlPullParserException ignored) { }
                     out.endTag(null, TAG_SUBSCRIPTION_PLAN);
                 }
             }
@@ -3310,7 +3333,8 @@
             // let in core system components (like the Settings app).
             final String ownerPackage = mSubscriptionPlansOwner.get(subId);
             if (Objects.equals(ownerPackage, callingPackage)
-                    || (UserHandle.getCallingAppId() == android.os.Process.SYSTEM_UID)) {
+                    || (UserHandle.getCallingAppId() == android.os.Process.SYSTEM_UID)
+                    || (UserHandle.getCallingAppId() == android.os.Process.PHONE_UID)) {
                 return mSubscriptionPlans.get(subId);
             } else {
                 Log.w(TAG, "Not returning plans because caller " + callingPackage
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index daca0b34..6246b28 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -280,6 +280,7 @@
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
 import android.permission.IPermissionManager;
+import android.provider.ContactsContract;
 import android.provider.DeviceConfig;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
@@ -25426,6 +25427,32 @@
         }
     }
 
+    @Override
+    public void grantImplicitAccess(int recipientUid, String visibleAuthority) {
+        // This API is exposed temporarily to only the contacts provider. (b/158688602)
+        final int callingUid = Binder.getCallingUid();
+        ProviderInfo contactsProvider = resolveContentProviderInternal(
+                        ContactsContract.AUTHORITY, 0, UserHandle.USER_SYSTEM);
+        if (contactsProvider == null || contactsProvider.applicationInfo == null
+                || !UserHandle.isSameApp(contactsProvider.applicationInfo.uid, callingUid)) {
+            throw new SecurityException(callingUid + " is not allow to call grantImplicitAccess");
+        }
+        final int userId = UserHandle.getUserId(recipientUid);
+        final long token = Binder.clearCallingIdentity();
+        final ProviderInfo providerInfo;
+        try {
+            providerInfo = resolveContentProvider(visibleAuthority, 0 /*flags*/, userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        if (providerInfo == null) {
+            return;
+        }
+        int visibleUid = providerInfo.applicationInfo.uid;
+        mPmInternal.grantImplicitAccess(userId, null /*Intent*/, UserHandle.getAppId(recipientUid),
+                visibleUid, false);
+    }
+
     boolean canHaveOatDir(String packageName) {
         synchronized (mLock) {
             AndroidPackage p = mPackages.get(packageName);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3faf3be..3c42e93 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -69,6 +69,7 @@
 import static android.view.WindowManagerGlobal.ADD_OKAY;
 import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
 
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -148,6 +149,7 @@
 import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
@@ -1379,12 +1381,14 @@
     }
 
     private long getScreenshotChordLongPressDelay() {
+        long delayMs = DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY,
+                ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
         if (mKeyguardDelegate.isShowing()) {
             // Double the time it takes to take a screenshot from the keyguard
-            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
-                    ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
+            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * delayMs);
         }
-        return ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout();
+        return delayMs;
     }
 
     private long getRingerToggleChordDelay() {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 959aedc..6d452c3 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1700,8 +1700,9 @@
         // If the most recent activity was noHistory but was only stopped rather
         // than stopped+finished because the device went to sleep, we need to make
         // sure to finish it as we're making a new activity topmost.
-        if (shouldSleepActivities() && mLastNoHistoryActivity != null &&
-                !mLastNoHistoryActivity.finishing) {
+        if (shouldSleepActivities() && mLastNoHistoryActivity != null
+                && !mLastNoHistoryActivity.finishing
+                && mLastNoHistoryActivity != next) {
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "no-history finish of " + mLastNoHistoryActivity + " on new resume");
             mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 3dd82a6..7e6b7cd 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -732,6 +732,11 @@
         final ActivityStack stack = task.getStack();
 
         beginDeferResume();
+        // The LaunchActivityItem also contains process configuration, so the configuration change
+        // from WindowProcessController#setProcess can be deferred. The major reason is that if
+        // the activity has FixedRotationAdjustments, it needs to be applied with configuration.
+        // In general, this reduces a binder transaction if process configuration is changed.
+        proc.pauseConfigurationDispatch();
 
         try {
             r.startFreezingScreenLocked(proc, 0);
@@ -826,9 +831,9 @@
                 // Because we could be starting an Activity in the system process this may not go
                 // across a Binder interface which would create a new Configuration. Consequently
                 // we have to always create a new Configuration here.
-
+                final Configuration procConfig = proc.prepareConfigurationForLaunchingActivity();
                 final MergedConfiguration mergedConfiguration = new MergedConfiguration(
-                        proc.getConfiguration(), r.getMergedOverrideConfiguration());
+                        procConfig, r.getMergedOverrideConfiguration());
                 r.setLastReportedConfiguration(mergedConfiguration);
 
                 logIfTransactionTooLarge(r.intent, r.getSavedState());
@@ -862,6 +867,11 @@
                 // Schedule transaction.
                 mService.getLifecycleManager().scheduleTransaction(clientTransaction);
 
+                if (procConfig.seq > mRootWindowContainer.getConfiguration().seq) {
+                    // If the seq is increased, there should be something changed (e.g. registered
+                    // activity configuration).
+                    proc.setLastReportedConfiguration(procConfig);
+                }
                 if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
                         && mService.mHasHeavyWeightFeature) {
                     // This may be a heavy-weight process! Note that the package manager will ensure
@@ -896,6 +906,7 @@
             }
         } finally {
             endDeferResume();
+            proc.resumeConfigurationDispatch();
         }
 
         r.launchFailed = false;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a78232c..7af237b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1481,9 +1481,10 @@
             // anyone interested in this piece of information.
             final ActivityStack homeStack = targetTask.getDisplayArea().getRootHomeTask();
             final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null);
+            final ActivityRecord top = targetTask.getTopNonFinishingActivity();
+            final boolean visible = top != null && top.isVisible();
             mService.getTaskChangeNotificationController().notifyActivityRestartAttempt(
-                    targetTask.getTaskInfo(), homeTaskVisible, clearedTask,
-                    targetTask.getTopNonFinishingActivity().isVisible());
+                    targetTask.getTaskInfo(), homeTaskVisible, clearedTask, visible);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 4a40fc6..c691a0e 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -3400,9 +3400,12 @@
                             ? Type.navigationBars() : 0)
                     | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
                             ? Type.statusBars() : 0)
-                    | (requestedState.getSourceOrDefaultVisibility(ITYPE_EXTRA_NAVIGATION_BAR)
+                    | (mExtraNavBarAlt != null
+                            && requestedState.getSourceOrDefaultVisibility(
+                                    ITYPE_EXTRA_NAVIGATION_BAR)
                             ? Type.navigationBars() : 0)
-                    | (requestedState.getSourceOrDefaultVisibility(ITYPE_CLIMATE_BAR)
+                    | (mClimateBarAlt != null
+                            && requestedState.getSourceOrDefaultVisibility(ITYPE_CLIMATE_BAR)
                             ? Type.statusBars() : 0);
 
             if (swipeTarget == mNavigationBar
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0216db4..3663ee1 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -219,6 +219,11 @@
 
     WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name,
             InputEventReceiver.Factory inputEventReceiverFactory) {
+        if (!name.contentEquals(INPUT_CONSUMER_NAVIGATION)) {
+            throw new IllegalArgumentException("Illegal input consumer : " + name
+                    + ", display: " + mDisplayId);
+        }
+
         if (mInputConsumers.containsKey(name)) {
             throw new IllegalStateException("Existing input consumer found with name: " + name
                     + ", display: " + mDisplayId);
@@ -248,6 +253,11 @@
                 // stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through
                 consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL;
                 break;
+            case INPUT_CONSUMER_RECENTS_ANIMATION:
+                break;
+            default:
+                throw new IllegalArgumentException("Illegal input consumer : " + name
+                        + ", display: " + mDisplayId);
         }
         addInputConsumer(name, consumer);
     }
@@ -459,9 +469,6 @@
             mDisplayContent.forAllWindows(this,
                     true /* traverseTopToBottom */);
 
-            if (mAddWallpaperInputConsumerHandle) {
-                mWallpaperInputConsumer.show(mInputTransaction, 0);
-            }
             if (!mUpdateInputWindowsImmediately) {
                 mDisplayContent.getPendingTransaction().merge(mInputTransaction);
                 mDisplayContent.scheduleAnimation();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a65eca8..72cd32f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -48,6 +48,7 @@
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
@@ -1931,24 +1932,29 @@
     }
 
     boolean attachApplication(WindowProcessController app) throws RemoteException {
-        final String processName = app.mName;
         boolean didSomething = false;
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
-            final DisplayContent display = getChildAt(displayNdx);
-            final ActivityStack stack = display.getFocusedStack();
-            if (stack == null) {
-                continue;
-            }
-
             mTmpRemoteException = null;
             mTmpBoolean = false; // Set to true if an activity was started.
-            final PooledFunction c = PooledLambda.obtainFunction(
-                    RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,
-                    PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivity());
-            stack.forAllActivities(c);
-            c.recycle();
-            if (mTmpRemoteException != null) {
-                throw mTmpRemoteException;
+
+            final DisplayContent display = getChildAt(displayNdx);
+            for (int areaNdx = display.getTaskDisplayAreaCount() - 1; areaNdx >= 0; --areaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(areaNdx);
+                for (int taskNdx = taskDisplayArea.getStackCount() - 1; taskNdx >= 0; --taskNdx) {
+                    final ActivityStack rootTask = taskDisplayArea.getStackAt(taskNdx);
+                    if (rootTask.getVisibility(null /*starting*/) == STACK_VISIBILITY_INVISIBLE) {
+                        break;
+                    }
+                    final PooledFunction c = PooledLambda.obtainFunction(
+                            RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,
+                            PooledLambda.__(ActivityRecord.class), app,
+                            rootTask.topRunningActivity());
+                    rootTask.forAllActivities(c);
+                    c.recycle();
+                    if (mTmpRemoteException != null) {
+                        throw mTmpRemoteException;
+                    }
+                }
             }
             didSomething |= mTmpBoolean;
         }
@@ -1966,8 +1972,8 @@
         }
 
         try {
-            if (mStackSupervisor.realStartActivityLocked(r, app, top == r /*andResume*/,
-                    true /*checkConfig*/)) {
+            if (mStackSupervisor.realStartActivityLocked(r, app,
+                    top == r && r.isFocusable() /*andResume*/, true /*checkConfig*/)) {
                 mTmpBoolean = true;
             }
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3a750f2..db3c74f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3671,6 +3671,10 @@
             return STACK_VISIBILITY_INVISIBLE;
         }
 
+        if (isTopActivityLaunchedBehind()) {
+            return STACK_VISIBILITY_VISIBLE;
+        }
+
         boolean gotSplitScreenStack = false;
         boolean gotOpaqueSplitScreenPrimary = false;
         boolean gotOpaqueSplitScreenSecondary = false;
@@ -3788,6 +3792,14 @@
                 : STACK_VISIBILITY_VISIBLE;
     }
 
+    private boolean isTopActivityLaunchedBehind() {
+        final ActivityRecord top = topRunningActivity();
+        if (top != null && top.mLaunchTaskBehind) {
+            return true;
+        }
+        return false;
+    }
+
     ActivityRecord isInTask(ActivityRecord r) {
         if (r == null) {
             return null;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f0f8c75..03834c34 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.INPUT_CONSUMER;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.MANAGE_APP_TOKENS;
@@ -5874,6 +5875,11 @@
     @Override
     public void createInputConsumer(IBinder token, String name, int displayId,
             InputChannel inputChannel) {
+        if (!mAtmInternal.isCallerRecents(Binder.getCallingUid())
+                && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) {
+            throw new SecurityException("createInputConsumer requires INPUT_CONSUMER permission");
+        }
+
         synchronized (mGlobalLock) {
             DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display != null) {
@@ -5885,6 +5891,11 @@
 
     @Override
     public boolean destroyInputConsumer(String name, int displayId) {
+        if (!mAtmInternal.isCallerRecents(Binder.getCallingUid())
+                && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) {
+            throw new SecurityException("destroyInputConsumer requires INPUT_CONSUMER permission");
+        }
+
         synchronized (mGlobalLock) {
             DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display != null) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index bd959ab..df49ac7 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -186,13 +186,16 @@
 
     // Last configuration that was reported to the process.
     private final Configuration mLastReportedConfiguration = new Configuration();
-    // Configuration that is waiting to be dispatched to the process.
-    private Configuration mPendingConfiguration;
+    /** Whether the process configuration is waiting to be dispatched to the process. */
+    private boolean mHasPendingConfigurationChange;
     // Registered display id as a listener to override config change
     private int mDisplayId;
     private ActivityRecord mConfigActivityRecord;
     // Whether the activity config override is allowed for this process.
     private volatile boolean mIsActivityConfigOverrideAllowed = true;
+    /** Non-zero to pause dispatching process configuration change. */
+    private int mPauseConfigurationDispatchCount;
+
     /**
      * Activities that hosts some UI drawn by the current process. The activities live
      * in another process. This is used to check if the process is currently showing anything
@@ -1115,8 +1118,10 @@
         onMergedOverrideConfigurationChanged(Configuration.EMPTY);
     }
 
-    private void registerActivityConfigurationListener(ActivityRecord activityRecord) {
-        if (activityRecord == null || activityRecord.containsListener(this)) {
+    void registerActivityConfigurationListener(ActivityRecord activityRecord) {
+        if (activityRecord == null || activityRecord.containsListener(this)
+                // Check for the caller from outside of this class.
+                || !mIsActivityConfigOverrideAllowed) {
             return;
         }
         // A process can only register to one activityRecord to listen to the override configuration
@@ -1168,25 +1173,25 @@
 
     @Override
     public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
-        super.onRequestedOverrideConfigurationChanged(
-                sanitizeProcessConfiguration(overrideConfiguration));
+        super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
     }
 
     @Override
     public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
-        super.onRequestedOverrideConfigurationChanged(
-                sanitizeProcessConfiguration(mergedOverrideConfig));
+        super.onRequestedOverrideConfigurationChanged(mergedOverrideConfig);
     }
 
-    private static Configuration sanitizeProcessConfiguration(Configuration config) {
+    @Override
+    void resolveOverrideConfiguration(Configuration newParentConfig) {
+        super.resolveOverrideConfiguration(newParentConfig);
+        final Configuration resolvedConfig = getResolvedOverrideConfiguration();
         // Make sure that we don't accidentally override the activity type.
-        if (config.windowConfiguration.getActivityType() != ACTIVITY_TYPE_UNDEFINED) {
-            final Configuration sanitizedConfig = new Configuration(config);
-            sanitizedConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
-            return sanitizedConfig;
-        }
-
-        return config;
+        resolvedConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+        // Activity has an independent ActivityRecord#mConfigurationSeq. If this process registers
+        // activity configuration, its config seq shouldn't go backwards by activity configuration.
+        // Otherwise if other places send wpc.getConfiguration() to client, the configuration may
+        // be ignored due to the seq is older.
+        resolvedConfig.seq = newParentConfig.seq;
     }
 
     private void updateConfiguration() {
@@ -1204,11 +1209,7 @@
         if (mListener.isCached()) {
             // This process is in a cached state. We will delay delivering the config change to the
             // process until the process is no longer cached.
-            if (mPendingConfiguration == null) {
-                mPendingConfiguration = new Configuration(config);
-            } else {
-                mPendingConfiguration.setTo(config);
-            }
+            mHasPendingConfigurationChange = true;
             return;
         }
 
@@ -1216,6 +1217,11 @@
     }
 
     private void dispatchConfigurationChange(Configuration config) {
+        if (mPauseConfigurationDispatchCount > 0) {
+            mHasPendingConfigurationChange = true;
+            return;
+        }
+        mHasPendingConfigurationChange = false;
         if (mThread == null) {
             if (Build.IS_DEBUGGABLE && mHasImeService) {
                 // TODO (b/135719017): Temporary log for debugging IME service.
@@ -1242,7 +1248,7 @@
         }
     }
 
-    private void setLastReportedConfiguration(Configuration config) {
+    void setLastReportedConfiguration(Configuration config) {
         mLastReportedConfiguration.setTo(config);
     }
 
@@ -1250,6 +1256,30 @@
         return mLastReportedConfiguration;
     }
 
+    void pauseConfigurationDispatch() {
+        mPauseConfigurationDispatchCount++;
+    }
+
+    void resumeConfigurationDispatch() {
+        mPauseConfigurationDispatchCount--;
+    }
+
+    /**
+     * This is called for sending {@link android.app.servertransaction.LaunchActivityItem}.
+     * The caller must call {@link #setLastReportedConfiguration} if the delivered configuration
+     * is newer.
+     */
+    Configuration prepareConfigurationForLaunchingActivity() {
+        final Configuration config = getConfiguration();
+        if (mHasPendingConfigurationChange) {
+            mHasPendingConfigurationChange = false;
+            // The global configuration may not change, so the client process may have the same
+            // config seq. This increment ensures that the client won't ignore the configuration.
+            config.seq = mAtm.increaseConfigurationSeqLocked();
+        }
+        return config;
+    }
+
     /** Returns the total time (in milliseconds) spent executing in both user and system code. */
     public long getCpuTime() {
         return (mListener != null) ? mListener.getCpuTime() : 0;
@@ -1341,10 +1371,8 @@
     public void onProcCachedStateChanged(boolean isCached) {
         if (!isCached) {
             synchronized (mAtm.mGlobalLockWithoutBoost) {
-                if (mPendingConfiguration != null) {
-                    final Configuration config = mPendingConfiguration;
-                    mPendingConfiguration = null;
-                    dispatchConfigurationChange(config);
+                if (mHasPendingConfigurationChange) {
+                    dispatchConfigurationChange(getConfiguration());
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 1716dcd..d86f6c9 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -172,6 +172,12 @@
                 }
             }
         }
+
+        /** The state may not only be used by self. Make sure to leave the influence by others. */
+        void disassociate(WindowToken token) {
+            mAssociatedTokens.remove(token);
+            mRotatedContainers.remove(token);
+        }
     }
 
     private class DeathRecipient implements IBinder.DeathRecipient {
@@ -531,7 +537,7 @@
     void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames,
             Configuration config) {
         if (mFixedRotationTransformState != null) {
-            cleanUpFixedRotationTransformState(true /* replacing */);
+            mFixedRotationTransformState.disassociate(this);
         }
         mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
                 new Configuration(config), mDisplayContent.getRotation());
@@ -539,8 +545,7 @@
         mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames,
                 mFixedRotationTransformState.mInsetsState,
                 mFixedRotationTransformState.mBarContentFrames);
-        onConfigurationChanged(getParent().getConfiguration());
-        notifyFixedRotationTransform(true /* enabled */);
+        onFixedRotationStatePrepared();
     }
 
     /**
@@ -553,12 +558,29 @@
             return;
         }
         if (mFixedRotationTransformState != null) {
-            cleanUpFixedRotationTransformState(true /* replacing */);
+            mFixedRotationTransformState.disassociate(this);
         }
         mFixedRotationTransformState = fixedRotationState;
         fixedRotationState.mAssociatedTokens.add(this);
-        onConfigurationChanged(getParent().getConfiguration());
+        onFixedRotationStatePrepared();
+    }
+
+    /**
+     * Makes the rotated states take effect for this window container and its client process.
+     * This should only be called when {@link #mFixedRotationTransformState} is non-null.
+     */
+    private void onFixedRotationStatePrepared() {
+        // Send the adjustment info first so when the client receives configuration change, it can
+        // get the rotated display metrics.
         notifyFixedRotationTransform(true /* enabled */);
+        // Resolve the rotated configuration.
+        onConfigurationChanged(getParent().getConfiguration());
+        final ActivityRecord r = asActivityRecord();
+        if (r != null && r.hasProcess()) {
+            // The application needs to be configured as in a rotated environment for compatibility.
+            // This registration will send the rotated configuration to its process.
+            r.app.registerActivityConfigurationListener(r);
+        }
     }
 
     /**
@@ -609,21 +631,12 @@
         // The state is cleared at the end, because it is used to indicate that other windows can
         // use seamless rotation when applying rotation to display.
         for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) {
-            state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState(
-                    false /* replacing */);
+            final WindowToken token = state.mAssociatedTokens.get(i);
+            token.mFixedRotationTransformState = null;
+            token.notifyFixedRotationTransform(false /* enabled */);
         }
     }
 
-    private void cleanUpFixedRotationTransformState(boolean replacing) {
-        if (replacing && mFixedRotationTransformState.mAssociatedTokens.size() > 1) {
-            // The state is not only used by self. Make sure to leave the influence by others.
-            mFixedRotationTransformState.mAssociatedTokens.remove(this);
-            mFixedRotationTransformState.mRotatedContainers.remove(this);
-        }
-        mFixedRotationTransformState = null;
-        notifyFixedRotationTransform(false /* enabled */);
-    }
-
     /** Notifies application side to enable or disable the rotation adjustment of display info. */
     private void notifyFixedRotationTransform(boolean enabled) {
         FixedRotationAdjustments adjustments = null;
@@ -687,8 +700,9 @@
         if (!isFixedRotationTransforming()) {
             return null;
         }
-        return new FixedRotationAdjustments(mFixedRotationTransformState.mDisplayInfo.rotation,
-                mFixedRotationTransformState.mDisplayInfo.displayCutout);
+        final DisplayInfo displayInfo = mFixedRotationTransformState.mDisplayInfo;
+        return new FixedRotationAdjustments(displayInfo.rotation, displayInfo.appWidth,
+                displayInfo.appHeight, displayInfo.displayCutout);
     }
 
     @Override
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index a5f0d04..e355a20 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -306,6 +306,7 @@
     }
     mJobCondition.notify_all();
     mJobProcessor.join();
+    mLooper->wake();
     mCmdLooperThread.join();
     mTimedQueue->stop();
     // Ensure that mounts are destroyed while the service is still valid.
@@ -1378,7 +1379,7 @@
 }
 
 void IncrementalService::runCmdLooper() {
-    constexpr auto kTimeoutMsecs = 1000;
+    constexpr auto kTimeoutMsecs = -1;
     while (mRunning.load(std::memory_order_relaxed)) {
         mLooper->pollAll(kTimeoutMsecs);
     }
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 236ac84..9e6cf84 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -86,8 +86,8 @@
             return;
         }
         List<ShareTarget> shareTargets = getDirectShareTargets();
-        SharesheetModelScorer.computeScore(shareTargets, getShareEventType(mIntentFilter),
-                System.currentTimeMillis());
+        SharesheetModelScorer.computeScoreForDirectShare(shareTargets,
+                getShareEventType(mIntentFilter), System.currentTimeMillis());
         Collections.sort(shareTargets,
                 Comparator.comparing(ShareTarget::getScore, reverseOrder())
                         .thenComparing(t -> t.getAppTarget().getRank()));
diff --git a/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java
index c77843c..d4a502d 100644
--- a/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java
+++ b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.usage.UsageEvents;
+import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.Pair;
 import android.util.Range;
@@ -27,12 +28,14 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ChooserActivity;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.server.people.data.AppUsageStatsData;
 import com.android.server.people.data.DataManager;
 import com.android.server.people.data.Event;
 
 import java.time.Duration;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
@@ -46,6 +49,7 @@
     private static final String TAG = "SharesheetModelScorer";
     private static final boolean DEBUG = false;
     private static final Integer RECENCY_SCORE_COUNT = 6;
+    private static final Integer NATIVE_RANK_COUNT = 2;
     private static final float RECENCY_INITIAL_BASE_SCORE = 0.4F;
     private static final float RECENCY_SCORE_INITIAL_DECAY = 0.05F;
     private static final float RECENCY_SCORE_SUBSEQUENT_DECAY = 0.02F;
@@ -174,6 +178,77 @@
         postProcess(shareTargets, targetsLimit, dataManager, callingUserId);
     }
 
+    /**
+     * Computes ranking score for direct sharing. Update
+     * {@link ShareTargetPredictor.ShareTargetScore}.
+     */
+    static void computeScoreForDirectShare(List<ShareTargetPredictor.ShareTarget> shareTargets,
+            int shareEventType, long now) {
+        computeScore(shareTargets, shareEventType, now);
+        promoteTopNativeRankedShortcuts(shareTargets);
+    }
+
+    /**
+     * Promotes top (NATIVE_RANK_COUNT) shortcuts for each package and class, as per shortcut native
+     * ranking provided by apps.
+     */
+    private static void promoteTopNativeRankedShortcuts(
+            List<ShareTargetPredictor.ShareTarget> shareTargets) {
+        float topShortcutBonus = DeviceConfig.getFloat(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+                0f);
+        float secondTopShortcutBonus = DeviceConfig.getFloat(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+                0f);
+        // Populates a map which key is a packageName and className pair, value is a max heap
+        // containing top (NATIVE_RANK_COUNT) shortcuts as per shortcut native ranking provided
+        // by apps.
+        Map<Pair<String, String>, PriorityQueue<ShareTargetPredictor.ShareTarget>>
+                topNativeRankedShareTargetMap = new ArrayMap<>();
+        for (ShareTargetPredictor.ShareTarget shareTarget : shareTargets) {
+            Pair<String, String> key = new Pair<>(shareTarget.getAppTarget().getPackageName(),
+                    shareTarget.getAppTarget().getClassName());
+            if (!topNativeRankedShareTargetMap.containsKey(key)) {
+                topNativeRankedShareTargetMap.put(key,
+                        new PriorityQueue<>(NATIVE_RANK_COUNT,
+                                Collections.reverseOrder(Comparator.comparingInt(
+                                        p -> p.getAppTarget().getRank()))));
+            }
+            PriorityQueue<ShareTargetPredictor.ShareTarget> rankMaxHeap =
+                    topNativeRankedShareTargetMap.get(key);
+            if (rankMaxHeap.isEmpty() || shareTarget.getAppTarget().getRank()
+                    < rankMaxHeap.peek().getAppTarget().getRank()) {
+                if (rankMaxHeap.size() == NATIVE_RANK_COUNT) {
+                    rankMaxHeap.poll();
+                }
+                rankMaxHeap.offer(shareTarget);
+            }
+        }
+        for (PriorityQueue<ShareTargetPredictor.ShareTarget> maxHeap :
+                topNativeRankedShareTargetMap.values()) {
+            while (!maxHeap.isEmpty()) {
+                ShareTargetPredictor.ShareTarget target = maxHeap.poll();
+                float bonus = maxHeap.isEmpty() ? topShortcutBonus : secondTopShortcutBonus;
+                target.setScore(probOR(target.getScore(), bonus));
+
+                if (DEBUG) {
+                    Slog.d(TAG, String.format(
+                            "SharesheetModel: promote top shortcut as per native ranking,"
+                                    + "packageName: %s, className: %s, shortcutId: %s, bonus:%.2f,"
+                                    + "total:%.2f",
+                            target.getAppTarget().getPackageName(),
+                            target.getAppTarget().getClassName(),
+                            target.getAppTarget().getShortcutInfo() != null
+                                    ? target.getAppTarget().getShortcutInfo().getId() : null,
+                            bonus,
+                            target.getScore()));
+                }
+            }
+        }
+    }
+
     private static void postProcess(List<ShareTargetPredictor.ShareTarget> shareTargets,
             int targetsLimit, @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
         // Populates a map which key is package name and value is list of shareTargets descended
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java
index 45fff48..605878d 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java
@@ -20,6 +20,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anySet;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -28,9 +29,13 @@
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetId;
 import android.app.usage.UsageEvents;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.util.Range;
 
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.server.people.data.AppUsageStatsData;
 import com.android.server.people.data.DataManager;
 import com.android.server.people.data.Event;
@@ -121,6 +126,13 @@
     private ShareTargetPredictor.ShareTarget mShareTarget5;
     private ShareTargetPredictor.ShareTarget mShareTarget6;
 
+    private ShareTargetPredictor.ShareTarget mShareShortcutTarget1;
+    private ShareTargetPredictor.ShareTarget mShareShortcutTarget2;
+    private ShareTargetPredictor.ShareTarget mShareShortcutTarget3;
+    private ShareTargetPredictor.ShareTarget mShareShortcutTarget4;
+    private ShareTargetPredictor.ShareTarget mShareShortcutTarget5;
+    private ShareTargetPredictor.ShareTarget mShareShortcutTarget6;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -154,6 +166,46 @@
                         new AppTargetId("cls2#pkg3"), PACKAGE_3, UserHandle.of(USER_ID))
                         .setClassName(CLASS_2).build(),
                 null, null);
+
+        mShareShortcutTarget1 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls1#pkg1#1"), buildShortcutInfo(PACKAGE_1, 0, "1"))
+                        .setClassName(CLASS_1).setRank(2).build(),
+                mEventHistory1, null);
+        mShareShortcutTarget2 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls1#pkg1#2"), buildShortcutInfo(PACKAGE_1, 0, "2"))
+                        .setClassName(CLASS_1).setRank(1).build(),
+                mEventHistory2, null);
+        mShareShortcutTarget3 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls1#pkg1#3"), buildShortcutInfo(PACKAGE_1, 0, "3"))
+                        .setClassName(CLASS_1).setRank(0).build(),
+                mEventHistory3, null);
+        mShareShortcutTarget4 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls1#pkg2#1"), buildShortcutInfo(PACKAGE_2, 0, "1"))
+                        .setClassName(CLASS_1).setRank(2).build(),
+                mEventHistory4, null);
+        mShareShortcutTarget5 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls1#pkg2#2"), buildShortcutInfo(PACKAGE_2, 0, "2"))
+                        .setClassName(CLASS_1).setRank(1).build(),
+                mEventHistory5, null);
+        mShareShortcutTarget6 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls1#pkg2#3"), buildShortcutInfo(PACKAGE_2, 0, "3"))
+                        .setClassName(CLASS_1).setRank(3).build(),
+                null, null);
+
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+                Float.toString(0f),
+                true /* makeDefault*/);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+                Float.toString(0f),
+                true /* makeDefault*/);
     }
 
     @Test
@@ -433,6 +485,101 @@
         assertEquals(0f, mShareTarget6.getScore(), DELTA);
     }
 
+    @Test
+    public void testComputeScoreForDirectShare() {
+        // Frequency and recency
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+
+        when(mEventIndex1.getActiveTimeSlots()).thenReturn(
+                List.of(WITHIN_ONE_DAY, TWO_DAYS_AGO, FIVE_DAYS_AGO));
+        when(mEventIndex2.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO));
+        when(mEventIndex3.getActiveTimeSlots()).thenReturn(List.of(FIVE_DAYS_AGO, TWENTY_DAYS_AGO));
+        when(mEventIndex4.getActiveTimeSlots()).thenReturn(
+                List.of(EIGHT_DAYS_AGO, TWELVE_DAYS_AGO, FOUR_WEEKS_AGO));
+        when(mEventIndex5.getActiveTimeSlots()).thenReturn(List.of());
+
+        when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY);
+        when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO);
+        when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO);
+        when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO);
+        when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null);
+
+        // Frequency of the same mime type
+        when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6);
+        when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7);
+        when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8);
+        when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9);
+        when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10);
+
+        when(mEventIndex6.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO));
+        when(mEventIndex7.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO));
+        when(mEventIndex8.getActiveTimeSlots()).thenReturn(List.of());
+        when(mEventIndex9.getActiveTimeSlots()).thenReturn(List.of(EIGHT_DAYS_AGO));
+        when(mEventIndex10.getActiveTimeSlots()).thenReturn(List.of());
+
+        SharesheetModelScorer.computeScore(
+                List.of(mShareShortcutTarget1, mShareShortcutTarget2, mShareShortcutTarget3,
+                        mShareShortcutTarget4, mShareShortcutTarget5, mShareShortcutTarget6),
+                Event.TYPE_SHARE_TEXT,
+                NOW);
+
+        // Verification
+        assertEquals(0.514f, mShareShortcutTarget1.getScore(), DELTA);
+        assertEquals(0.475125f, mShareShortcutTarget2.getScore(), DELTA);
+        assertEquals(0.33f, mShareShortcutTarget3.getScore(), DELTA);
+        assertEquals(0.4411f, mShareShortcutTarget4.getScore(), DELTA);
+        assertEquals(0f, mShareShortcutTarget5.getScore(), DELTA);
+        assertEquals(0f, mShareShortcutTarget6.getScore(), DELTA);
+    }
+
+    @Test
+    public void testComputeScoreForDirectShare_promoteTopNativeRankedShortcuts() {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+                Float.toString(0.4f),
+                true /* makeDefault*/);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.NON_TOP_NATIVE_RANKED_SHARING_SHORTCUTS_BOOSTER,
+                Float.toString(0.3f),
+                true /* makeDefault*/);
+
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+        when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6);
+        when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7);
+        when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8);
+        when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9);
+        when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10);
+
+        SharesheetModelScorer.computeScoreForDirectShare(
+                List.of(mShareShortcutTarget1, mShareShortcutTarget2, mShareShortcutTarget3,
+                        mShareShortcutTarget4, mShareShortcutTarget5, mShareShortcutTarget6),
+                Event.TYPE_SHARE_TEXT, 20);
+
+        assertEquals(0f, mShareShortcutTarget1.getScore(), DELTA);
+        assertEquals(0.3f, mShareShortcutTarget2.getScore(), DELTA);
+        assertEquals(0.4f, mShareShortcutTarget3.getScore(), DELTA);
+        assertEquals(0.3f, mShareShortcutTarget4.getScore(), DELTA);
+        assertEquals(0.4f, mShareShortcutTarget5.getScore(), DELTA);
+        assertEquals(0f, mShareShortcutTarget6.getScore(), DELTA);
+    }
+
+    private static ShortcutInfo buildShortcutInfo(String packageName, int userId, String id) {
+        Context mockContext = mock(Context.class);
+        when(mockContext.getPackageName()).thenReturn(packageName);
+        when(mockContext.getUserId()).thenReturn(userId);
+        when(mockContext.getUser()).thenReturn(UserHandle.of(userId));
+        ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, id).setShortLabel(id);
+        return builder.build();
+    }
+
     private static UsageEvents.Event createUsageEvent(String packageName) {
         UsageEvents.Event e = new UsageEvents.Event();
         e.mPackage = packageName;
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 4dec7a1..a07e60c 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -112,6 +112,8 @@
     private static final int UID_SYSTEM_HEADFULL = 10002;
     private static final String PACKAGE_SYSTEM_HEADLESS = "com.example.system.headless";
     private static final int UID_SYSTEM_HEADLESS = 10003;
+    private static final String PACKAGE_WELLBEING = "com.example.wellbeing";
+    private static final int UID_WELLBEING = 10004;
     private static final int USER_ID = 0;
     private static final int USER_ID2 = 10;
     private static final UserHandle USER_HANDLE_USER2 = new UserHandle(USER_ID2);
@@ -218,6 +220,11 @@
         }
 
         @Override
+        boolean isWellbeingPackage(String packageName) {
+            return PACKAGE_WELLBEING.equals(packageName);
+        }
+
+        @Override
         void updatePowerWhitelistCache() {
         }
 
@@ -329,6 +336,12 @@
         pish.packageName = PACKAGE_SYSTEM_HEADLESS;
         packages.add(pish);
 
+        PackageInfo piw = new PackageInfo();
+        piw.applicationInfo = new ApplicationInfo();
+        piw.applicationInfo.uid = UID_WELLBEING;
+        piw.packageName = PACKAGE_WELLBEING;
+        packages.add(piw);
+
         doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
         try {
             for (int i = 0; i < packages.size(); ++i) {
@@ -1516,6 +1529,25 @@
         assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
     }
 
+    @Test
+    public void testWellbeingAppElevated() {
+        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_WELLBEING);
+        assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_WELLBEING);
+        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+        assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
+        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+
+        // Make sure the default wellbeing app does not get lowered below WORKING_SET.
+        mController.setAppStandbyBucket(PACKAGE_WELLBEING, USER_ID, STANDBY_BUCKET_RARE,
+                REASON_MAIN_TIMEOUT);
+        assertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_WELLBEING);
+
+        // A non default wellbeing app should be able to fall lower than WORKING_SET.
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+                REASON_MAIN_TIMEOUT);
+        assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
+    }
+
     private String getAdminAppsStr(int userId) {
         return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index b100c84..4538300 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.Manifest;
 import android.app.AlarmManager;
 import android.app.IUiModeManager;
 import android.content.BroadcastReceiver;
@@ -24,6 +25,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -66,6 +68,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -227,6 +230,15 @@
     }
 
     @Test
+    public void setNightModeActivated_permissiontoChangeOtherUsers() throws RemoteException {
+        mUiManagerService.onSwitchUser(9);
+        when(mContext.checkCallingOrSelfPermission(
+                eq(Manifest.permission.INTERACT_ACROSS_USERS)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        assertFalse(mService.setNightModeActivated(true));
+    }
+
+    @Test
     public void autoNightModeSwitch_batterySaverOn() throws RemoteException {
         mService.setNightMode(MODE_NIGHT_NO);
         when(mTwilightState.isNight()).thenReturn(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 02a3bb1..a37f4be 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -548,7 +548,7 @@
         final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
         try {
             doReturn(false).when(stack).isTranslucent(any());
-            assertFalse(mStack.shouldBeVisible(null /* starting */));
+            assertTrue(mStack.shouldBeVisible(null /* starting */));
 
             mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
                     mActivity.getConfiguration()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 6a84c13..c7a8bd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1149,8 +1149,10 @@
         verify(t, never()).setPosition(any(), eq(0), eq(0));
 
         // Launch another activity before the transition is finished.
-        final ActivityRecord app2 = new ActivityTestsBase.StackBuilder(mWm.mRoot)
-                .setDisplay(mDisplayContent).build().getTopMostActivity();
+        final ActivityStack stack2 = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+                .setDisplay(mDisplayContent).build();
+        final ActivityRecord app2 = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+                .setStack(stack2).setUseProcess(app.app).build();
         app2.setVisible(false);
         mDisplayContent.mOpeningApps.add(app2);
         app2.setRequestedOrientation(newOrientation);
@@ -1160,6 +1162,12 @@
         assertTrue(app.hasFixedRotationTransform(app2));
         assertTrue(mDisplayContent.isFixedRotationLaunchingApp(app2));
 
+        final Configuration expectedProcConfig = new Configuration(app2.app.getConfiguration());
+        expectedProcConfig.windowConfiguration.setActivityType(
+                WindowConfiguration.ACTIVITY_TYPE_UNDEFINED);
+        assertEquals("The process should receive rotated configuration for compatibility",
+                expectedProcConfig, app2.app.getConfiguration());
+
         // The fixed rotation transform can only be finished when all animation finished.
         doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
         mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 24950ce..a46e6d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -266,6 +266,15 @@
         mWpc.onMergedOverrideConfigurationChanged(config);
         assertEquals(ACTIVITY_TYPE_HOME, config.windowConfiguration.getActivityType());
         assertEquals(ACTIVITY_TYPE_UNDEFINED, mWpc.getActivityType());
+
+        final int globalSeq = 100;
+        mRootWindowContainer.getConfiguration().seq = globalSeq;
+        invertOrientation(mWpc.getConfiguration());
+        new ActivityBuilder(mService).setCreateTask(true).setUseProcess(mWpc).build();
+
+        assertTrue(mWpc.registeredForActivityConfigChanges());
+        assertEquals("Config seq of process should not be affected by activity",
+                mWpc.getConfiguration().seq, globalSeq);
     }
 
     private TestDisplayContent createTestDisplayContentInContainer() {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8ae1ee9..3d51d7c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1888,8 +1888,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
@@ -1941,8 +1941,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
@@ -2010,8 +2010,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
@@ -2088,8 +2088,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
@@ -2126,8 +2126,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
@@ -2210,8 +2210,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
@@ -2247,8 +2247,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
@@ -3853,8 +3853,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
@@ -3891,8 +3891,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
@@ -4145,8 +4145,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.
@@ -4184,8 +4184,8 @@
      *
      * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
      * restrictions, and apps are recommended to use resettable identifiers (see <a
-     * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
-     * the following requirements is met:
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
      * <ul>
      *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
      *     is a privileged permission that can only be granted to apps preloaded on the device.