Merge "Make getFrameNumber lazy" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 039d656..5df8750 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34649,6 +34649,7 @@
   public abstract class MediaBrowserService extends android.app.Service {
     ctor public MediaBrowserService();
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public void notifyChildrenChanged(java.lang.String);
     method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
diff --git a/api/system-current.txt b/api/system-current.txt
index 004746c..acb699d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -37304,6 +37304,7 @@
   public abstract class MediaBrowserService extends android.app.Service {
     ctor public MediaBrowserService();
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public void notifyChildrenChanged(java.lang.String);
     method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
diff --git a/api/test-current.txt b/api/test-current.txt
index 11f111d..e4b11bd 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34724,6 +34724,7 @@
   public abstract class MediaBrowserService extends android.app.Service {
     ctor public MediaBrowserService();
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public final android.os.Bundle getBrowserRootHints();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public void notifyChildrenChanged(java.lang.String);
     method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 374c4f6..ee4c2f7 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -133,4 +133,10 @@
      * the focused activity.
      */
     public abstract List<IBinder> getTopVisibleActivities();
+
+    /**
+     * Callback for window manager to let activity manager know that docked stack changes its
+     * minimized state.
+     */
+    public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
 }
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 4c4f128..aef92cf 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -29,6 +29,7 @@
 import android.content.res.ResourcesKey;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.IBinder;
+import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.LocaleList;
@@ -430,37 +431,44 @@
             @Nullable Configuration overrideConfig,
             @NonNull CompatibilityInfo compatInfo,
             @Nullable ClassLoader classLoader) {
-        final ResourcesKey key = new ResourcesKey(
-                resDir,
-                splitResDirs,
-                overlayDirs,
-                libDirs,
-                displayId,
-                overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
-                compatInfo);
-        classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                    "ResourcesManager#createBaseActivityResources");
+            final ResourcesKey key = new ResourcesKey(
+                    resDir,
+                    splitResDirs,
+                    overlayDirs,
+                    libDirs,
+                    displayId,
+                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+                    compatInfo);
+            classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
 
-        if (DEBUG) {
-            Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
-                    + " with key=" + key);
-        }
-
-        synchronized (this) {
-            final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
-                    activityToken);
-
-            if (overrideConfig != null) {
-                activityResources.overrideConfig.setTo(overrideConfig);
-            } else {
-                activityResources.overrideConfig.setToDefaults();
+            if (DEBUG) {
+                Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
+                        + " with key=" + key);
             }
+
+            synchronized (this) {
+                final ActivityResources activityResources =
+                        getOrCreateActivityResourcesStructLocked(
+                                activityToken);
+
+                if (overrideConfig != null) {
+                    activityResources.overrideConfig.setTo(overrideConfig);
+                } else {
+                    activityResources.overrideConfig.setToDefaults();
+                }
+            }
+
+            // Update any existing Activity Resources references.
+            updateResourcesForActivity(activityToken, overrideConfig);
+
+            // Now request an actual Resources object.
+            return getOrCreateResources(activityToken, key, classLoader);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
-
-        // Update any existing Activity Resources references.
-        updateResourcesForActivity(activityToken, overrideConfig);
-
-        // Now request an actual Resources object.
-        return getOrCreateResources(activityToken, key, classLoader);
     }
 
     /**
@@ -490,8 +498,8 @@
             }
 
             if (activityToken != null) {
-                final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
-                        activityToken);
+                final ActivityResources activityResources =
+                        getOrCreateActivityResourcesStructLocked(activityToken);
 
                 // Clean up any dead references so they don't pile up.
                 ArrayUtils.unstableRemoveIf(activityResources.activityResources,
@@ -539,6 +547,7 @@
         final String[] systemLocales = findSystemLocales
                 ? AssetManager.getSystem().getLocales() : null;
         final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
+
         // Avoid checking for non-pseudo-locales if we already know there were some from a previous
         // Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
         // since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
@@ -613,16 +622,21 @@
             @Nullable Configuration overrideConfig,
             @NonNull CompatibilityInfo compatInfo,
             @Nullable ClassLoader classLoader) {
-        final ResourcesKey key = new ResourcesKey(
-                resDir,
-                splitResDirs,
-                overlayDirs,
-                libDirs,
-                displayId,
-                overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
-                compatInfo);
-        classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
-        return getOrCreateResources(activityToken, key, classLoader);
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
+            final ResourcesKey key = new ResourcesKey(
+                    resDir,
+                    splitResDirs,
+                    overlayDirs,
+                    libDirs,
+                    displayId,
+                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+                    compatInfo);
+            classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+            return getOrCreateResources(activityToken, key, classLoader);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
     }
 
     /**
@@ -636,93 +650,104 @@
      */
     public void updateResourcesForActivity(@NonNull IBinder activityToken,
             @Nullable Configuration overrideConfig) {
-        synchronized (this) {
-            final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
-                    activityToken);
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                    "ResourcesManager#updateResourcesForActivity");
+            synchronized (this) {
+                final ActivityResources activityResources =
+                        getOrCreateActivityResourcesStructLocked(activityToken);
 
-            if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
-                // They are the same, no work to do.
-                return;
-            }
-
-            // Grab a copy of the old configuration so we can create the delta's of each
-            // Resources object associated with this Activity.
-            final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
-
-            // Update the Activity's base override.
-            if (overrideConfig != null) {
-                activityResources.overrideConfig.setTo(overrideConfig);
-            } else {
-                activityResources.overrideConfig.setToDefaults();
-            }
-
-            if (DEBUG) {
-                Throwable here = new Throwable();
-                here.fillInStackTrace();
-                Slog.d(TAG, "updating resources override for activity=" + activityToken
-                        + " from oldConfig=" + Configuration.resourceQualifierString(oldConfig)
-                        + " to newConfig="
-                        + Configuration.resourceQualifierString(activityResources.overrideConfig),
-                        here);
-            }
-
-            final boolean activityHasOverrideConfig =
-                    !activityResources.overrideConfig.equals(Configuration.EMPTY);
-
-            // Rebase each Resources associated with this Activity.
-            final int refCount = activityResources.activityResources.size();
-            for (int i = 0; i < refCount; i++) {
-                WeakReference<Resources> weakResRef = activityResources.activityResources.get(i);
-                Resources resources = weakResRef.get();
-                if (resources == null) {
-                    continue;
+                if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
+                    // They are the same, no work to do.
+                    return;
                 }
 
-                // Extract the ResourcesKey that was last used to create the Resources for this
-                // activity.
-                final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
-                if (oldKey == null) {
-                    Slog.e(TAG, "can't find ResourcesKey for resources impl="
-                            + resources.getImpl());
-                    continue;
-                }
+                // Grab a copy of the old configuration so we can create the delta's of each
+                // Resources object associated with this Activity.
+                final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
 
-                // Build the new override configuration for this ResourcesKey.
-                final Configuration rebasedOverrideConfig = new Configuration();
+                // Update the Activity's base override.
                 if (overrideConfig != null) {
-                    rebasedOverrideConfig.setTo(overrideConfig);
+                    activityResources.overrideConfig.setTo(overrideConfig);
+                } else {
+                    activityResources.overrideConfig.setToDefaults();
                 }
 
-                if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
-                    // Generate a delta between the old base Activity override configuration and
-                    // the actual final override configuration that was used to figure out the real
-                    // delta this Resources object wanted.
-                    Configuration overrideOverrideConfig = Configuration.generateDelta(
-                            oldConfig, oldKey.mOverrideConfiguration);
-                    rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
-                }
-
-                // Create the new ResourcesKey with the rebased override config.
-                final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir, oldKey.mSplitResDirs,
-                        oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
-                        rebasedOverrideConfig, oldKey.mCompatInfo);
-
                 if (DEBUG) {
-                    Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
-                            + " to newKey=" + newKey);
+                    Throwable here = new Throwable();
+                    here.fillInStackTrace();
+                    Slog.d(TAG, "updating resources override for activity=" + activityToken
+                            + " from oldConfig="
+                            + Configuration.resourceQualifierString(oldConfig)
+                            + " to newConfig="
+                            + Configuration.resourceQualifierString(
+                            activityResources.overrideConfig),
+                            here);
                 }
 
-                ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
-                if (resourcesImpl == null) {
-                    resourcesImpl = createResourcesImpl(newKey);
-                    mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
-                }
+                final boolean activityHasOverrideConfig =
+                        !activityResources.overrideConfig.equals(Configuration.EMPTY);
 
-                if (resourcesImpl != resources.getImpl()) {
-                    // Set the ResourcesImpl, updating it for all users of this Resources object.
-                    resources.setImpl(resourcesImpl);
+                // Rebase each Resources associated with this Activity.
+                final int refCount = activityResources.activityResources.size();
+                for (int i = 0; i < refCount; i++) {
+                    WeakReference<Resources> weakResRef = activityResources.activityResources.get(
+                            i);
+                    Resources resources = weakResRef.get();
+                    if (resources == null) {
+                        continue;
+                    }
+
+                    // Extract the ResourcesKey that was last used to create the Resources for this
+                    // activity.
+                    final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
+                    if (oldKey == null) {
+                        Slog.e(TAG, "can't find ResourcesKey for resources impl="
+                                + resources.getImpl());
+                        continue;
+                    }
+
+                    // Build the new override configuration for this ResourcesKey.
+                    final Configuration rebasedOverrideConfig = new Configuration();
+                    if (overrideConfig != null) {
+                        rebasedOverrideConfig.setTo(overrideConfig);
+                    }
+
+                    if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
+                        // Generate a delta between the old base Activity override configuration and
+                        // the actual final override configuration that was used to figure out the
+                        // real delta this Resources object wanted.
+                        Configuration overrideOverrideConfig = Configuration.generateDelta(
+                                oldConfig, oldKey.mOverrideConfiguration);
+                        rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+                    }
+
+                    // Create the new ResourcesKey with the rebased override config.
+                    final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
+                            oldKey.mSplitResDirs,
+                            oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
+                            rebasedOverrideConfig, oldKey.mCompatInfo);
+
+                    if (DEBUG) {
+                        Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+                                + " to newKey=" + newKey);
+                    }
+
+                    ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
+                    if (resourcesImpl == null) {
+                        resourcesImpl = createResourcesImpl(newKey);
+                        mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
+                    }
+
+                    if (resourcesImpl != resources.getImpl()) {
+                        // Set the ResourcesImpl, updating it for all users of this Resources
+                        // object.
+                        resources.setImpl(resourcesImpl);
+                    }
                 }
             }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
     }
 
@@ -745,86 +770,95 @@
 
     public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
                                                              @Nullable CompatibilityInfo compat) {
-        if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
-            if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
-                    + mResConfiguration.seq + ", newSeq=" + config.seq);
-            return false;
-        }
-        int changes = mResConfiguration.updateFrom(config);
-        // Things might have changed in display manager, so clear the cached displays.
-        mDisplays.clear();
-        DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                    "ResourcesManager#applyConfigurationToResourcesLocked");
 
-        if (compat != null && (mResCompatibilityInfo == null ||
-                !mResCompatibilityInfo.equals(compat))) {
-            mResCompatibilityInfo = compat;
-            changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
-                    | ActivityInfo.CONFIG_SCREEN_SIZE
-                    | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
-        }
+            if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+                if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+                        + mResConfiguration.seq + ", newSeq=" + config.seq);
+                return false;
+            }
+            int changes = mResConfiguration.updateFrom(config);
+            // Things might have changed in display manager, so clear the cached displays.
+            mDisplays.clear();
+            DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
 
-        Configuration localeAdjustedConfig = config;
-        final LocaleList configLocales = config.getLocales();
-        if (!configLocales.isEmpty()) {
-            setDefaultLocalesLocked(configLocales);
-            final LocaleList adjustedLocales = LocaleList.getAdjustedDefault();
-            if (adjustedLocales != configLocales) { // has the same result as .equals() in this case
-                // The first locale in the list was not chosen. So we create a modified
-                // configuration with the adjusted locales (which moves the chosen locale to the
-                // front).
-                localeAdjustedConfig = new Configuration();
-                localeAdjustedConfig.setTo(config);
-                localeAdjustedConfig.setLocales(adjustedLocales);
-                // Also adjust the locale list in mResConfiguration, so that the Resources created
-                // later would have the same locale list.
-                if (!mResConfiguration.getLocales().equals(adjustedLocales)) {
-                    mResConfiguration.setLocales(adjustedLocales);
-                    changes |= ActivityInfo.CONFIG_LOCALE;
+            if (compat != null && (mResCompatibilityInfo == null ||
+                    !mResCompatibilityInfo.equals(compat))) {
+                mResCompatibilityInfo = compat;
+                changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+                        | ActivityInfo.CONFIG_SCREEN_SIZE
+                        | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+            }
+
+            Configuration localeAdjustedConfig = config;
+            final LocaleList configLocales = config.getLocales();
+            if (!configLocales.isEmpty()) {
+                setDefaultLocalesLocked(configLocales);
+                final LocaleList adjustedLocales = LocaleList.getAdjustedDefault();
+                if (adjustedLocales
+                        != configLocales) { // has the same result as .equals() in this case
+                    // The first locale in the list was not chosen. So we create a modified
+                    // configuration with the adjusted locales (which moves the chosen locale to the
+                    // front).
+                    localeAdjustedConfig = new Configuration();
+                    localeAdjustedConfig.setTo(config);
+                    localeAdjustedConfig.setLocales(adjustedLocales);
+                    // Also adjust the locale list in mResConfiguration, so that the Resources
+                    // created later would have the same locale list.
+                    if (!mResConfiguration.getLocales().equals(adjustedLocales)) {
+                        mResConfiguration.setLocales(adjustedLocales);
+                        changes |= ActivityInfo.CONFIG_LOCALE;
+                    }
                 }
             }
-        }
 
-        Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics, compat);
+            Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics,
+                    compat);
 
-        ApplicationPackageManager.configurationChanged();
-        //Slog.i(TAG, "Configuration changed in " + currentPackageName());
+            ApplicationPackageManager.configurationChanged();
+            //Slog.i(TAG, "Configuration changed in " + currentPackageName());
 
-        Configuration tmpConfig = null;
+            Configuration tmpConfig = null;
 
-        for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
-            ResourcesKey key = mResourceImpls.keyAt(i);
-            ResourcesImpl r = mResourceImpls.valueAt(i).get();
-            if (r != null) {
-                if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
-                        + r + " config to: " + localeAdjustedConfig);
-                int displayId = key.mDisplayId;
-                boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-                DisplayMetrics dm = defaultDisplayMetrics;
-                final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
-                if (!isDefaultDisplay || hasOverrideConfiguration) {
-                    if (tmpConfig == null) {
-                        tmpConfig = new Configuration();
+            for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
+                ResourcesKey key = mResourceImpls.keyAt(i);
+                ResourcesImpl r = mResourceImpls.valueAt(i).get();
+                if (r != null) {
+                    if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+                            + r + " config to: " + localeAdjustedConfig);
+                    int displayId = key.mDisplayId;
+                    boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+                    DisplayMetrics dm = defaultDisplayMetrics;
+                    final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
+                    if (!isDefaultDisplay || hasOverrideConfiguration) {
+                        if (tmpConfig == null) {
+                            tmpConfig = new Configuration();
+                        }
+                        tmpConfig.setTo(localeAdjustedConfig);
+                        if (!isDefaultDisplay) {
+                            dm = getDisplayMetrics(displayId);
+                            applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+                        }
+                        if (hasOverrideConfiguration) {
+                            tmpConfig.updateFrom(key.mOverrideConfiguration);
+                        }
+                        r.updateConfiguration(tmpConfig, dm, compat);
+                    } else {
+                        r.updateConfiguration(localeAdjustedConfig, dm, compat);
                     }
-                    tmpConfig.setTo(localeAdjustedConfig);
-                    if (!isDefaultDisplay) {
-                        dm = getDisplayMetrics(displayId);
-                        applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
-                    }
-                    if (hasOverrideConfiguration) {
-                        tmpConfig.updateFrom(key.mOverrideConfiguration);
-                    }
-                    r.updateConfiguration(tmpConfig, dm, compat);
+                    //Slog.i(TAG, "Updated app resources " + v.getKey()
+                    //        + " " + r + ": " + r.getConfiguration());
                 } else {
-                    r.updateConfiguration(localeAdjustedConfig, dm, compat);
+                    //Slog.i(TAG, "Removing old resources " + v.getKey());
+                    mResourceImpls.removeAt(i);
                 }
-                //Slog.i(TAG, "Updated app resources " + v.getKey()
-                //        + " " + r + ": " + r.getConfiguration());
-            } else {
-                //Slog.i(TAG, "Removing old resources " + v.getKey());
-                mResourceImpls.removeAt(i);
             }
-        }
 
-        return changes != 0;
+            return changes != 0;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
     }
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index cd6e572..7cd13ea 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -542,7 +542,8 @@
             public RttManager createService(ContextImpl ctx) {
                 IBinder b = ServiceManager.getService(Context.WIFI_RTT_SERVICE);
                 IRttManager service = IRttManager.Stub.asInterface(b);
-                return new RttManager(ctx.getOuterContext(), service);
+                return new RttManager(ctx.getOuterContext(), service,
+                        ConnectivityThread.getInstanceLooper());
             }});
 
         registerService(Context.ETHERNET_SERVICE, EthernetManager.class,
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ade2248..787974d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2181,7 +2181,7 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
-     * The device implements {@link #FEATURE_VR_MODE} but additionally meets all CTS requirements
+     * The device implements {@link #FEATURE_VR_MODE} but additionally meets all CDD requirements
      * to be certified as a "VR Ready" device, which guarantees that the device is capable of
      * delivering consistent performance at a high framerate over an extended period of time for
      * typical VR application CPU/GPU workloads with a minimal number of frame drops, implements
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index dd73e53..7d74a12 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -437,6 +437,14 @@
         return filePath.startsWith(dirPath);
     }
 
+    public static boolean deleteContentsAndDir(File dir) {
+        if (deleteContents(dir)) {
+            return dir.delete();
+        } else {
+            return false;
+        }
+    }
+
     public static boolean deleteContents(File dir) {
         File[] files = dir.listFiles();
         boolean success = true;
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index c21c65a..3915b02 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1325,6 +1325,24 @@
             }
 
             @Override
+            public void destroyUserStorage(String volumeUuid, int userId, int flags)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(volumeUuid);
+                    _data.writeInt(userId);
+                    _data.writeInt(flags);
+                    mRemote.transact(Stub.TRANSACTION_destroyUserStorage, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            @Override
             public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -1465,6 +1483,7 @@
         static final int TRANSACTION_isUserKeyUnlocked = IBinder.FIRST_CALL_TRANSACTION + 65;
 
         static final int TRANSACTION_prepareUserStorage = IBinder.FIRST_CALL_TRANSACTION + 66;
+        static final int TRANSACTION_destroyUserStorage = IBinder.FIRST_CALL_TRANSACTION + 67;
 
         static final int TRANSACTION_isConvertibleToFBE = IBinder.FIRST_CALL_TRANSACTION + 68;
 
@@ -2096,6 +2115,15 @@
                     reply.writeNoException();
                     return true;
                 }
+                case TRANSACTION_destroyUserStorage: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String volumeUuid = data.readString();
+                    int userId = data.readInt();
+                    int _flags = data.readInt();
+                    destroyUserStorage(volumeUuid, userId, _flags);
+                    reply.writeNoException();
+                    return true;
+                }
                 case TRANSACTION_mountAppFuse: {
                     data.enforceInterface(DESCRIPTOR);
                     String name = data.readString();
@@ -2434,6 +2462,7 @@
 
     public void prepareUserStorage(String volumeUuid, int userId, int serialNumber,
             int flags) throws RemoteException;
+    public void destroyUserStorage(String volumeUuid, int userId, int flags) throws RemoteException;
 
     public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 0a8fdd9..f68e227 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1053,6 +1053,15 @@
     }
 
     /** {@hide} */
+    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
+        try {
+            mMountService.destroyUserStorage(volumeUuid, userId, flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
     public boolean isUserKeyUnlocked(int userId) {
         try {
             return mMountService.isUserKeyUnlocked(userId);
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 2481e04..415c291 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
@@ -184,7 +185,7 @@
      * any references to the functor, just that the reference from this specific
      * canvas's display list has been released.
      */
-    public void drawGLFunctor2(long drawGLFunctor, Runnable releasedCallback) {
+    public void drawGLFunctor2(long drawGLFunctor, @Nullable Runnable releasedCallback) {
         nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
     }
 
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index a0f5142..7f92777 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -417,6 +417,21 @@
     public static final int FLAG_WINDOW_IS_OBSCURED = 0x1;
 
     /**
+     * This flag indicates that the window that received this motion event is partly
+     * or wholly obscured by another visible window above it.  This flag is set to true
+     * even if the event did not directly pass through the obscured area.
+     * A security sensitive application can check this flag to identify situations in which
+     * a malicious application may have covered up part of its content for the purpose
+     * of misleading the user or hijacking touches.  An appropriate response might be
+     * to drop the suspect touches or to take additional precautions to confirm the user's
+     * actual intent.
+     *
+     * Unlike FLAG_WINDOW_IS_OBSCURED, this is actually true.
+     * @hide
+     */
+    public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2;
+
+    /**
      * Private flag that indicates when the system has detected that this motion event
      * may be inconsistent with respect to the sequence of previously delivered motion events,
      * such as when a pointer move event is sent but the pointer is not down.
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 738aaca..b1598e7 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -270,15 +270,12 @@
             mLastXOffset = xOffset;
             mLastYOffset = yOffset;
 
-            // Only clip the content to the bounds if we are not fullscreen. In the other case, we
-            // actually need to draw outside these.
-            if (mResizeMode == RESIZE_MODE_FREEFORM) {
-                mRenderer.setContentDrawBounds(
-                        mLastXOffset,
-                        mLastYOffset,
-                        mLastXOffset + mLastContentWidth,
-                        mLastYOffset + mLastCaptionHeight + mLastContentHeight);
-            }
+            // Inform the renderer of the content's new bounds
+            mRenderer.setContentDrawBounds(
+                    mLastXOffset,
+                    mLastYOffset,
+                    mLastXOffset + mLastContentWidth,
+                    mLastYOffset + mLastCaptionHeight + mLastContentHeight);
 
             // If this was the first call and redrawLocked got already called prior
             // to us, we should re-issue a redrawLocked now.
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 4f17c39..eac9f64 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -17,22 +17,27 @@
 package com.android.internal.policy;
 
 import android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
 import android.view.ContextThemeWrapper;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 
 /**
  * Context for decor views which can be seeded with pure application context and not depend on the
- * activity, but still provide some of the facilities that Activity has, e.g. themes.
+ * activity, but still provide some of the facilities that Activity has,
+ * e.g. themes, activity-based resources, etc.
  *
  * @hide
  */
 class DecorContext extends ContextThemeWrapper {
     private PhoneWindow mPhoneWindow;
     private WindowManager mWindowManager;
+    private Resources mActivityResources;
 
-    public DecorContext(Context context) {
+    public DecorContext(Context context, Resources activityResources) {
         super(context, null);
+        mActivityResources = activityResources;
     }
 
     void setPhoneWindow(PhoneWindow phoneWindow) {
@@ -52,4 +57,14 @@
         }
         return super.getSystemService(name);
     }
+
+    @Override
+    public Resources getResources() {
+        return mActivityResources;
+    }
+
+    @Override
+    public AssetManager getAssets() {
+        return mActivityResources.getAssets();
+    }
 }
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index fe63267..151c530 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2297,7 +2297,7 @@
             if (applicationContext == null) {
                 context = getContext();
             } else {
-                context = new DecorContext(applicationContext);
+                context = new DecorContext(applicationContext, getContext().getResources());
                 if (mTheme != -1) {
                     context.setTheme(mTheme);
                 }
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index cadfd3d..d6f9db5 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -105,8 +105,10 @@
         jlong canvasPtr, jlong functorPtr, jobject releasedCallback) {
     Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
     Functor* functor = reinterpret_cast<Functor*>(functorPtr);
-    sp<GlFunctorReleasedCallbackBridge> bridge(new GlFunctorReleasedCallbackBridge(
-            env, releasedCallback));
+    sp<GlFunctorReleasedCallbackBridge> bridge;
+    if (releasedCallback) {
+        bridge = new GlFunctorReleasedCallbackBridge(env, releasedCallback);
+    }
     canvas->callDrawGLFunction(functor, bridge.get());
 }
 
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 6913f43..715c875 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -34,9 +34,7 @@
 #include <utils/String8.h>
 #include <utils/threads.h>
 #include <utils/Timers.h>
-#ifdef __ANDROID__
-#include <cutils/trace.h>
-#endif
+#include <utils/Trace.h>
 
 #include <assert.h>
 #include <dirent.h>
@@ -54,14 +52,6 @@
     _rc; })
 #endif
 
-#ifdef __ANDROID__
-#define MY_TRACE_BEGIN(x) ATRACE_BEGIN(x)
-#define MY_TRACE_END() ATRACE_END()
-#else
-#define MY_TRACE_BEGIN(x)
-#define MY_TRACE_END()
-#endif
-
 using namespace android;
 
 static const bool kIsDebug = false;
@@ -623,7 +613,7 @@
     ResTable* sharedRes = NULL;
     bool shared = true;
     bool onlyEmptyResources = true;
-    MY_TRACE_BEGIN(ap.path.string());
+    ATRACE_NAME(ap.path.string());
     Asset* idmap = openIdmapLocked(ap);
     size_t nextEntryIdx = mResources->getTableCount();
     ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
@@ -703,8 +693,6 @@
     if (idmap != NULL) {
         delete idmap;
     }
-    MY_TRACE_END();
-
     return onlyEmptyResources;
 }
 
@@ -752,6 +740,7 @@
 
 void AssetManager::updateResourceParamsLocked() const
 {
+    ATRACE_CALL();
     ResTable* res = mResources;
     if (!res) {
         return;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 1ccc59a..15cb684 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <algorithm>
 #include <limits>
 #include <memory>
 #include <type_traits>
@@ -5810,6 +5811,10 @@
     return NULL;
 }
 
+static bool compareResTableConfig(const ResTable_config& a, const ResTable_config& b) {
+    return a.compare(b) < 0;
+}
+
 void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap,
         bool ignoreAndroidPackage, bool includeSystemConfigs) const {
     const size_t packageCount = mPackageGroups.size();
@@ -5840,17 +5845,11 @@
                     ResTable_config cfg;
                     memset(&cfg, 0, sizeof(ResTable_config));
                     cfg.copyFromDtoH(config->config);
-                    // only insert unique
-                    const size_t N = configs->size();
-                    size_t n;
-                    for (n = 0; n < N; n++) {
-                        if (0 == (*configs)[n].compare(cfg)) {
-                            break;
-                        }
-                    }
-                    // if we didn't find it
-                    if (n == N) {
-                        configs->add(cfg);
+
+                    auto iter = std::lower_bound(configs->begin(), configs->end(), cfg,
+                                                 compareResTableConfig);
+                    if (iter == configs->end() || iter->compare(cfg) != 0) {
+                        configs->insertAt(cfg, std::distance(configs->begin(), iter));
                     }
                 }
             }
@@ -5858,6 +5857,10 @@
     }
 }
 
+static bool compareString8AndCString(const String8& str, const char* cStr) {
+    return strcmp(str.string(), cStr) < 0;
+}
+
 void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales) const
 {
     Vector<ResTable_config> configs;
@@ -5872,15 +5875,11 @@
     char locale[RESTABLE_MAX_LOCALE_LEN];
     for (size_t i=0; i<I; i++) {
         configs[i].getBcp47Locale(locale);
-        const size_t J = locales->size();
-        size_t j;
-        for (j=0; j<J; j++) {
-            if (0 == strcmp(locale, (*locales)[j].string())) {
-                break;
-            }
-        }
-        if (j == J) {
-            locales->add(String8(locale));
+
+        auto iter = std::lower_bound(locales->begin(), locales->end(), locale,
+                                     compareString8AndCString);
+        if (iter == locales->end() || strcmp(iter->string(), locale) != 0) {
+            locales->insertAt(String8(locale), std::distance(locales->begin(), iter));
         }
     }
 }
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 7cd7fb5..b8b4625 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -39,8 +39,20 @@
  */
 #include "data/basic/basic_arsc.h"
 
+/**
+ * Include a binary library resource table.
+ *
+ * Package: com.android.test.basic
+ */
 #include "data/lib/lib_arsc.h"
 
+/**
+ * Include a system resource table.
+ *
+ * Package: android
+ */
+#include "data/system/system_arsc.h"
+
 TEST(ResTableTest, shouldLoadSuccessfully) {
     ResTable table;
     ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
@@ -324,4 +336,25 @@
     ASSERT_EQ(uint32_t(600), val.data);
 }
 
+TEST(ResTableTest, GetConfigurationsReturnsUniqueList) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len));
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    ResTable_config configSv;
+    memset(&configSv, 0, sizeof(configSv));
+    configSv.language[0] = 's';
+    configSv.language[1] = 'v';
+
+    Vector<ResTable_config> configs;
+    table.getConfigurations(&configs);
+
+    EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv));
+
+    Vector<String8> locales;
+    table.getLocales(&locales);
+
+    EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv")));
+}
+
 } // namespace
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index ac80d88..ff9be16 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -21,7 +21,7 @@
 enum { MAY_NOT_BE_BAG = false };
 
 static inline bool operator==(const android::ResTable_config& a, const android::ResTable_config& b) {
-    return memcmp(&a, &b, sizeof(a)) == 0;
+    return a.compare(b) == 0;
 }
 
 static inline ::std::ostream& operator<<(::std::ostream& out, const android::ResTable_config& c) {
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
index 27f25fe..6a31fb8 100644
--- a/libs/androidfw/tests/data/system/R.h
+++ b/libs/androidfw/tests/data/system/R.h
@@ -33,6 +33,12 @@
     };
 }
 
+namespace integer {
+    enum {
+        number = 0x01030000, // sv
+    };
+}
+
 } // namespace R
 } // namespace android
 
diff --git a/libs/androidfw/tests/data/system/res/values-sv/values.xml b/libs/androidfw/tests/data/system/res/values-sv/values.xml
new file mode 100644
index 0000000..b97bdb6
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values-sv/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <public type="integer" name="number" id="0x01030000" />
+    <integer name="number">1</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/system/system_arsc.h b/libs/androidfw/tests/data/system/system_arsc.h
index 215ecae..b0dab6b 100644
--- a/libs/androidfw/tests/data/system/system_arsc.h
+++ b/libs/androidfw/tests/data/system/system_arsc.h
@@ -1,8 +1,8 @@
 unsigned char system_arsc[] = {
-  0x02, 0x00, 0x0c, 0x00, 0x18, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x0c, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
   0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf0, 0x02, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xd0, 0x03, 0x00, 0x00,
   0x01, 0x00, 0x00, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
   0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -25,26 +25,33 @@
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
-  0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
-  0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
-  0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x78, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00,
+  0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00,
+  0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x5e, 0x00,
+  0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x2d, 0x00, 0x70, 0x00,
+  0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x84, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00,
   0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x67, 0x00,
   0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
   0x0a, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x67, 0x00,
   0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
   0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
-  0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40,
-  0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00,
+  0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+  0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x8c, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+  0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -55,15 +62,27 @@
   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00,
   0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
-  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x44, 0x00,
-  0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00,
+  0x78, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff,
-  0x01, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+  0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x01, 0x01,
+  0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x60, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+  0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+  0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
-unsigned int system_arsc_len = 792;
+unsigned int system_arsc_len = 1016;
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 6fc74a5..502f027 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -31,18 +31,16 @@
 namespace android {
 namespace uirenderer {
 
-FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+FrameBuilder::FrameBuilder(const SkRect& clip,
         uint32_t viewportWidth, uint32_t viewportHeight,
-        const std::vector< sp<RenderNode> >& nodes,
-        const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches)
-        : mCanvasState(*this)
+        const LightGeometry& lightGeometry, Caches& caches)
+        : mStdAllocator(mAllocator)
+        , mLayerBuilders(mStdAllocator)
+        , mLayerStack(mStdAllocator)
+        , mCanvasState(*this)
         , mCaches(caches)
         , mLightRadius(lightGeometry.radius)
-        , mDrawFbo0(!nodes.empty()) {
-    ATRACE_NAME("prepare drawing commands");
-
-    mLayerBuilders.reserve(layers.entries().size());
-    mLayerStack.reserve(layers.entries().size());
+        , mDrawFbo0(true) {
 
     // Prepare to defer Fbo0
     auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip));
@@ -51,7 +49,31 @@
     mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
             clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
             lightGeometry.center);
+}
 
+FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers,
+        const LightGeometry& lightGeometry, Caches& caches)
+        : mStdAllocator(mAllocator)
+        , mLayerBuilders(mStdAllocator)
+        , mLayerStack(mStdAllocator)
+        , mCanvasState(*this)
+        , mCaches(caches)
+        , mLightRadius(lightGeometry.radius)
+        , mDrawFbo0(false) {
+    // TODO: remove, with each layer on its own save stack
+
+    // Prepare to defer Fbo0 (which will be empty)
+    auto fbo0 = mAllocator.create<LayerBuilder>(1, 1, Rect(1, 1));
+    mLayerBuilders.push_back(fbo0);
+    mLayerStack.push_back(0);
+    mCanvasState.initializeSaveStack(1, 1,
+            0, 0, 1, 1,
+            lightGeometry.center);
+
+    deferLayers(layers);
+}
+
+void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) {
     // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
     // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
     for (int i = layers.entries().size() - 1; i >= 0; i--) {
@@ -76,10 +98,45 @@
             restoreForLayer();
         }
     }
+}
 
+void FrameBuilder::deferRenderNode(RenderNode& renderNode) {
+    renderNode.computeOrdering();
+
+    mCanvasState.save(SaveFlags::MatrixClip);
+    deferNodePropsAndOps(renderNode);
+    mCanvasState.restore();
+}
+
+void FrameBuilder::deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode) {
+    renderNode.computeOrdering();
+
+    mCanvasState.save(SaveFlags::MatrixClip);
+    mCanvasState.translate(tx, ty);
+    mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+            SkRegion::kIntersect_Op);
+    deferNodePropsAndOps(renderNode);
+    mCanvasState.restore();
+}
+
+static Rect nodeBounds(RenderNode& node) {
+    auto& props = node.properties();
+    return Rect(props.getLeft(), props.getTop(),
+            props.getRight(), props.getBottom());
+}
+
+void FrameBuilder::deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes,
+        const Rect& contentDrawBounds) {
+    if (nodes.size() < 1) return;
+    if (nodes.size() == 1) {
+        if (!nodes[0]->nothingToDraw()) {
+            deferRenderNode(*nodes[0]);
+        }
+        return;
+    }
     // It there are multiple render nodes, they are laid out as follows:
     // #0 - backdrop (content + caption)
-    // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
+    // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
     // #2 - additional overlay nodes
     // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
     // resizing however it might become partially visible. The following render loop will crop the
@@ -88,45 +145,52 @@
     //
     // Additional nodes will be drawn on top with no particular clipping semantics.
 
-    // The bounds of the backdrop against which the content should be clipped.
-    Rect backdropBounds = contentDrawBounds;
     // Usually the contents bounds should be mContentDrawBounds - however - we will
     // move it towards the fixed edge to give it a more stable appearance (for the moment).
     // If there is no content bounds we ignore the layering as stated above and start with 2.
-    int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0;
 
-    for (const sp<RenderNode>& node : nodes) {
-        if (node->nothingToDraw()) continue;
-        node->computeOrdering();
-        int count = mCanvasState.save(SaveFlags::MatrixClip);
+    // Backdrop bounds in render target space
+    const Rect backdrop = nodeBounds(*nodes[0]);
 
-        if (layer == 0) {
-            const RenderProperties& properties = node->properties();
-            Rect targetBounds(properties.getLeft(), properties.getTop(),
-                              properties.getRight(), properties.getBottom());
-            // Move the content bounds towards the fixed corner of the backdrop.
-            const int x = targetBounds.left;
-            const int y = targetBounds.top;
-            // Remember the intersection of the target bounds and the intersection bounds against
-            // which we have to crop the content.
-            backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
-            backdropBounds.doIntersect(targetBounds);
-        } else if (layer == 1) {
-            // We shift and clip the content to match its final location in the window.
-            const float left = contentDrawBounds.left;
-            const float top = contentDrawBounds.top;
-            const float dx = backdropBounds.left - left;
-            const float dy = backdropBounds.top - top;
-            const float width = backdropBounds.getWidth();
-            const float height = backdropBounds.getHeight();
-            mCanvasState.translate(dx, dy);
-            // It gets cropped against the bounds of the backdrop to stay inside.
-            mCanvasState.clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op);
+    // Bounds that content will fill in render target space (note content node bounds may be bigger)
+    Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
+    content.translate(backdrop.left, backdrop.top);
+    if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
+        // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
+
+        // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
+        // also fill left/top. Currently, both 2up and freeform position content at the top/left of
+        // the backdrop, so this isn't necessary.
+        if (content.right < backdrop.right) {
+            // draw backdrop to right side of content
+            deferRenderNode(0, 0, Rect(content.right, backdrop.top,
+                    backdrop.right, backdrop.bottom), *nodes[0]);
         }
+        if (content.bottom < backdrop.bottom) {
+            // draw backdrop to bottom of content
+            // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
+            deferRenderNode(0, 0, Rect(content.left, content.bottom,
+                    content.right, backdrop.bottom), *nodes[0]);
+        }
+    }
 
-        deferNodePropsAndOps(*node);
-        mCanvasState.restoreToCount(count);
-        layer++;
+    if (!backdrop.isEmpty()) {
+        // content node translation to catch up with backdrop
+        float dx = contentDrawBounds.left - backdrop.left;
+        float dy = contentDrawBounds.top - backdrop.top;
+
+        Rect contentLocalClip = backdrop;
+        contentLocalClip.translate(dx, dy);
+        deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]);
+    } else {
+        deferRenderNode(*nodes[1]);
+    }
+
+    // remaining overlay nodes, simply defer
+    for (size_t index = 2; index < nodes.size(); index++) {
+        if (!nodes[index]->nothingToDraw()) {
+            deferRenderNode(*nodes[index]);
+        }
     }
 }
 
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index a6fd761..b915443 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -37,8 +37,8 @@
 class Rect;
 
 /**
- * Traverses all of the drawing commands from the layers and RenderNodes passed into it, preparing
- * them to be rendered.
+ * Processes, optimizes, and stores rendering commands from RenderNodes and
+ * LayerUpdateQueue, building content needed to render a frame.
  *
  * Resolves final drawing state for each operation (including clip, alpha and matrix), and then
  * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
@@ -60,21 +60,21 @@
         float radius;
     };
 
-    // TODO: remove
-    FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+    FrameBuilder(const SkRect& clip,
             uint32_t viewportWidth, uint32_t viewportHeight,
-            const std::vector< sp<RenderNode> >& nodes,
-            const LightGeometry& lightGeometry,
-            Caches& caches)
-            : FrameBuilder(layers, clip, viewportWidth, viewportHeight,
-                    nodes, lightGeometry, Rect(), caches) {}
+            const LightGeometry& lightGeometry, Caches& caches);
 
-    FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
-            uint32_t viewportWidth, uint32_t viewportHeight,
-            const std::vector< sp<RenderNode> >& nodes,
-            const LightGeometry& lightGeometry,
-            const Rect &contentDrawBounds,
-            Caches& caches);
+    FrameBuilder(const LayerUpdateQueue& layerUpdateQueue,
+            const LightGeometry& lightGeometry, Caches& caches);
+
+    void deferLayers(const LayerUpdateQueue& layers);
+
+    void deferRenderNode(RenderNode& renderNode);
+
+    void deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode);
+
+    void deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes,
+            const Rect& contentDrawBounds);
 
     virtual ~FrameBuilder() {}
 
@@ -223,8 +223,12 @@
     MAP_DEFERRABLE_OPS(X)
 #undef X
 
+    // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
+    LinearAllocator mAllocator;
+    LinearStdAllocator<void*> mStdAllocator;
+
     // List of every deferred layer's render state. Replayed in reverse order to render a frame.
-    std::vector<LayerBuilder*> mLayerBuilders;
+    LsaVector<LayerBuilder*> mLayerBuilders;
 
     /*
      * Stack of indices within mLayerBuilders representing currently active layers. If drawing
@@ -238,7 +242,7 @@
      * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
      * ops added to it.
     */
-    std::vector<size_t> mLayerStack;
+    LsaVector<size_t> mLayerStack;
 
     CanvasState mCanvasState;
 
@@ -246,9 +250,6 @@
 
     float mLightRadius;
 
-    // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
-    LinearAllocator mAllocator;
-
     const bool mDrawFbo0;
 };
 
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index eea11bf..3000777 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -244,7 +244,8 @@
 
         if (CC_UNLIKELY(activeUnclippedSaveLayers.empty()
                 && bakedState->computedState.opaqueOverClippedBounds
-                && bakedState->computedState.clippedBounds.contains(repaintRect))) {
+                && bakedState->computedState.clippedBounds.contains(repaintRect)
+                && !Properties::debugOverdraw)) {
             // discard all deferred drawing ops, since new one will occlude them
             clear();
         }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index df5c8d9..e6399d4 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -354,9 +354,13 @@
 
 #if HWUI_NEW_OPS
     auto& caches = Caches::getInstance();
-    FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
-            mRenderNodes, mLightGeometry, mContentDrawBounds, caches);
+    FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), mLightGeometry, caches);
+
+    frameBuilder.deferLayers(mLayerUpdateQueue);
     mLayerUpdateQueue.clear();
+
+    frameBuilder.deferRenderNodeScene(mRenderNodes, mContentDrawBounds);
+
     BakedOpRenderer renderer(caches, mRenderThread.renderState(),
             mOpaque, mLightInfo);
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -622,8 +626,7 @@
 #if HWUI_NEW_OPS
     static const std::vector< sp<RenderNode> > emptyNodeList;
     auto& caches = Caches::getInstance();
-    FrameBuilder frameBuilder(mLayerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
-            emptyNodeList, mLightGeometry, mContentDrawBounds, caches);
+    FrameBuilder frameBuilder(mLayerUpdateQueue, mLightGeometry, caches);
     mLayerUpdateQueue.clear();
     BakedOpRenderer renderer(caches, mRenderThread.renderState(),
             mOpaque, mLightInfo);
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 5f4ebc0..dbaefa4 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -171,11 +171,9 @@
         syncHierarchyPropertiesAndDisplayListImpl(node.get());
     }
 
-    static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) {
-        TestUtils::syncHierarchyPropertiesAndDisplayList(node);
-        std::vector<sp<RenderNode>> vec;
-        vec.emplace_back(node);
-        return vec;
+    static sp<RenderNode>& getSyncedNode(sp<RenderNode>& node) {
+        syncHierarchyPropertiesAndDisplayList(node);
+        return node;
     }
 
     typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 0aef620..84ef9c2 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -35,11 +35,10 @@
 using namespace android::uirenderer::renderthread;
 using namespace android::uirenderer::test;
 
-const LayerUpdateQueue sEmptyLayerUpdateQueue;
 const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
 const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
 
-static std::vector<sp<RenderNode>> createTestNodeList() {
+static sp<RenderNode> createTestNode() {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
@@ -56,31 +55,33 @@
         canvas.restore();
     });
     TestUtils::syncHierarchyPropertiesAndDisplayList(node);
-    std::vector<sp<RenderNode>> vec;
-    vec.emplace_back(node);
-    return vec;
+    return node;
 }
 
 void BM_FrameBuilder_defer(benchmark::State& state) {
-    auto nodes = createTestNodeList();
-    while (state.KeepRunning()) {
-        FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-                nodes, sLightGeometry, Caches::getInstance());
-        benchmark::DoNotOptimize(&frameBuilder);
-    }
+    TestUtils::runOnRenderThread([&state](RenderThread& thread) {
+        auto node = createTestNode();
+        while (state.KeepRunning()) {
+            FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+                    sLightGeometry, Caches::getInstance());
+            frameBuilder.deferRenderNode(*node);
+            benchmark::DoNotOptimize(&frameBuilder);
+        }
+    });
 }
 BENCHMARK(BM_FrameBuilder_defer);
 
 void BM_FrameBuilder_deferAndRender(benchmark::State& state) {
     TestUtils::runOnRenderThread([&state](RenderThread& thread) {
-        auto nodes = createTestNodeList();
+        auto node = createTestNode();
 
         RenderState& renderState = thread.renderState();
         Caches& caches = Caches::getInstance();
 
         while (state.KeepRunning()) {
-            FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-                    nodes, sLightGeometry, Caches::getInstance());
+            FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+                    sLightGeometry, caches);
+            frameBuilder.deferRenderNode(*node);
 
             BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
             frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -90,7 +91,7 @@
 }
 BENCHMARK(BM_FrameBuilder_deferAndRender);
 
-static std::vector<sp<RenderNode>> getSyncedSceneNodes(const char* sceneName) {
+static sp<RenderNode> getSyncedSceneNode(const char* sceneName) {
     gDisplay = getBuiltInDisplay(); // switch to real display if present
 
     TestContext testContext;
@@ -103,9 +104,7 @@
     });
 
     TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode);
-    std::vector<sp<RenderNode>> nodes;
-    nodes.emplace_back(rootNode);
-    return nodes;
+    return rootNode;
 }
 
 static auto SCENES = {
@@ -116,11 +115,12 @@
     TestUtils::runOnRenderThread([&state](RenderThread& thread) {
         const char* sceneName = *(SCENES.begin() + state.range_x());
         state.SetLabel(sceneName);
-        auto nodes = getSyncedSceneNodes(sceneName);
+        auto node = getSyncedSceneNode(sceneName);
         while (state.KeepRunning()) {
-            FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
-                    SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
-                    nodes, sLightGeometry, Caches::getInstance());
+            FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h),
+                    gDisplay.w, gDisplay.h,
+                    sLightGeometry, Caches::getInstance());
+            frameBuilder.deferRenderNode(*node);
             benchmark::DoNotOptimize(&frameBuilder);
         }
     });
@@ -131,15 +131,16 @@
     TestUtils::runOnRenderThread([&state](RenderThread& thread) {
         const char* sceneName = *(SCENES.begin() + state.range_x());
         state.SetLabel(sceneName);
-        auto nodes = getSyncedSceneNodes(sceneName);
+        auto node = getSyncedSceneNode(sceneName);
 
         RenderState& renderState = thread.renderState();
         Caches& caches = Caches::getInstance();
 
         while (state.KeepRunning()) {
-            FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
-                    SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
-                    nodes, sLightGeometry, Caches::getInstance());
+            FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h),
+                    gDisplay.w, gDisplay.h,
+                    sLightGeometry, Caches::getInstance());
+            frameBuilder.deferRenderNode(*node);
 
             BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
             frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index ebc1c80..0f16b15 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -29,11 +29,8 @@
 namespace android {
 namespace uirenderer {
 
-const LayerUpdateQueue sEmptyLayerUpdateQueue;
-const std::vector< sp<RenderNode> > sEmptyNodeList;
 const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
 
-
 /**
  * Virtual class implemented by each test to redirect static operation / state transitions to
  * virtual methods.
@@ -136,8 +133,10 @@
         canvas.drawRect(0, 0, 100, 200, SkPaint());
         canvas.drawBitmap(bitmap, 10, 10, nullptr);
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
@@ -162,8 +161,10 @@
         strokedPaint.setStrokeWidth(10);
         canvas.drawPoint(50, 50, strokedPaint);
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SimpleStrokeTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
@@ -177,8 +178,9 @@
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
 
     FailRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
@@ -211,15 +213,111 @@
         }
         canvas.restore();
     });
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
     SimpleBatchingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2 * LOOPS, renderer.getIndex())
             << "Expect number of ops = 2 * loop count";
 }
 
+RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
+    class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
+    public:
+        void onRectOp(const RectOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(0, mIndex++);
+            EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds);
+            EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom,
+                    state.computedState.clipSideFlags);
+        }
+    };
+
+    auto node = TestUtils::createNode(0, 0, 100, 100,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.drawRect(0, 0, 100, 100, SkPaint());
+    });
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node
+            *TestUtils::getSyncedNode(node));
+
+    DeferRenderNodeTranslateClipTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(1, renderer.getIndex());
+}
+
+RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
+    class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
+    public:
+        void onRectOp(const RectOp& op, const BakedOpState& state) override {
+            const Rect& clippedBounds = state.computedState.clippedBounds;
+            Matrix4 expected;
+            switch (mIndex++) {
+            case 0:
+                // background - left side
+                EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds);
+                expected.loadTranslate(100, 100, 0);
+                break;
+            case 1:
+                // background - top side
+                EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds);
+                expected.loadTranslate(100, 100, 0);
+                break;
+            case 2:
+                // content
+                EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds);
+                expected.loadTranslate(-50, -50, 0);
+                break;
+            case 3:
+                // overlay
+                EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds);
+                break;
+            default:
+                ADD_FAILURE() << "Too many rects observed";
+            }
+            EXPECT_EQ(expected, state.computedState.transform);
+        }
+    };
+
+    std::vector<sp<RenderNode>> nodes;
+    SkPaint transparentPaint;
+    transparentPaint.setAlpha(128);
+
+    // backdrop
+    nodes.push_back(TestUtils::createNode(100, 100, 700, 500, // 600x400
+            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.drawRect(0, 0, 600, 400, transparentPaint);
+    }));
+
+    // content
+    Rect contentDrawBounds(150, 150, 650, 450); // 500x300
+    nodes.push_back(TestUtils::createNode(0, 0, 800, 600,
+            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.drawRect(0, 0, 800, 600, transparentPaint);
+    }));
+
+    // overlay
+    nodes.push_back(TestUtils::createNode(0, 0, 800, 600,
+            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.drawRect(0, 0, 800, 200, transparentPaint);
+    }));
+
+    for (auto& node : nodes) {
+        TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+    }
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
+
+    DeferRenderNodeSceneTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(4, renderer.getIndex());
+}
+
 RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
     class EmptyNoFbo0TestRenderer : public TestRendererBase {
     public:
@@ -231,9 +329,9 @@
         }
     };
 
-    // Pass empty node list, so no work is enqueued for Fbo0
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            sEmptyNodeList, sLightGeometry, Caches::getInstance());
+    // Use layer update constructor, so no work is enqueued for Fbo0
+    LayerUpdateQueue emptyLayerUpdateQueue;
+    FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance());
     EmptyNoFbo0TestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
@@ -252,11 +350,12 @@
             [](RenderProperties& props, RecordingCanvas& canvas) {
         // no drawn content
     });
-    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
 
-    // Draw, but pass empty node list, so no work is done for primary frame
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            syncedNodeList, sLightGeometry, Caches::getInstance());
+    // Draw, but pass node without draw content, so no work is done for primary frame
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     EmptyWithFbo0TestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
@@ -281,9 +380,9 @@
 
     // Damage (and therefore clip) is same as last draw, subset of renderable area.
     // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
-    SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190);
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
 
     EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
             << "Recording must not have rejected ops, in order for this test to be valid";
@@ -324,9 +423,9 @@
         canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
         canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
     });
-
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
 
     EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
             << "Recording must not have rejected ops, in order for this test to be valid";
@@ -369,8 +468,10 @@
         canvas.drawBitmap(bitmap, 40, 70, nullptr);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     ClippedMergingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
@@ -397,8 +498,10 @@
         TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
         TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     TextMergingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
@@ -428,8 +531,11 @@
             TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
         }
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     TextStrikethroughTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2 * LOOPS, renderer.getIndex())
@@ -485,8 +591,9 @@
             TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
         }
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
     TextStyleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
@@ -516,8 +623,11 @@
         canvas.drawLayer(layerUpdater.get());
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     TextureLayerClipLocalMatrixTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
@@ -546,8 +656,10 @@
         canvas.restore();
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     TextureLayerCombineMatricesTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
@@ -562,8 +674,11 @@
             [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.drawLayer(layerUpdater.get());
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     FailRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
@@ -584,9 +699,10 @@
         canvas.callDrawGLFunction(&noopFunctor, nullptr);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(scrolledFunctorView),
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
             sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView));
+
     FunctorTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
@@ -608,9 +724,10 @@
         canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(unclippedColorView),
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
             sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView));
+
     ColorTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
@@ -654,8 +771,10 @@
         canvas.restore();
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     RenderNodeTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
@@ -678,9 +797,11 @@
         canvas.drawBitmap(bitmap, 0, 0, nullptr);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
-            SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
-            200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    // clip to small area, should see in receiver
+    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     ClippedTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
@@ -725,8 +846,11 @@
         canvas.drawRect(10, 10, 190, 190, SkPaint());
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerSimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
@@ -806,8 +930,10 @@
         canvas.restore();
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerNestedTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(12, renderer.getIndex());
@@ -826,8 +952,10 @@
         canvas.restore();
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
 
     FailRenderer renderer;
     // should see no ops, even within the layer, since the layer should be rejected
@@ -869,8 +997,11 @@
         canvas.drawRect(0, 0, 200, 200, SkPaint());
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerUnclippedSimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
@@ -923,8 +1054,11 @@
         canvas.drawRect(0, 0, 100, 100, SkPaint());
         canvas.restoreToCount(restoreTo);
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerUnclippedMergedClearsTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex())
@@ -964,8 +1098,10 @@
     });
 
     // draw with partial screen dirty, and assert we see that rect later
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerUnclippedClearClipTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
@@ -981,8 +1117,10 @@
     });
 
     // draw with partial screen dirty that doesn't intersect with savelayer
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     FailRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
@@ -1046,8 +1184,11 @@
         canvas.restore();
         canvas.restore();
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     SaveLayerUnclippedComplexTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(13, renderer.getIndex());
@@ -1098,14 +1239,17 @@
     OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
     *layerHandle = &layer;
 
-    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
+    auto syncedNode = TestUtils::getSyncedNode(node);
 
     // only enqueue partial damage
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
 
-    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            syncedNodeList, sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferLayers(layerUpdateQueue);
+    frameBuilder.deferRenderNode(*syncedNode);
+
     HwLayerSimpleTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(6, renderer.getIndex());
@@ -1202,14 +1346,17 @@
     OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
     *(parent->getLayerHandle()) = &parentLayer;
 
-    auto syncedList = TestUtils::createSyncedNodeList(parent);
+    auto syncedNode = TestUtils::getSyncedNode(parent);
 
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
 
-    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            syncedList, sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferLayers(layerUpdateQueue);
+    frameBuilder.deferRenderNode(*syncedNode);
+
     HwLayerComplexTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(14, renderer.getIndex());
@@ -1260,15 +1407,14 @@
     OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
     *layerHandle = &layer;
 
-    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
+    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
 
     // only enqueue partial damage
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
 
     // Draw, but pass empty node list, so no work is done for primary frame
-    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
-            sEmptyNodeList, sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance());
     BuildLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(3, renderer.getIndex());
@@ -1315,8 +1461,10 @@
         drawOrderedRect(&canvas, 8);
         drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
     });
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ZReorderTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex());
@@ -1406,8 +1554,10 @@
         canvas.restore();
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ProjectionReorderTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(3, renderer.getIndex());
@@ -1486,11 +1636,16 @@
     layer.setWindowTransform(windowTransform);
     *layerHandle = &layer;
 
-    auto syncedList = TestUtils::createSyncedNodeList(parent);
+    auto syncedNode = TestUtils::getSyncedNode(parent);
+
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
-    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            syncedList, sLightGeometry, Caches::getInstance());
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferLayers(layerUpdateQueue);
+    frameBuilder.deferRenderNode(*syncedNode);
+
     ProjectionHwLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(6, renderer.getIndex());
@@ -1545,8 +1700,10 @@
         canvas.drawRenderNode(child.get());
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ProjectionChildScrollTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
@@ -1588,8 +1745,10 @@
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ShadowTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
@@ -1632,9 +1791,10 @@
         canvas.restoreToCount(count);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(parent),
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
             (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ShadowSaveLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(6, renderer.getIndex());
@@ -1681,12 +1841,15 @@
     layer.setWindowTransform(windowTransform);
     *layerHandle = &layer;
 
-    auto syncedList = TestUtils::createSyncedNodeList(parent);
+    auto syncedNode = TestUtils::getSyncedNode(parent);
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
-    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            syncedList,
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
             (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
+    frameBuilder.deferLayers(layerUpdateQueue);
+    frameBuilder.deferRenderNode(*syncedNode);
+
     ShadowHwLayerTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
@@ -1713,10 +1876,10 @@
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
     });
-
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(parent),
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
             (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ShadowLayeringTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
@@ -1743,9 +1906,10 @@
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(parent),
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
             (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
     ShadowClippingTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
@@ -1772,8 +1936,10 @@
         canvas.drawRect(0, 0, 100, 100, paint);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     PropertyTestRenderer renderer(opValidateCallback);
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
@@ -1915,10 +2081,12 @@
         paint.setColor(SK_ColorWHITE);
         canvas.drawRect(0, 0, 10000, 10000, paint);
     });
-    auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
+    auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            nodes, sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+                sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*syncedNode);
+
     SaveLayerAlphaClipTestRenderer renderer(outObservedData);
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 
@@ -1991,8 +2159,10 @@
         canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
     });
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
-            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
     ClipReplaceTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index e2fc376..6148b33 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -26,7 +26,6 @@
 using namespace android;
 using namespace android::uirenderer;
 
-const LayerUpdateQueue sEmptyLayerUpdateQueue;
 const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50};
 const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
 
@@ -43,8 +42,9 @@
     RenderState& renderState = renderThread.renderState();
     Caches& caches = Caches::getInstance();
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+            sLightGeometery, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
     BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
 }
@@ -59,8 +59,9 @@
     RenderState& renderState = renderThread.renderState();
     Caches& caches = Caches::getInstance();
 
-    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance());
+    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+            sLightGeometery, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
     BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
 }
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 2cd9872..baa6b0f 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -135,7 +135,7 @@
         mContext = context;
         mServiceComponent = serviceComponent;
         mCallback = callback;
-        mRootHints = rootHints;
+        mRootHints = rootHints == null ? null : new Bundle(rootHints);
     }
 
     /**
@@ -444,7 +444,7 @@
             }
         };
         try {
-            mServiceBinder.getMediaItem(mediaId, receiver);
+            mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks);
         } catch (RemoteException e) {
             Log.i(TAG, "Remote error getting media item.");
             mHandler.post(new Runnable() {
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
index eef5a7c..6b6a561 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -20,5 +20,5 @@
     void addSubscription(String uri, in IBinder token, in Bundle options,
             IMediaBrowserServiceCallbacks callbacks);
     void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks);
-    void getMediaItem(String uri, in ResultReceiver cb);
+    void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
 }
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index ddc0e88..4b88926 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -97,6 +97,7 @@
     private @interface ResultFlags { }
 
     private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
+    private ConnectionRecord mCurConnection;
     private final Handler mHandler = new Handler();
     private ServiceBinder mBinder;
     MediaSession.Token mSession;
@@ -291,7 +292,8 @@
         }
 
         @Override
-        public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
+        public void getMediaItem(final String mediaId, final ResultReceiver receiver,
+                final IMediaBrowserServiceCallbacks callbacks) {
             if (TextUtils.isEmpty(mediaId) || receiver == null) {
                 return;
             }
@@ -299,7 +301,13 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    performLoadItem(mediaId, receiver);
+                    final IBinder b = callbacks.asBinder();
+                    ConnectionRecord connection = mConnections.get(b);
+                    if (connection == null) {
+                        Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
+                        return;
+                    }
+                    performLoadItem(mediaId, connection, receiver);
                 }
             });
         }
@@ -470,6 +478,20 @@
     }
 
     /**
+     * Gets the root hints sent from the currently connected {@link MediaBrowser}.
+     *
+     * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren}
+     *             or {@link #onLoadItem}
+     */
+    public final Bundle getBrowserRootHints() {
+        if (mCurConnection == null) {
+            throw new IllegalStateException("This should be called inside of onLoadChildren or"
+                    + " onLoadItem methods");
+        }
+        return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
+    }
+
+    /**
      * Notifies all connected media browsers that the children of
      * the specified parent id have changed in some way.
      * This will cause browsers to fetch subscribed content again.
@@ -619,11 +641,13 @@
             }
         };
 
+        mCurConnection = connection;
         if (options == null) {
             onLoadChildren(parentId, result);
         } else {
             onLoadChildren(parentId, result, options);
         }
+        mCurConnection = null;
 
         if (!result.isDone()) {
             throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
@@ -652,7 +676,8 @@
         return list.subList(fromIndex, toIndex);
     }
 
-    private void performLoadItem(String itemId, final ResultReceiver receiver) {
+    private void performLoadItem(String itemId, final ConnectionRecord connection,
+            final ResultReceiver receiver) {
         final Result<MediaBrowser.MediaItem> result =
                 new Result<MediaBrowser.MediaItem>(itemId) {
             @Override
@@ -663,7 +688,9 @@
             }
         };
 
-        MediaBrowserService.this.onLoadItem(itemId, result);
+        mCurConnection = connection;
+        onLoadItem(itemId, result);
+        mCurConnection = null;
 
         if (!result.isDone()) {
             throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 985fe3c..699f827 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -299,6 +299,11 @@
     <!-- [CHAR LIMIT=50] Title of the settings section that displays general preferences
          that are applicable to all engines, such as the speech rate -->
     <string name="tts_general_section_title">General</string>
+    <!-- On main TTS Settings screen, in default settings section,
+         reset speech pitch of synthesized voice to 1x speech pitch. [CHAR LIMIT=50] -->
+    <string name="tts_reset_speech_pitch_title">Reset speech pitch</string>
+    <!--On main TTS Settings screen, summary for reset speech pitch of synthesized voice [CHAR LIMIT=150] -->
+    <string name="tts_reset_speech_pitch_summary">Reset the pitch at which the text is spoken to default.</string>
 
     <!-- Default speech rate choices -->
     <string-array name="tts_rate_entries">
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 502eed1..346ae20 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -336,7 +336,7 @@
                     launchBugreportInfoDialog(id);
                     break;
                 case INTENT_BUGREPORT_SCREENSHOT:
-                    takeScreenshot(id, true);
+                    takeScreenshot(id);
                     break;
                 case INTENT_BUGREPORT_SHARE:
                     shareBugreport(id, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
@@ -417,8 +417,6 @@
             return true;
         }
         mProcesses.put(info.id, info);
-        // Take initial screenshot.
-        takeScreenshot(id, false);
         updateProgress(info);
         return true;
     }
@@ -635,19 +633,11 @@
     /**
      * Starting point for taking a screenshot.
      * <p>
-     * If {@code delayed} is set, it first display a toast message and waits
-     * {@link #SCREENSHOT_DELAY_SECONDS} seconds before taking it, otherwise it takes the screenshot
-     * right away.
-     * <p>
-     * Typical usage is delaying when taken from the notification action, and taking it right away
-     * upon receiving a {@link #INTENT_BUGREPORT_STARTED}.
+     * It first display a toast message and waits {@link #SCREENSHOT_DELAY_SECONDS} seconds before
+     * taking the screenshot.
      */
-    private void takeScreenshot(int id, boolean delayed) {
-        if (delayed) {
-            // Only logs screenshots requested from the notification action.
-            MetricsLogger.action(this,
-                    MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT);
-        }
+    private void takeScreenshot(int id) {
+        MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT);
         if (getInfo(id) == null) {
             // Most likely am killed Shell before user tapped the notification. Since system might
             // be too busy anwyays, it's better to ignore the notification and switch back to the
@@ -659,19 +649,15 @@
             return;
         }
         setTakingScreenshot(true);
-        if (delayed) {
-            collapseNotificationBar();
-            final String msg = mContext.getResources()
-                    .getQuantityString(com.android.internal.R.plurals.bugreport_countdown,
-                            SCREENSHOT_DELAY_SECONDS, SCREENSHOT_DELAY_SECONDS);
-            Log.i(TAG, msg);
-            // Show a toast just once, otherwise it might be captured in the screenshot.
-            Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
+        collapseNotificationBar();
+        final String msg = mContext.getResources()
+                .getQuantityString(com.android.internal.R.plurals.bugreport_countdown,
+                        SCREENSHOT_DELAY_SECONDS, SCREENSHOT_DELAY_SECONDS);
+        Log.i(TAG, msg);
+        // Show a toast just once, otherwise it might be captured in the screenshot.
+        Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
 
-            takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
-        } else {
-            takeScreenshot(id, 0);
-        }
+        takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
     }
 
     /**
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 537e4c5..f76fb26 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -216,7 +216,7 @@
         Bundle extras =
                 sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath, mScreenshotPath);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
-                NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
+                NAME, NO_TITLE, NO_DESCRIPTION, 0, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -266,7 +266,7 @@
 
         Bundle extras = acceptBugreportAndGetSharedIntent(ID);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
-                NAME, NO_TITLE, NO_DESCRIPTION, 2, RENAMED_SCREENSHOTS);
+                NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -283,6 +283,8 @@
         resetProperties();
 
         sendBugreportStarted(1000);
+        waitForScreenshotButtonEnabled(true);
+        takeScreenshot();
         sendBugreportFinished(ID, mPlainTextPath, NO_SCREENSHOT);
         waitShareNotification(ID);
 
@@ -340,7 +342,7 @@
         Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath,
                 mScreenshotPath);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
-                NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
+                NEW_NAME, TITLE, mDescription, 0, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -377,7 +379,7 @@
         Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID,
                 plainText? mPlainTextPath : mZipPath, mScreenshotPath);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
-                NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
+                NEW_NAME, TITLE, mDescription, 0, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -404,7 +406,7 @@
 
         Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mZipPath, mScreenshotPath);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
-                NO_NAME, NO_TITLE, mDescription, 1, DIDNT_RENAME_SCREENSHOTS);
+                NO_NAME, NO_TITLE, mDescription, 0, DIDNT_RENAME_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -449,7 +451,7 @@
         // title.txt and description.txt entries.
         extras = sendBugreportFinishedAndGetSharedIntent(ID2, mZipPath2, NO_SCREENSHOT);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, ID2, PID2, TITLE2,
-                NEW_NAME2, TITLE2, DESCRIPTION2, 1, RENAMED_SCREENSHOTS);
+                NEW_NAME2, TITLE2, DESCRIPTION2, 0, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
@@ -500,7 +502,7 @@
         // Finally, share bugreport.
         Bundle extras = acceptBugreportAndGetSharedIntent(ID);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
-                NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
+                NAME, TITLE, mDescription, 0, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
diff --git a/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml b/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml
new file mode 100644
index 0000000..dc8e629
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:shape="rectangle">
+
+    <solid
+            android:color="@color/recents_tv_card_background_color"/>
+    <corners
+            android:radius="@dimen/recents_tv_card_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
index d2ec52d..201f47d 100644
--- a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
+++ b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
@@ -30,7 +30,7 @@
             android:layout_height="@dimen/recents_tv_screenshot_height"
             android:gravity="center"
             android:orientation="vertical"
-            android:background="@color/recents_tv_card_background_color"
+            android:background="@drawable/recents_tv_card_thumbnail_background"
             android:layout_centerHorizontal="true" >
 
         <ImageView
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index c2de150..5fa802b 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -29,6 +29,7 @@
     <dimen name="recents_tv_icon_padding_bottom">8dip</dimen>
     <dimen name="recents_tv_text_padding_start">12dip</dimen>
     <dimen name="recents_tv_text_padding_bottom">12dip</dimen>
+    <dimen name="recents_tv_card_corner_radius">2dip</dimen>
 
     <!-- Padding for grid view in recents view on tv -->
     <dimen name="recents_tv_gird_row_top_margin">215dip</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index a72a7e6..235b782 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -21,6 +21,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Outline;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -30,6 +31,7 @@
 import android.view.Display;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.ViewOutlineProvider;
 import android.view.WindowManager;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -49,6 +51,7 @@
     private ImageView mBadgeView;
     private Task mTask;
     private boolean mDismissState;
+    private int mCornerRadius;
 
     private ViewFocusAnimator mViewFocusAnimator;
     private DismissAnimationsHolder mDismissAnimationsHolder;
@@ -77,6 +80,8 @@
         mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
         mDismissAnimationsHolder = new DismissAnimationsHolder(this);
         View title = findViewById(R.id.card_info_field);
+        mCornerRadius = getResources().getDimensionPixelSize(
+                R.dimen.recents_task_view_rounded_corners_radius);
         mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
         mViewFocusAnimator = new ViewFocusAnimator(this);
     }
@@ -272,13 +277,18 @@
 
     private void setAsScreenShotView(Bitmap screenshot, ImageView screenshotView) {
         LayoutParams lp = (LayoutParams) screenshotView.getLayoutParams();
-        lp.width = getResources()
-                .getDimensionPixelSize(R.dimen.recents_tv_card_width);
-        lp.height = getResources()
-                .getDimensionPixelSize(R.dimen.recents_tv_screenshot_height);
+        lp.width = LayoutParams.MATCH_PARENT;
+        lp.height = LayoutParams.MATCH_PARENT;
 
         screenshotView.setLayoutParams(lp);
         screenshotView.setImageBitmap(screenshot);
+        screenshotView.setClipToOutline(true);
+        screenshotView.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
+            }
+        });
     }
 
     private void setAsBannerView(Drawable banner, ImageView bannerView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index e25f9de..6e9de23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -41,6 +41,7 @@
 import android.widget.ImageView;
 import android.widget.RemoteViews;
 
+import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
@@ -103,6 +104,7 @@
     private int mMaxExpandHeight;
     private int mHeadsUpHeight;
     private View mVetoButton;
+    private int mNotificationColor;
     private boolean mClearable;
     private ExpansionLogger mLogger;
     private String mLoggingKey;
@@ -226,6 +228,7 @@
         mPrivateLayout.onNotificationUpdated(entry);
         mPublicLayout.onNotificationUpdated(entry);
         mShowingPublicInitialized = false;
+        updateNotificationColor();
         updateClearability();
         if (mIsSummaryWithChildren) {
             recreateNotificationHeader();
@@ -603,11 +606,12 @@
     }
 
     public int getNotificationColor() {
-        int color = getStatusBarNotification().getNotification().color;
-        if (color == Notification.COLOR_DEFAULT) {
-            return mContext.getColor(com.android.internal.R.color.notification_icon_default_color);
-        }
-        return color;
+        return mNotificationColor;
+    }
+
+    private void updateNotificationColor() {
+        mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext,
+                getStatusBarNotification().getNotification().color);
     }
 
     public HybridNotificationView getSingleLineView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index ee483e5..be98d7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -663,6 +663,11 @@
             addView(divider, index);
             mDividers.set(i, divider);
         }
+        removeView(mOverflowNumber);
+        mOverflowNumber = null;
+        mOverflowInvertHelper = null;
+        mGroupOverFlowState = null;
+        updateGroupOverflow();
     }
 
     public void setUserLocked(boolean userLocked) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 229a3f4..ec05dae 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2875,6 +2875,19 @@
     }
 
     @Override
+    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
+        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+        waitForReady();
+
+        try {
+            mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid),
+                    userId, flags);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
     public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
         try {
             final int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 820551d..9602bf6 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -714,20 +714,24 @@
             }
             handleRemoveListLocked();
         }
+
+        // Called only by Telecomm to communicate call state across different phone accounts. So
+        // there is no need to add a valid subId or slotId.
         broadcastCallStateChanged(state, incomingNumber,
+                SubscriptionManager.INVALID_PHONE_INDEX,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
     }
 
-    public void notifyCallStateForSubscriber(int subId, int state, String incomingNumber) {
+    public void notifyCallStateForPhoneId(int phoneId, int subId, int state,
+                String incomingNumber) {
         if (!checkNotifyPermission("notifyCallState()")) {
             return;
         }
         if (VDBG) {
-            log("notifyCallStateForSubscriber: subId=" + subId
+            log("notifyCallStateForPhoneId: subId=" + subId
                 + " state=" + state + " incomingNumber=" + incomingNumber);
         }
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mCallState[phoneId] = state;
                 mCallIncomingNumber[phoneId] = incomingNumber;
@@ -746,7 +750,7 @@
             }
             handleRemoveListLocked();
         }
-        broadcastCallStateChanged(state, incomingNumber, subId);
+        broadcastCallStateChanged(state, incomingNumber, phoneId, subId);
     }
 
     public void notifyServiceStateForPhoneId(int phoneId, int subId, ServiceState state) {
@@ -788,31 +792,27 @@
             }
             handleRemoveListLocked();
         }
-        broadcastServiceStateChanged(state, subId);
+        broadcastServiceStateChanged(state, phoneId, subId);
     }
 
-    public void notifySignalStrength(SignalStrength signalStrength) {
-        notifySignalStrengthForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                signalStrength);
-    }
-
-    public void notifySignalStrengthForSubscriber(int subId, SignalStrength signalStrength) {
+    public void notifySignalStrengthForPhoneId(int phoneId, int subId,
+                SignalStrength signalStrength) {
         if (!checkNotifyPermission("notifySignalStrength()")) {
             return;
         }
         if (VDBG) {
-            log("notifySignalStrengthForSubscriber: subId=" + subId
-                + " signalStrength=" + signalStrength);
-            toStringLogSSC("notifySignalStrengthForSubscriber");
+            log("notifySignalStrengthForPhoneId: subId=" + subId
+                +" phoneId=" + phoneId + " signalStrength=" + signalStrength);
+            toStringLogSSC("notifySignalStrengthForPhoneId");
         }
+
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
-                if (VDBG) log("notifySignalStrengthForSubscriber: valid phoneId=" + phoneId);
+                if (VDBG) log("notifySignalStrengthForPhoneId: valid phoneId=" + phoneId);
                 mSignalStrength[phoneId] = signalStrength;
                 for (Record r : mRecords) {
                     if (VDBG) {
-                        log("notifySignalStrengthForSubscriber: r=" + r + " subId=" + subId
+                        log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
                                 + " phoneId=" + phoneId + " ss=" + signalStrength);
                     }
                     if (r.matchPhoneStateListenerEvent(
@@ -820,7 +820,7 @@
                             idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG) {
-                                log("notifySignalStrengthForSubscriber: callback.onSsS r=" + r
+                                log("notifySignalStrengthForPhoneId: callback.onSsS r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
                                         + " ss=" + signalStrength);
                             }
@@ -835,7 +835,7 @@
                             int gsmSignalStrength = signalStrength.getGsmSignalStrength();
                             int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
                             if (DBG) {
-                                log("notifySignalStrengthForSubscriber: callback.onSS r=" + r
+                                log("notifySignalStrengthForPhoneId: callback.onSS r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
                                         + " gsmSS=" + gsmSignalStrength + " ss=" + ss);
                             }
@@ -846,11 +846,11 @@
                     }
                 }
             } else {
-                log("notifySignalStrengthForSubscriber: invalid phoneId=" + phoneId);
+                log("notifySignalStrengthForPhoneId: invalid phoneId=" + phoneId);
             }
             handleRemoveListLocked();
         }
-        broadcastSignalStrengthChanged(signalStrength, subId);
+        broadcastSignalStrengthChanged(signalStrength, phoneId, subId);
     }
 
     @Override
@@ -1347,7 +1347,7 @@
     // the legacy intent broadcasting
     //
 
-    private void broadcastServiceStateChanged(ServiceState state, int subId) {
+    private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
         long ident = Binder.clearCallingIdentity();
         try {
             mBatteryStats.notePhoneState(state.getState());
@@ -1363,10 +1363,12 @@
         intent.putExtras(data);
         // Pass the subscription along with the intent.
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+        intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
-    private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int subId) {
+    private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
+            int subId) {
         long ident = Binder.clearCallingIdentity();
         try {
             mBatteryStats.notePhoneSignalStrength(signalStrength);
@@ -1382,6 +1384,7 @@
         signalStrength.fillInNotifierBundle(data);
         intent.putExtras(data);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+        intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
@@ -1391,7 +1394,8 @@
      * can be {@code SubscriptionManager.INVALID_SUBSCRIPTION_ID}, in which case we send
      * a global state change broadcast ({@code TelephonyManager.ACTION_PHONE_STATE_CHANGED}).
      */
-    private void broadcastCallStateChanged(int state, String incomingNumber, int subId) {
+    private void broadcastCallStateChanged(int state, String incomingNumber, int phoneId,
+                int subId) {
         long ident = Binder.clearCallingIdentity();
         try {
             if (state == TelephonyManager.CALL_STATE_IDLE) {
@@ -1418,6 +1422,10 @@
             intent.setAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED);
             intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
         }
+        // If the phoneId is invalid, the broadcast is for overall call state.
+        if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
+            intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
+        }
 
         // Send broadcast twice, once for apps that have PRIVILEGED permission and once for those
         // that have the runtime one
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 40430b4..8653f1a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -97,6 +97,7 @@
 import android.app.IUidObserver;
 import android.app.IUserSwitchObserver;
 import android.app.Instrumentation;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -341,6 +342,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FORCE_NEW_TASK_FLAGS;
+import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -9742,7 +9744,7 @@
                 } else {
                     mStackSupervisor.resizeStackLocked(stackId, bounds, null /* tempTaskBounds */,
                             null /* tempTaskInsetBounds */, preserveWindows,
-                            allowResizeInDockedMode);
+                            allowResizeInDockedMode, !DEFER_RESUME);
                 }
             }
         } finally {
@@ -11471,20 +11473,13 @@
         }
 
         synchronized (this) {
-            if (mStackSupervisor.isFocusedUserLockedProfile()) {
+            if (mStackSupervisor.isUserLockedProfile(userId)) {
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     final int currentUserId = mUserController.getCurrentUserIdLocked();
-                    // Get the focused task before launching launcher.
-
                     if (mUserController.isLockScreenDisabled(currentUserId)) {
-
                         // If there is no device lock, we will show the profile's credential page.
-                        // startActivityFromRecentsInner is intercepted and will forward user to it.
-                        if (mFocusedActivity != null) {
-                            mStackSupervisor.startActivityFromRecentsInner(
-                                    mFocusedActivity.task.taskId, null);
-                        }
+                        mActivityStarter.showConfirmDeviceCredential(userId);
                     } else {
                         // Showing launcher to avoid user entering credential twice.
                         startHomeActivityLocked(currentUserId, "notifyLockedProfile");
@@ -17966,46 +17961,11 @@
         }
         synchronized (this) {
             final long origId = Binder.clearCallingIdentity();
-            final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
-            if (stack != null) {
-                mWindowManager.deferSurfaceLayout();
-                try {
-                    if (fromStackId == DOCKED_STACK_ID) {
-
-                        // We are moving all tasks from the docked stack to the fullscreen stack,
-                        // which is dismissing the docked stack, so resize all other stacks to
-                        // fullscreen here already so we don't end up with resize trashing.
-                        for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
-                            if (StackId.isResizeableByDockedStack(i)) {
-                                ActivityStack otherStack = mStackSupervisor.getStack(i);
-                                if (otherStack != null) {
-                                    mStackSupervisor.resizeStackLocked(i,
-                                            null, null, null, PRESERVE_WINDOWS,
-                                            true /* allowResizeInDockedMode */);
-                                }
-                            }
-                        }
-                    }
-                    final ArrayList<TaskRecord> tasks = stack.getAllTasks();
-                    final int size = tasks.size();
-                    if (onTop) {
-                        for (int i = 0; i < size; i++) {
-                            mStackSupervisor.moveTaskToStackLocked(tasks.get(i).taskId,
-                                    FULLSCREEN_WORKSPACE_STACK_ID, onTop, !FORCE_FOCUS,
-                                    "moveTasksToFullscreenStack", ANIMATE);
-                        }
-                    } else {
-                        for (int i = size - 1; i >= 0; i--) {
-                            mStackSupervisor.positionTaskInStackLocked(tasks.get(i).taskId,
-                                    FULLSCREEN_WORKSPACE_STACK_ID, 0);
-                        }
-                    }
-                } finally {
-                    mWindowManager.continueSurfaceLayout();
-                }
-
+            try {
+                mStackSupervisor.moveTasksToFullscreenStackLocked(fromStackId, onTop);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
-            Binder.restoreCallingIdentity(origId);
         }
     }
 
@@ -18227,7 +18187,7 @@
                     for (int stackId : resizedStacks) {
                         final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId);
                         mStackSupervisor.resizeStackLocked(
-                                stackId, newBounds, null, null, false, false);
+                                stackId, newBounds, null, null, false, false, !DEFER_RESUME);
                     }
                 }
             }
@@ -21006,6 +20966,13 @@
                 return mStackSupervisor.getTopVisibleActivities();
             }
         }
+
+        @Override
+        public void notifyDockedStackMinimizedChanged(boolean minimized) {
+            synchronized (ActivityManagerService.this) {
+                mStackSupervisor.setDockedStackMinimized(minimized);
+            }
+        }
     }
 
     private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6e2cebb..8f7f396 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import android.Manifest;
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -29,6 +30,8 @@
 import android.app.IActivityContainerCallback;
 import android.app.IActivityManager;
 import android.app.IActivityManager.WaitResult;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.app.StatusBarManager;
@@ -37,6 +40,7 @@
 import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -166,6 +170,7 @@
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -238,6 +243,9 @@
     // Restore task from the saved recents if it can't be found in any live stack.
     static final boolean RESTORE_FROM_RECENTS = true;
 
+    // Don't execute any calls to resume.
+    static final boolean DEFER_RESUME = true;
+
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
             new ArrayMap<>();
@@ -342,9 +350,6 @@
     /** Used on user changes */
     final ArrayList<UserState> mStartingUsers = new ArrayList<>();
 
-    /** Used to queue up any background users being started */
-    final ArrayList<UserState> mStartingBackgroundUsers = new ArrayList<>();
-
     /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
      * is being brought in front of us. */
     boolean mUserLeaving = false;
@@ -432,6 +437,20 @@
      */
     private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
 
+
+    /**
+     * If set to {@code false} all calls to resize the docked stack {@link #resizeDockedStackLocked}
+     * will be ignored. Useful for the case where the caller is handling resizing of other stack and
+     * moving tasks around and doesn't want dock stack to be resized due to an automatic trigger
+     * like the docked stack going empty.
+     */
+    private boolean mAllowDockedStackResize = true;
+
+    /**
+     * Is dock currently minimized.
+     */
+    boolean mIsDockMinimized;
+
     /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
@@ -716,10 +735,44 @@
         return null;
     }
 
-    boolean isFocusedUserLockedProfile() {
-        final int userId = mFocusedStack.topRunningActivityLocked().userId;
-        return userId != UserHandle.myUserId()
-                && mService.mUserController.shouldConfirmCredentials(userId);
+    /**
+     * TODO: Handle freefom mode.
+     * @return true when credential confirmation is needed for the user and there is any
+     *         activity started by the user in any visible stack.
+     */
+    boolean isUserLockedProfile(@UserIdInt int userId) {
+        if (!mService.mUserController.shouldConfirmCredentials(userId)) {
+            return false;
+        }
+        final ActivityStack fullScreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+        final ActivityStack dockedStack = getStack(DOCKED_STACK_ID);
+        final ActivityStack[] activityStacks = new ActivityStack[] {fullScreenStack, dockedStack};
+        for (final ActivityStack activityStack : activityStacks) {
+            if (activityStack == null) {
+                continue;
+            }
+            if (activityStack.topRunningActivityLocked() == null) {
+                continue;
+            }
+            if (activityStack.getStackVisibilityLocked(null) == STACK_INVISIBLE) {
+                continue;
+            }
+            if (activityStack.isDockedStack() && mIsDockMinimized) {
+                continue;
+            }
+            final TaskRecord topTask = activityStack.topTask();
+            if (topTask == null) {
+                continue;
+            }
+            // To handle the case that work app is in the task but just is not the top one.
+            for (int i = topTask.mActivities.size() - 1; i >= 0; i--) {
+                final ActivityRecord activityRecord = topTask.mActivities.get(i);
+                if (activityRecord.userId == userId) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     void setNextTaskIdForUserLocked(int taskId, int userId) {
@@ -1794,7 +1847,7 @@
                 if (StackId.resizeStackWithLaunchBounds(stackId)) {
                     resizeStackLocked(stackId, bounds,
                             null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                            !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */);
+                            !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME);
                 } else {
                     // WM resizeTask must be done after the task is moved to the correct stack,
                     // because Task's setBounds() also updates dim layer's bounds, but that has
@@ -1921,7 +1974,7 @@
     }
 
     void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
-            boolean preserveWindows, boolean allowResizeInDockedMode) {
+            boolean preserveWindows, boolean allowResizeInDockedMode, boolean deferResume) {
         if (stackId == DOCKED_STACK_ID) {
             resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
                     preserveWindows);
@@ -1943,7 +1996,10 @@
         mWindowManager.deferSurfaceLayout();
         try {
             resizeStackUncheckedLocked(stack, bounds, tempTaskBounds, tempTaskInsetBounds);
-            ensureConfigurationAndResume(stack, stack.topRunningActivityLocked(), preserveWindows);
+            if (!deferResume) {
+                ensureConfigurationAndResume(
+                        stack, stack.topRunningActivityLocked(), preserveWindows);
+            }
         } finally {
             mWindowManager.continueSurfaceLayout();
             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -2030,9 +2086,64 @@
         }
     }
 
+    void moveTasksToFullscreenStackLocked(int fromStackId, boolean onTop) {
+        final ActivityStack stack = getStack(fromStackId);
+        if (stack == null) {
+            return;
+        }
+
+        mWindowManager.deferSurfaceLayout();
+        try {
+            if (fromStackId == DOCKED_STACK_ID) {
+
+                // We are moving all tasks from the docked stack to the fullscreen stack,
+                // which is dismissing the docked stack, so resize all other stacks to
+                // fullscreen here already so we don't end up with resize trashing.
+                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
+                    if (StackId.isResizeableByDockedStack(i)) {
+                        ActivityStack otherStack = getStack(i);
+                        if (otherStack != null) {
+                            resizeStackLocked(i, null, null, null, PRESERVE_WINDOWS,
+                                    true /* allowResizeInDockedMode */, DEFER_RESUME);
+                        }
+                    }
+                }
+
+                // Also disable docked stack resizing since we have manually adjusted the
+                // size of other stacks above and we don't want to trigger a docked stack
+                // resize when we remove task from it below and it is detached from the
+                // display because it no longer contains any tasks.
+                mAllowDockedStackResize = false;
+            }
+            final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+            final int size = tasks.size();
+            if (onTop) {
+                for (int i = 0; i < size; i++) {
+                    moveTaskToStackLocked(tasks.get(i).taskId,
+                            FULLSCREEN_WORKSPACE_STACK_ID, onTop, !FORCE_FOCUS,
+                            "moveTasksToFullscreenStack", ANIMATE);
+                }
+            } else {
+                for (int i = size - 1; i >= 0; i--) {
+                    positionTaskInStackLocked(tasks.get(i).taskId,
+                            FULLSCREEN_WORKSPACE_STACK_ID, 0);
+                }
+            }
+        } finally {
+            mAllowDockedStackResize = true;
+            mWindowManager.continueSurfaceLayout();
+        }
+    }
+
     void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
             Rect tempDockedTaskInsetBounds,
             Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows) {
+
+        if (!mAllowDockedStackResize) {
+            // Docked stack resize currently disabled.
+            return;
+        }
+
         final ActivityStack stack = getStack(DOCKED_STACK_ID);
         if (stack == null) {
             Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
@@ -2042,6 +2153,8 @@
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
         mWindowManager.deferSurfaceLayout();
         try {
+            // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
+            mAllowDockedStackResize = false;
             ActivityRecord r = stack.topRunningActivityLocked();
             resizeStackUncheckedLocked(stack, dockedBounds, tempDockedTaskBounds,
                     tempDockedTaskInsetBounds);
@@ -2052,20 +2165,7 @@
                 // The dock stack either was dismissed or went fullscreen, which is kinda the same.
                 // In this case we make all other static stacks fullscreen and move all
                 // docked stack tasks to the fullscreen stack.
-                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
-                    if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
-                        resizeStackLocked(i, null, null, null, preserveWindows,
-                                true /* allowResizeInDockedMode */);
-                    }
-                }
-
-                ArrayList<TaskRecord> tasks = stack.getAllTasks();
-                final int count = tasks.size();
-                for (int i = 0; i < count; i++) {
-                    moveTaskToStackLocked(tasks.get(i).taskId,
-                            FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack",
-                            false /* animate */);
-                }
+                moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, ON_TOP);
 
                 // stack shouldn't contain anymore activities, so nothing to resume.
                 r = null;
@@ -2080,12 +2180,13 @@
                     if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
                         resizeStackLocked(i, tempRect, tempOtherTaskBounds,
                                 tempOtherTaskInsetBounds, preserveWindows,
-                                true /* allowResizeInDockedMode */);
+                                true /* allowResizeInDockedMode */, !DEFER_RESUME);
                     }
                 }
             }
             ensureConfigurationAndResume(stack, r, preserveWindows);
         } finally {
+            mAllowDockedStackResize = true;
             mWindowManager.continueSurfaceLayout();
             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
         }
@@ -2444,7 +2545,7 @@
             // animation bounds for the pinned stack to the desired bounds the caller wants.
             resizeStackLocked(PINNED_STACK_ID, task.mBounds, null /* tempTaskBounds */,
                     null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
-                    true /* allowResizeInDockedMode */);
+                    true /* allowResizeInDockedMode */, !DEFER_RESUME);
 
             if (task.mActivities.size() == 1) {
                 // There is only one activity in the task. So, we can just move the task over to
@@ -2487,8 +2588,6 @@
         stack.positionTask(task, position);
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
-        stack.ensureActivityConfigurationLocked(task.topRunningActivityLocked(), 0,
-                !PRESERVE_WINDOWS);
         stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeFocusedStackTopActivityLocked();
     }
@@ -3590,6 +3689,22 @@
         }
     }
 
+    void setDockedStackMinimized(boolean minimized) {
+        mIsDockMinimized = minimized;
+        if (minimized) {
+            // Docked stack is not visible, no need to confirm credentials for its top activity.
+            return;
+        }
+        final ActivityStack dockedStack = getStack(StackId.DOCKED_STACK_ID);
+        if (dockedStack == null) {
+            return;
+        }
+        final ActivityRecord top = dockedStack.topRunningActivityLocked();
+        if (top != null && mService.mUserController.shouldConfirmCredentials(top.userId)) {
+            mService.mActivityStarter.showConfirmDeviceCredential(top.userId);
+        }
+    }
+
     private final class ActivityStackSupervisorHandler extends Handler {
 
         public ActivityStackSupervisorHandler(Looper looper) {
@@ -4241,28 +4356,31 @@
                 ? new ActivityOptions(bOptions) : null;
         final int launchStackId = (activityOptions != null)
                 ? activityOptions.getLaunchStackId() : INVALID_STACK_ID;
-
         if (launchStackId == HOME_STACK_ID) {
             throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
                     + taskId + " can't be launch in the home stack.");
         }
+
+        if (launchStackId == DOCKED_STACK_ID) {
+            mWindowManager.setDockedStackCreateState(
+                    activityOptions.getDockCreateMode(), null /* initialBounds */);
+
+            // Defer updating the stack in which recents is until the app transition is done, to
+            // not run into issues where we still need to draw the task in recents but the
+            // docked stack is already created.
+            deferUpdateBounds(HOME_STACK_ID);
+            mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
+        }
+
         task = anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
         if (task == null) {
+            continueUpdateBounds(HOME_STACK_ID);
+            mWindowManager.executeAppTransition();
             throw new IllegalArgumentException(
                     "startActivityFromRecentsInner: Task " + taskId + " not found.");
         }
 
         if (launchStackId != INVALID_STACK_ID) {
-            if (launchStackId == DOCKED_STACK_ID) {
-                mWindowManager.setDockedStackCreateState(
-                        activityOptions.getDockCreateMode(), null /* initialBounds */);
-
-                // Defer updating the stack in which recents is until the app transition is done, to
-                // not run into issues where we still need to draw the task in recents but the
-                // docked stack is already created.
-                deferUpdateBounds(HOME_STACK_ID);
-                mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
-            }
             if (task.stack.mStackId != launchStackId) {
                 moveTaskToStackLocked(
                         taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
@@ -4290,12 +4408,12 @@
         intent = task.intent;
         intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
         userId = task.userId;
-            int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
-                    null, null, 0, 0, bOptions, userId, null, task);
-            if (launchStackId == DOCKED_STACK_ID) {
-                setResizingDuringAnimation(task.taskId);
-            }
-            return result;
+        int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
+                null, null, 0, 0, bOptions, userId, null, task);
+        if (launchStackId == DOCKED_STACK_ID) {
+            setResizingDuringAnimation(task.taskId);
+        }
+        return result;
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 785dd47..566d8d9 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -29,6 +29,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
+import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.IIntentSender;
@@ -76,6 +77,7 @@
     ActivityInfo mAInfo;
     String mResolvedType;
     TaskRecord mInTask;
+    ActivityOptions mActivityOptions;
 
     ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
         mService = service;
@@ -92,7 +94,7 @@
     }
 
     void intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
-            TaskRecord inTask, int callingPid, int callingUid) {
+            TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
         mUserManager = UserManager.get(mService.mContext);
         mIntent = intent;
         mCallingPid = callingPid;
@@ -101,6 +103,7 @@
         mAInfo = aInfo;
         mResolvedType = resolvedType;
         mInTask = inTask;
+        mActivityOptions = activityOptions;
         if (interceptSuspendPackageIfNeed()) {
             // Skip the rest of interceptions as the package is suspended by device admin so
             // no user action can undo this.
@@ -177,6 +180,12 @@
             mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId);
             mInTask = null;
         }
+        if (mActivityOptions == null) {
+            mActivityOptions = ActivityOptions.makeBasic();
+        }
+        // Showing credential confirmation activity in home task to avoid stopping multi-windowed
+        // mode after showing the full-screen credential confirmation activity.
+        mActivityOptions.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
 
         final UserInfo parent = mUserManager.getProfileParent(mUserId);
         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 4075230a..9c93f2c 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -82,9 +82,11 @@
 import android.app.IActivityContainer;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
+import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.ProfilerInfo;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -380,7 +382,8 @@
         }
 
         mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
-        mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid);
+        mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid,
+                options);
         intent = mInterceptor.mIntent;
         rInfo = mInterceptor.mRInfo;
         aInfo = mInterceptor.mAInfo;
@@ -388,7 +391,7 @@
         inTask = mInterceptor.mInTask;
         callingPid = mInterceptor.mCallingPid;
         callingUid = mInterceptor.mCallingUid;
-
+        options = mInterceptor.mActivityOptions;
         if (abort) {
             if (resultRecord != null) {
                 resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
@@ -589,6 +592,53 @@
         }
     }
 
+    void showConfirmDeviceCredential(int userId) {
+        // First, retrieve the stack that we want to resume after credential is confirmed.
+        ActivityStack targetStack;
+        ActivityStack fullscreenStack =
+                mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+        if (fullscreenStack != null &&
+                fullscreenStack.getStackVisibilityLocked(null) != ActivityStack.STACK_INVISIBLE) {
+            // Single window case and the case that the docked stack is shown with fullscreen stack.
+            targetStack = fullscreenStack;
+        } else {
+            // The case that the docked stack is shown with recent.
+            targetStack = mSupervisor.getStack(HOME_STACK_ID);
+        }
+        if (targetStack == null) {
+            return;
+        }
+        final KeyguardManager km = (KeyguardManager) mService.mContext
+                .getSystemService(Context.KEYGUARD_SERVICE);
+        final Intent credential =
+                km.createConfirmDeviceCredentialIntent(null, null, userId);
+        credential.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+                Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
+        final ActivityRecord activityRecord = targetStack.topRunningActivityLocked();
+        if (activityRecord != null) {
+            final IIntentSender target = mService.getIntentSenderLocked(
+                    ActivityManager.INTENT_SENDER_ACTIVITY,
+                    activityRecord.launchedFromPackage,
+                    activityRecord.launchedFromUid,
+                    activityRecord.userId,
+                    null, null, 0,
+                    new Intent[] { activityRecord.intent },
+                    new String[] { activityRecord.resolvedType },
+                    PendingIntent.FLAG_CANCEL_CURRENT |
+                            PendingIntent.FLAG_ONE_SHOT |
+                            PendingIntent.FLAG_IMMUTABLE,
+                    null);
+            credential.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+            // Show confirm credentials activity.
+            mService.mContext.startActivityAsUser(credential, options.toBundle(),
+                    UserHandle.CURRENT);
+        }
+    }
+
+
     final int startActivityMayWait(IApplicationThread caller, int callingUid,
             String callingPackage, Intent intent, String resolvedType,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 2dafa3e..c770620 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -222,7 +222,7 @@
             removeClient(client);
         }
         if (DEBUG) Slog.v(TAG, "handleError(client="
-                + client != null ? client.getOwnerString() : "null" + ", error = " + error + ")");
+                + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
         // This is the magic code that starts the next client when the old client finishes.
         if (error == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
             mHandler.removeCallbacks(mResetClientState);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index a11ee74..66c1a53 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -174,12 +174,13 @@
         mInstaller.execute("destroy_app_profiles", pkgName);
     }
 
-    public void createUserConfig(int userid) throws InstallerException {
-        mInstaller.execute("mkuserconfig", userid);
+    public void createUserData(String uuid, int userId, int userSerial, int flags)
+            throws InstallerException {
+        mInstaller.execute("create_user_data", uuid, userId, userSerial, flags);
     }
 
-    public void removeUserDataDirs(String uuid, int userid) throws InstallerException {
-        mInstaller.execute("rmuser", uuid, userid);
+    public void destroyUserData(String uuid, int userId, int flags) throws InstallerException {
+        mInstaller.execute("destroy_user_data", uuid, userId, flags);
     }
 
     public void markBootComplete(String instructionSet) throws InstallerException {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index dc81c65..4819de5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -109,7 +109,6 @@
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SecurityLog;
 import android.app.backup.IBackupManager;
-import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -13420,14 +13419,17 @@
             final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
                     move.dataAppName);
             Slog.d(TAG, "Cleaning up " + move.packageName + " on " + volumeUuid);
+            final int[] userIds = sUserManager.getUserIds();
             synchronized (mInstallLock) {
                 // Clean up both app data and code
                 // All package moves are frozen until finished
-                try {
-                    mInstaller.destroyAppData(volumeUuid, move.packageName, UserHandle.USER_ALL,
-                            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
-                } catch (InstallerException e) {
-                    Slog.w(TAG, String.valueOf(e));
+                for (int userId : userIds) {
+                    try {
+                        mInstaller.destroyAppData(volumeUuid, move.packageName, userId,
+                                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, String.valueOf(e));
+                    }
                 }
                 removeCodePathLI(codeFile);
             }
@@ -17421,6 +17423,10 @@
                 return true;
             }
         });
+
+        // Now that we're mostly running, clean up stale users and apps
+        reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
+        reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
     }
 
     @Override
@@ -18653,14 +18659,100 @@
     }
 
     /**
+     * Prepare storage areas for given user on all mounted devices.
+     */
+    void prepareUserData(int userId, int userSerial, int flags) {
+        synchronized (mInstallLock) {
+            final StorageManager storage = mContext.getSystemService(StorageManager.class);
+            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+                final String volumeUuid = vol.getFsUuid();
+                prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
+            }
+        }
+    }
+
+    private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
+            boolean allowRecover) {
+        // Prepare storage and verify that serial numbers are consistent; if
+        // there's a mismatch we need to destroy to avoid leaking data
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        try {
+            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
+
+            if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
+                UserManagerService.enforceSerialNumber(
+                        Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
+            }
+            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+                UserManagerService.enforceSerialNumber(
+                        Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
+            }
+
+            synchronized (mInstallLock) {
+                mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
+            }
+        } catch (Exception e) {
+            logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
+                    + " because we failed to prepare: " + e);
+            destroyUserDataLI(volumeUuid, userId, flags);
+
+            if (allowRecover) {
+                // Try one last time; if we fail again we're really in trouble
+                prepareUserDataLI(volumeUuid, userId, userSerial, flags, false);
+            }
+        }
+    }
+
+    /**
+     * Destroy storage areas for given user on all mounted devices.
+     */
+    void destroyUserData(int userId, int flags) {
+        synchronized (mInstallLock) {
+            final StorageManager storage = mContext.getSystemService(StorageManager.class);
+            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+                final String volumeUuid = vol.getFsUuid();
+                destroyUserDataLI(volumeUuid, userId, flags);
+            }
+        }
+    }
+
+    private void destroyUserDataLI(String volumeUuid, int userId, int flags) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        try {
+            // Clean up app data, profile data, and media data
+            mInstaller.destroyUserData(volumeUuid, userId, flags);
+
+            // Clean up system data
+            if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
+                if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
+                    FileUtils.deleteContentsAndDir(Environment.getUserSystemDirectory(userId));
+                    FileUtils.deleteContentsAndDir(Environment.getDataSystemDeDirectory(userId));
+                }
+                if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+                    FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
+                }
+            }
+
+            // Data with special labels is now gone, so finish the job
+            storage.destroyUserStorage(volumeUuid, userId, flags);
+
+        } catch (Exception e) {
+            logCriticalInfo(Log.WARN,
+                    "Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
+        }
+    }
+
+    /**
      * Examine all users present on given mounted volume, and destroy data
      * belonging to users that are no longer valid, or whose user ID has been
      * recycled.
      */
     private void reconcileUsers(String volumeUuid) {
-        // TODO: also reconcile DE directories
-        final File[] files = FileUtils
-                .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid));
+        final List<File> files = new ArrayList<>();
+        Collections.addAll(files, FileUtils
+                .listFilesOrEmpty(Environment.getDataUserDeDirectory(volumeUuid)));
+        Collections.addAll(files, FileUtils
+                .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid)));
         for (File file : files) {
             if (!file.isDirectory()) continue;
 
@@ -18691,11 +18783,8 @@
 
             if (destroyUser) {
                 synchronized (mInstallLock) {
-                    try {
-                        mInstaller.removeUserDataDirs(volumeUuid, userId);
-                    } catch (InstallerException e) {
-                        Slog.w(TAG, "Failed to clean up user dirs", e);
-                    }
+                    destroyUserDataLI(volumeUuid, userId,
+                            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
                 }
             }
         }
@@ -19490,21 +19579,7 @@
             mSettings.removeUserLPw(userHandle);
             mPendingBroadcasts.remove(userHandle);
             mEphemeralApplicationRegistry.onUserRemovedLPw(userHandle);
-        }
-        synchronized (mInstallLock) {
-            final StorageManager storage = mContext.getSystemService(StorageManager.class);
-            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
-                final String volumeUuid = vol.getFsUuid();
-                if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
-                try {
-                    mInstaller.removeUserDataDirs(volumeUuid, userHandle);
-                } catch (InstallerException e) {
-                    Slog.w(TAG, "Failed to remove user data", e);
-                }
-            }
-            synchronized (mPackages) {
-                removeUnusedPackagesLILPw(userManager, userHandle);
-            }
+            removeUnusedPackagesLPw(userManager, userHandle);
         }
     }
 
@@ -19513,7 +19588,7 @@
      * that are no longer in use by any other user.
      * @param userHandle the user being removed
      */
-    private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
+    private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) {
         final boolean DEBUG_CLEAN_APKS = false;
         int [] users = userManager.getUserIds();
         Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
@@ -19563,11 +19638,6 @@
     /** Called by UserManagerService */
     void createNewUser(int userHandle) {
         synchronized (mInstallLock) {
-            try {
-                mInstaller.createUserConfig(userHandle);
-            } catch (InstallerException e) {
-                Slog.w(TAG, "Failed to create user config", e);
-            }
             mSettings.createNewUserLI(this, mInstaller, userHandle);
         }
         synchronized (mPackages) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 9b918f3..42f7166 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -65,7 +65,6 @@
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -2060,7 +2059,7 @@
             }
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
             storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
-            prepareUserStorage(userId, userInfo.serialNumber,
+            mPm.prepareUserData(userId, userInfo.serialNumber,
                     StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
             mPm.createNewUser(userId);
             userInfo.partial = false;
@@ -2300,9 +2299,9 @@
             Slog.i(LOG_TAG,
                 "Destroying key for user " + userHandle + " failed, continuing anyway", e);
         }
+
         // Cleanup package manager settings
         mPm.cleanUpUser(this, userHandle);
-
         // Remove this user from the list
         synchronized (mUsersLock) {
             mUsers.remove(userHandle);
@@ -2322,24 +2321,12 @@
         AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
         userFile.delete();
         updateUserIds();
-        File userDir = Environment.getUserSystemDirectory(userHandle);
-        File renamedUserDir = Environment.getUserSystemDirectory(UserHandle.USER_NULL - userHandle);
-        if (userDir.renameTo(renamedUserDir)) {
-            removeDirectoryRecursive(renamedUserDir);
-        } else {
-            removeDirectoryRecursive(userDir);
-        }
-    }
 
-    private void removeDirectoryRecursive(File parent) {
-        if (parent.isDirectory()) {
-            String[] files = parent.list();
-            for (String filename : files) {
-                File child = new File(parent, filename);
-                removeDirectoryRecursive(child);
-            }
-        }
-        parent.delete();
+        // Now that we've purged all the metadata above, destroy the actual data
+        // on disk; if we battery pull in here we'll finish cleaning up when
+        // reconciling after reboot.
+        mPm.destroyUserData(userHandle,
+                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
     }
 
     private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) {
@@ -2634,23 +2621,12 @@
     }
 
     /**
-     * Prepare storage areas for given user on all mounted devices.
-     */
-    private void prepareUserStorage(int userId, int userSerial, int flags) {
-        final StorageManager storage = mContext.getSystemService(StorageManager.class);
-        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
-            final String volumeUuid = vol.getFsUuid();
-            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
-        }
-    }
-
-    /**
      * Called right before a user is started. This gives us a chance to prepare
      * app storage and apply any user restrictions.
      */
     public void onBeforeStartUser(int userId) {
         final int userSerial = getUserSerialNumber(userId);
-        prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
+        mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
         mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE);
 
         if (userId != UserHandle.USER_SYSTEM) {
@@ -2666,7 +2642,7 @@
      */
     public void onBeforeUnlockUser(@UserIdInt int userId) {
         final int userSerial = getUserSerialNumber(userId);
-        prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+        mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
         mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE);
     }
 
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 49ff385..890a715 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -19,10 +19,15 @@
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Binder;
@@ -44,6 +49,7 @@
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.server.SystemConfig;
 import com.android.server.SystemService;
 import com.android.server.utils.ManagedApplicationService.PendingEvent;
 import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
@@ -56,6 +62,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
@@ -85,6 +92,8 @@
 
     public static final String VR_MANAGER_BINDER_SERVICE = "vrmanager";
 
+    private static final int PENDING_STATE_DELAY_MS = 300;
+
     private static native void initializeNative();
     private static native void setVrModeNative(boolean enabled);
 
@@ -107,8 +116,10 @@
     private String mPreviousNotificationPolicyAccessPackage;
     private String mPreviousCoarseLocationPackage;
     private String mPreviousManageOverlayPackage;
+    private VrState mPendingState;
 
     private static final int MSG_VR_STATE_CHANGE = 0;
+    private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -127,12 +138,32 @@
                     }
                     mRemoteCallbacks.finishBroadcast();
                 } break;
+                case MSG_PENDING_VR_STATE_CHANGE : {
+                    synchronized(mLock) {
+                        VrManagerService.this.consumeAndApplyPendingStateLocked();
+                    }
+                } break;
                 default :
                     throw new IllegalStateException("Unknown message type: " + msg.what);
             }
         }
     };
 
+    private static class VrState {
+        final boolean enabled;
+        final int userId;
+        final ComponentName targetPackageName;
+        final ComponentName callingPackage;
+
+        VrState(boolean enabled, ComponentName targetPackageName, int userId,
+                ComponentName callingPackage) {
+            this.enabled = enabled;
+            this.userId = userId;
+            this.targetPackageName = targetPackageName;
+            this.callingPackage = callingPackage;
+        }
+    };
+
     private static final BinderChecker sBinderChecker = new BinderChecker() {
         @Override
         public IInterface asInterface(IBinder binder) {
@@ -156,6 +187,13 @@
                 return; // No active services
             }
 
+            // If there is a pending state change, we'd better deal with that first
+            consumeAndApplyPendingStateLocked();
+
+            if (mCurrentVrService == null) {
+                return; // No active services
+            }
+
             // There is an active service, update it if needed
             updateCurrentVrServiceLocked(mVrModeEnabled, mCurrentVrService.getComponent(),
                     mCurrentVrService.getUserId(), null);
@@ -258,6 +296,93 @@
 
         publishLocalService(VrManagerInternal.class, new LocalService());
         publishBinderService(VR_MANAGER_BINDER_SERVICE, mVrManager.asBinder());
+
+        // If there are no VR packages installed on the device, then disable VR
+        // components, otherwise, enable them.
+        setEnabledStatusOfVrComponents();
+    }
+
+    private void setEnabledStatusOfVrComponents() {
+        ArraySet<ComponentName> vrComponents = SystemConfig.getInstance().getDefaultVrComponents();
+        if (vrComponents == null) {
+           return;
+        }
+
+        // We only want to enable VR components if there is a VR package installed on the device.
+        // The VR components themselves do not quality as a VR package, so exclude them.
+        ArraySet<String> vrComponentPackageNames = new ArraySet<>();
+        for (ComponentName componentName : vrComponents) {
+            vrComponentPackageNames.add(componentName.getPackageName());
+        }
+
+        // Check to see if there are any packages on the device, other than the VR component
+        // packages.
+        PackageManager pm = mContext.getPackageManager();
+        List<PackageInfo> packageInfos = pm.getInstalledPackages(
+                PackageManager.GET_CONFIGURATIONS);
+        boolean vrModeIsUsed = false;
+        for (PackageInfo packageInfo : packageInfos) {
+            if (packageInfo != null && packageInfo.packageName != null &&
+                    pm.getApplicationEnabledSetting(packageInfo.packageName) ==
+                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+                vrModeIsUsed = enableVrComponentsIfVrModeUsed(pm, packageInfo,
+                        vrComponentPackageNames, vrComponents);
+                if (vrModeIsUsed) {
+                    break;
+                }
+            }
+        }
+
+        if (!vrModeIsUsed) {
+            Slog.i(TAG, "No VR packages found, disabling VR components");
+            for (ComponentName componentName : vrComponents) {
+                pm.setApplicationEnabledSetting(componentName.getPackageName(),
+                        PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+            }
+
+            // Register to receive an intent when a new package is installed, in case that package
+            // requires VR components.
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+            intentFilter.addDataScheme("package");
+            mContext.registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    PackageManager pm = context.getPackageManager();
+                    final String packageName = intent.getData().getSchemeSpecificPart();
+                    if (packageName != null) {
+                        try {
+                            PackageInfo packageInfo = pm.getPackageInfo(packageName,
+                                    PackageManager.GET_CONFIGURATIONS);
+                            enableVrComponentsIfVrModeUsed(pm, packageInfo,
+                                    vrComponentPackageNames, vrComponents);
+                        } catch (NameNotFoundException e) {
+                        }
+                    }
+                };
+            }, intentFilter);
+        }
+    }
+
+    private boolean enableVrComponentsIfVrModeUsed(PackageManager pm, PackageInfo packageInfo,
+            ArraySet<String> vrComponentPackageNames, ArraySet<ComponentName> vrComponents) {
+        boolean isVrComponent = vrComponents != null &&
+                vrComponentPackageNames.contains(packageInfo.packageName);
+        if (packageInfo != null && packageInfo.reqFeatures != null && !isVrComponent) {
+            for (FeatureInfo featureInfo : packageInfo.reqFeatures) {
+                if (featureInfo.name != null &&
+                    (featureInfo.name.equals(PackageManager.FEATURE_VR_MODE) ||
+                     featureInfo.name.equals(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE))) {
+                    Slog.i(TAG, "VR package found, enabling VR components");
+                    for (ComponentName componentName : vrComponents) {
+                        pm.setApplicationEnabledSetting(componentName.getPackageName(),
+                                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+                    }
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     @Override
@@ -679,14 +804,40 @@
                 sBinderChecker);
     }
 
+    private void consumeAndApplyPendingStateLocked() {
+        if (mPendingState != null) {
+            updateCurrentVrServiceLocked(mPendingState.enabled,
+                    mPendingState.targetPackageName, mPendingState.userId,
+                    mPendingState.callingPackage);
+            mPendingState = null;
+        }
+    }
+
     /*
      * Implementation of VrManagerInternal calls.  These are callable from system services.
      */
 
-    private boolean setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
+    private void setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
             int userId, @NonNull ComponentName callingPackage) {
+
         synchronized (mLock) {
-            return updateCurrentVrServiceLocked(enabled, targetPackageName, userId, callingPackage);
+
+            if (!enabled && mCurrentVrService != null) {
+                // If we're transitioning out of VR mode, delay briefly to avoid expensive HAL calls
+                // and service bind/unbind in case we are immediately switching to another VR app.
+                if (mPendingState == null) {
+                    mHandler.sendEmptyMessageDelayed(MSG_PENDING_VR_STATE_CHANGE,
+                            PENDING_STATE_DELAY_MS);
+                }
+
+                mPendingState = new VrState(enabled, targetPackageName, userId, callingPackage);
+                return;
+            } else {
+                mHandler.removeMessages(MSG_PENDING_VR_STATE_CHANGE);
+                mPendingState = null;
+            }
+
+            updateCurrentVrServiceLocked(enabled, targetPackageName, userId, callingPackage);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index c240d07..52146c2 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -149,8 +149,10 @@
             if (state.animator.mWin.mAppToken == null && !dimLayerUser.isFullscreen()) {
                 // Dim should cover the entire screen for system windows.
                 mDisplayContent.getLogicalDisplayRect(mTmpBounds);
-                state.dimLayer.setBounds(mTmpBounds);
+            } else {
+                dimLayerUser.getDimBounds(mTmpBounds);
             }
+            state.dimLayer.setBounds(mTmpBounds);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 3bd7a96..0039c0a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -27,6 +27,7 @@
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -84,9 +85,12 @@
 
     private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
 
+    private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
+
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
     private int mDividerWindowWidth;
+    private int mDividerWindowWidthInactive;
     private int mDividerInsets;
     private boolean mResizing;
     private WindowState mWindow;
@@ -130,6 +134,8 @@
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
         mDividerInsets = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_insets);
+        mDividerWindowWidthInactive = WindowManagerService.dipToPixel(
+                DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics());
     }
 
     void onConfigurationChanged() {
@@ -148,6 +154,10 @@
         return mDividerInsets;
     }
 
+    int getContentWidthInactive() {
+        return mDividerWindowWidthInactive;
+    }
+
     void setResizing(boolean resizing) {
         if (mResizing != resizing) {
             mResizing = resizing;
@@ -276,6 +286,9 @@
     }
 
     void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
+        mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
+        mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
+                minimizedDock ? 1 : 0, 0).sendToTarget();
         final int size = mDockedStackListeners.beginBroadcast();
         for (int i = 0; i < size; ++i) {
             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
@@ -355,6 +368,12 @@
             return;
         }
 
+        // If the app that having visibility change is not the top visible one in the task,
+        // it does not affect whether the docked stack is minimized, ignore it.
+        if (task.getTopVisibleAppToken() == null || task.getTopVisibleAppToken() != wtoken) {
+            return;
+        }
+
         // If the stack is completely offscreen, this might just be an intermediate state when
         // docking a task/launching recents at the same time, but home doesn't actually get
         // visible after the state settles in.
@@ -644,4 +663,4 @@
     public String toShortString() {
         return TAG;
     }
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index c322cd8..872bc6d 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -53,6 +53,12 @@
 
 public class TaskStack implements DimLayer.DimLayerUser,
         BoundsAnimationController.AnimateBoundsUser {
+    /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
+     * restrict IME adjustment so that a min portion of top stack remains visible.*/
+    private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
+
+    /** Dimming amount for non-focused stack when stacks are IME-adjusted. */
+    private static final float IME_ADJUST_DIM_AMOUNT = 0.25f;
 
     /** Unique identifier */
     final int mStackId;
@@ -249,7 +255,12 @@
                 task.scrollLocked(mTmpRect2);
             } else if (task.isResizeable() && task.mOverrideConfig != Configuration.EMPTY) {
                 task.getBounds(mTmpRect2);
-                mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
+                if (mAdjustedForIme && getDockSide() == DOCKED_TOP) {
+                    int offsetY = adjustedBounds.bottom - mTmpRect2.bottom;
+                    mTmpRect2.offset(0, offsetY);
+                } else {
+                    mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
+                }
                 task.setTempInsetBounds(tempInsetBounds);
                 task.resizeLocked(mTmpRect2, task.mOverrideConfig, false /* forced */);
             }
@@ -882,6 +893,7 @@
             mImeGoingAway = false;
             mAdjustImeAmount = 0f;
             updateAdjustedBounds();
+            mService.setResizeDimLayer(false, mStackId, 1.0f);
         } else {
             mImeGoingAway |= mAdjustedForIme;
         }
@@ -930,6 +942,11 @@
         }
     }
 
+    int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
+        return displayContentRect.top + (int)
+                ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
+    }
+
     private boolean adjustForIME(final WindowState imeWin) {
         final int dockedSide = getDockSide();
         final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
@@ -953,23 +970,41 @@
         mLastContentBounds.set(contentBounds);
         final int yOffset = displayContentRect.bottom - contentBounds.bottom;
 
+        final int dividerWidth =
+                getDisplayContent().mDividerControllerLocked.getContentWidth();
+        final int dividerWidthInactive =
+                getDisplayContent().mDividerControllerLocked.getContentWidthInactive();
+
         if (dockedSide == DOCKED_TOP) {
             // If this stack is docked on top, we make it smaller so the bottom stack is not
-            // occluded by IME. We shift its bottom up by the height of the IME (capped by
-            // the display content rect). Note that we don't change the task bounds.
-            int bottom = Math.max(
-                    mBounds.bottom - yOffset, displayContentRect.top);
+            // occluded by IME. We shift its bottom up by the height of the IME, but
+            // leaves at least 30% of the top stack visible.
+            final int minTopStackBottom =
+                    getMinTopStackBottom(displayContentRect, mBounds.bottom);
+            final int bottom = Math.max(
+                    mBounds.bottom - yOffset + dividerWidth - dividerWidthInactive,
+                    minTopStackBottom);
             mTmpAdjustedBounds.set(mBounds);
             mTmpAdjustedBounds.bottom =
                     (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom);
             mFullyAdjustedImeBounds.set(mBounds);
         } else {
-            // If this stack is docked on bottom, we shift it up so that it's not occluded by
-            // IME. We try to move it up by the height of the IME window (although the best
-            // we could do is to make the top stack fully collapsed).
-            final int dividerWidth = getDisplayContent().mDividerControllerLocked
-                    .getContentWidth();
-            int top = Math.max(mBounds.top - yOffset, displayContentRect.top + dividerWidth);
+            final int top;
+            final boolean isFocusedStack = mService.getFocusedStackLocked() == this;
+            if (isFocusedStack) {
+                // If this stack is docked on bottom and has focus, we shift it up so that it's not
+                // occluded by IME. We try to move it up by the height of the IME window, but only
+                // to the extent that leaves at least 30% of the top stack visible.
+                final int minTopStackBottom =
+                        getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth);
+                top = Math.max(
+                        mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive);
+            } else {
+                // If this stack is docked on bottom but doesn't have focus, we don't need to adjust
+                // for IME, but still need to apply a small adjustment due to the thinner divider.
+                top = mBounds.top - dividerWidth + dividerWidthInactive;
+            }
+
             mTmpAdjustedBounds.set(mBounds);
             mTmpAdjustedBounds.top =
                     (int) (mAdjustImeAmount * top + (1 - mAdjustImeAmount) * mBounds.top);
@@ -1043,6 +1078,12 @@
             mLastContentBounds.setEmpty();
         }
         setAdjustedBounds(mTmpAdjustedBounds);
+
+        final boolean isFocusedStack = mService.getFocusedStackLocked() == this;
+        if (mAdjustedForIme && adjust && !isFocusedStack) {
+            final float alpha = mAdjustImeAmount * IME_ADJUST_DIM_AMOUNT;
+            mService.setResizeDimLayer(true, mStackId, alpha);
+        }
     }
 
     boolean isAdjustedForMinimizedDockedStack() {
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 0217c09..ed1f428 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -203,6 +203,10 @@
         if (mDockDivider != null && mDockDivider.isVisibleLw()
                 && mService.mInputMethodWindow != null) {
             layer = assignAndIncreaseLayerIfNeeded(mService.mInputMethodWindow, layer);
+            for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
+                final WindowState dialog = mService.mInputMethodDialogs.get(i);
+                layer = assignAndIncreaseLayerIfNeeded(dialog, layer);
+            }
         }
 
         // We know that we will be animating a relaunching window in the near future, which will
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2e0c187..875ad92 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7445,17 +7445,16 @@
 
     void adjustForImeIfNeeded(final DisplayContent displayContent) {
         final WindowState imeWin = mInputMethodWindow;
-        final TaskStack focusedStack =
-                mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+        final TaskStack focusedStack = getFocusedStackLocked();
         final boolean dockVisible = isStackVisibleLocked(DOCKED_STACK_ID);
         if (imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
-                && dockVisible
-                && focusedStack != null
-                && focusedStack.getDockSide() == DOCKED_BOTTOM){
+                && dockVisible && focusedStack != null) {
+            final boolean isFocusOnBottom = focusedStack.getDockSide() == DOCKED_BOTTOM;
             final ArrayList<TaskStack> stacks = displayContent.getStacks();
             for (int i = stacks.size() - 1; i >= 0; --i) {
                 final TaskStack stack = stacks.get(i);
-                if (stack.isVisibleLocked()) {
+                final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
+                if (stack.isVisibleLocked() && (isFocusOnBottom || isDockedOnBottom)) {
                     stack.setAdjustedForIme(imeWin);
                 }
             }
@@ -7599,6 +7598,10 @@
         return mCurrentFocus;
     }
 
+    TaskStack getFocusedStackLocked() {
+        return mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+    }
+
     private void showAuditSafeModeNotification() {
         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
                 new Intent(Intent.ACTION_VIEW,
@@ -7800,6 +7803,8 @@
         public static final int UPDATE_ANIMATION_SCALE = 51;
         public static final int WINDOW_REMOVE_TIMEOUT = 52;
 
+        public static final int NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED = 53;
+
         /**
          * Used to denote that an integer field in a message will not be used.
          */
@@ -8423,6 +8428,10 @@
                     }
                 }
                 break;
+                case NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED: {
+                    mAmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG_WM, "handleMessage: exit");
@@ -9333,6 +9342,7 @@
         WindowState newFocus = computeFocusedWindowLocked();
         if (mCurrentFocus != newFocus) {
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
+            TaskStack oldFocusedStack = getFocusedStackLocked();
             // This check makes sure that we don't already have the focus
             // change message pending.
             mH.removeMessages(H.REPORT_FOCUS_CHANGE);
@@ -9354,6 +9364,7 @@
             mLosingFocus.remove(newFocus);
 
             int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
+            TaskStack newFocusedStack = getFocusedStackLocked();
 
             if (imWindowChanged && oldFocus != mInputMethodWindow) {
                 // Focus of the input method window changed. Perform layout if needed.
@@ -9383,6 +9394,20 @@
                 mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
             }
 
+            // TODO: Reset and re-apply IME adjustment if needed when stack focus changed.
+            // This makes sure divider starts an animation from pre-adjust position to final
+            // position. Ideally we want to skip the reset and animation from current position
+            // directly to final position.
+            final WindowState imeWin = mInputMethodWindow;
+            if (oldFocusedStack != null) {
+                oldFocusedStack.resetAdjustedForIme(true);
+            }
+            if (newFocusedStack != null) {
+                newFocusedStack.resetAdjustedForIme(true);
+            }
+            displayContent.mDividerControllerLocked.setAdjustedForIme(false, false, imeWin);
+            adjustForImeIfNeeded(displayContent);
+
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index bf69717..5077f32 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2608,7 +2608,7 @@
         final int ph = containingFrame.height();
         final Task task = getTask();
         final boolean nonFullscreenTask = isInMultiWindowMode();
-        final boolean fitToDisplay = task != null && !nonFullscreenTask && !layoutInParentFrame();
+        final boolean fitToDisplay = (task == null || !nonFullscreenTask) && !layoutInParentFrame();
         float x, y;
         int w,h;
 
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f5c58c5..da0d048 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -225,6 +225,30 @@
             "android.telecom.extra.UNKNOWN_CALL_HANDLE";
 
     /**
+     * Optional extra for incoming and outgoing calls containing a long which specifies the time the
+     * call was created. This value is in milliseconds since boot.
+     * @hide
+     */
+    public static final String EXTRA_CALL_CREATED_TIME_MILLIS =
+            "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
+
+    /**
+     * Optional extra for incoming and outgoing calls containing a long which specifies the time
+     * telecom began routing the call. This value is in milliseconds since boot.
+     * @hide
+     */
+    public static final String EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS =
+            "android.telecom.extra.CALL_TELECOM_ROUTING_START_TIME_MILLIS";
+
+    /**
+     * Optional extra for incoming and outgoing calls containing a long which specifies the time
+     * telecom finished routing the call. This value is in milliseconds since boot.
+     * @hide
+     */
+    public static final String EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS =
+            "android.telecom.extra.CALL_TELECOM_ROUTING_END_TIME_MILLIS";
+
+    /**
      * Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
      * containing the disconnect code.
      */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 26730de..5ac697f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -547,6 +547,13 @@
      */
     public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
 
+     /**
+      * Flag to indicate if Wi-Fi needs to be disabled in ECBM
+      * @hide
+      **/
+     public static final String
+              KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm";
+
     /**
      * List operator-specific error codes and indices of corresponding error strings in
      * wfcOperatorErrorAlertMessages and wfcOperatorErrorNotificationMessages.
@@ -755,6 +762,7 @@
         sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
         sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
         sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
+        sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index ff8c71c..f3b0ce2 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1347,6 +1347,7 @@
         if (subInfo != null) {
             newConfig.mcc = subInfo.getMcc();
             newConfig.mnc = subInfo.getMnc();
+            if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
         }
         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         DisplayMetrics newMetrics = new DisplayMetrics();
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 907d76e..2c6be62 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -37,10 +37,10 @@
     void listenForSubscriber(in int subId, String pkg, IPhoneStateListener callback, int events,
             boolean notifyNow);
     void notifyCallState(int state, String incomingNumber);
-    void notifyCallStateForSubscriber(in int subId, int state, String incomingNumber);
+    void notifyCallStateForPhoneId(in int phoneId, in int subId, int state, String incomingNumber);
     void notifyServiceStateForPhoneId(in int phoneId, in int subId, in ServiceState state);
-    void notifySignalStrength(in SignalStrength signalStrength);
-    void notifySignalStrengthForSubscriber(in int subId, in SignalStrength signalStrength);
+    void notifySignalStrengthForPhoneId(in int phoneId, in int subId,
+            in SignalStrength signalStrength);
     void notifyMessageWaitingChangedForPhoneId(in int phoneId, in int subId, in boolean mwi);
     void notifyCallForwardingChanged(boolean cfi);
     void notifyCallForwardingChangedForSubscriber(in int subId, boolean cfi);
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 2579d9f..a404a90 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -5,7 +5,6 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
@@ -19,8 +18,6 @@
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 
-import java.util.concurrent.CountDownLatch;
-
 /** @hide */
 @SystemApi
 public class RttManager {
@@ -311,7 +308,7 @@
     }
 
     public RttCapabilities getRttCapabilities() {
-        synchronized (sCapabilitiesLock) {
+        synchronized (mCapabilitiesLock) {
             if (mRttCapabilities == null) {
                 try {
                     mRttCapabilities = mService.getRttCapabilities();
@@ -932,13 +929,13 @@
         validateChannel();
         ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
         Log.i(TAG, "Send RTT request to RTT Service");
-        sAsyncChannel.sendMessage(CMD_OP_START_RANGING,
+        mAsyncChannel.sendMessage(CMD_OP_START_RANGING,
                 0, putListener(listener), parcelableParams);
     }
 
     public void stopRanging(RttListener listener) {
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
+        mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
     }
 
     /**
@@ -975,7 +972,7 @@
         }
         validateChannel();
         int key = putListenerIfAbsent(callback);
-        sAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+        mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
     }
 
     /**
@@ -998,7 +995,7 @@
             Log.e(TAG, "responder not enabled yet");
             return;
         }
-        sAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+        mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
     }
 
     /**
@@ -1110,23 +1107,17 @@
     public static final int
             CMD_OP_ENALBE_RESPONDER_FAILED              = BASE + 8;
 
-    private Context mContext;
-    private IRttManager mService;
-    private RttCapabilities mRttCapabilities;
-
     private static final int INVALID_KEY = 0;
-    private static int sListenerKey = 1;
 
-    private static final SparseArray sListenerMap = new SparseArray();
-    private static final Object sListenerMapLock = new Object();
-    private static final Object sCapabilitiesLock = new Object();
+    private final Context mContext;
+    private final IRttManager mService;
+    private final SparseArray mListenerMap = new SparseArray();
+    private final Object mListenerMapLock = new Object();
+    private final Object mCapabilitiesLock = new Object();
 
-    private static AsyncChannel sAsyncChannel;
-    private static CountDownLatch sConnected;
-
-    private static final Object sThreadRefLock = new Object();
-    private static int sThreadRefCount;
-    private static HandlerThread sHandlerThread;
+    private RttCapabilities mRttCapabilities;
+    private int mListenerKey = 1;
+    private AsyncChannel mAsyncChannel;
 
     /**
      * Create a new WifiScanner instance.
@@ -1135,122 +1126,107 @@
      * the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}.
      * @param context the application context
      * @param service the Binder interface
+     * @param looper Looper for running the callbacks.
+     *
      * @hide
      */
-
-    public RttManager(Context context, IRttManager service) {
+    public RttManager(Context context, IRttManager service, Looper looper) {
         mContext = context;
         mService = service;
-        init();
-    }
-
-    private void init() {
-        synchronized (sThreadRefLock) {
-            if (++sThreadRefCount == 1) {
-                Messenger messenger = null;
-                try {
-                    Log.d(TAG, "Get the messenger from " + mService);
-                    messenger = mService.getMessenger();
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                } catch (SecurityException e) {
-                    /* do nothing */
-                }
-
-                if (messenger == null) {
-                    sAsyncChannel = null;
-                    return;
-                }
-
-                sHandlerThread = new HandlerThread("RttManager");
-                sAsyncChannel = new AsyncChannel();
-                sConnected = new CountDownLatch(1);
-
-                sHandlerThread.start();
-                Handler handler = new ServiceHandler(sHandlerThread.getLooper());
-                sAsyncChannel.connect(mContext, handler, messenger);
-                try {
-                    sConnected.await();
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "interrupted wait at init");
-                }
-            }
+        Messenger messenger = null;
+        try {
+            Log.d(TAG, "Get the messenger from " + mService);
+            messenger = mService.getMessenger();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
+
+        if (messenger == null) {
+            throw new IllegalStateException("getMessenger() returned null!  This is invalid.");
+        }
+
+        mAsyncChannel = new AsyncChannel();
+
+        Handler handler = new ServiceHandler(looper);
+        mAsyncChannel.connectSync(mContext, handler, messenger);
+        // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
+        // synchronously, which causes RttService to receive the wrong replyTo value.
+        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
     }
 
     private void validateChannel() {
-        if (sAsyncChannel == null) throw new IllegalStateException(
+        if (mAsyncChannel == null) throw new IllegalStateException(
                 "No permission to access and change wifi or a bad initialization");
     }
 
-    private static int putListener(Object listener) {
+    private int putListener(Object listener) {
         if (listener == null) return INVALID_KEY;
         int key;
-        synchronized (sListenerMapLock) {
+        synchronized (mListenerMapLock) {
             do {
-                key = sListenerKey++;
+                key = mListenerKey++;
             } while (key == INVALID_KEY);
-            sListenerMap.put(key, listener);
+            mListenerMap.put(key, listener);
         }
         return key;
     }
 
-    // Insert a listener if it doesn't exist in sListenerMap. Returns the key of the listener.
-    private static int putListenerIfAbsent(Object listener) {
+    // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener.
+    private int putListenerIfAbsent(Object listener) {
         if (listener == null) return INVALID_KEY;
-        synchronized (sListenerMapLock) {
+        synchronized (mListenerMapLock) {
             int key = getListenerKey(listener);
             if (key != INVALID_KEY) {
                 return key;
             }
             do {
-                key = sListenerKey++;
+                key = mListenerKey++;
             } while (key == INVALID_KEY);
-            sListenerMap.put(key, listener);
+            mListenerMap.put(key, listener);
             return key;
         }
 
     }
 
-    private static Object getListener(int key) {
+    private Object getListener(int key) {
         if (key == INVALID_KEY) return null;
-        synchronized (sListenerMapLock) {
-            Object listener = sListenerMap.get(key);
+        synchronized (mListenerMapLock) {
+            Object listener = mListenerMap.get(key);
             return listener;
         }
     }
 
-    private static int getListenerKey(Object listener) {
+    private int getListenerKey(Object listener) {
         if (listener == null) return INVALID_KEY;
-        synchronized (sListenerMapLock) {
-            int index = sListenerMap.indexOfValue(listener);
+        synchronized (mListenerMapLock) {
+            int index = mListenerMap.indexOfValue(listener);
             if (index == -1) {
                 return INVALID_KEY;
             } else {
-                return sListenerMap.keyAt(index);
+                return mListenerMap.keyAt(index);
             }
         }
     }
 
-    private static Object removeListener(int key) {
+    private Object removeListener(int key) {
         if (key == INVALID_KEY) return null;
-        synchronized (sListenerMapLock) {
-            Object listener = sListenerMap.get(key);
-            sListenerMap.remove(key);
+        synchronized (mListenerMapLock) {
+            Object listener = mListenerMap.get(key);
+            mListenerMap.remove(key);
             return listener;
         }
     }
 
-    private static int removeListener(Object listener) {
+    private int removeListener(Object listener) {
         int key = getListenerKey(listener);
         if (key == INVALID_KEY) return key;
-        synchronized (sListenerMapLock) {
-            sListenerMap.remove(key);
+        synchronized (mListenerMapLock) {
+            mListenerMap.remove(key);
             return key;
         }
     }
 
-    private static class ServiceHandler extends Handler {
+    private class ServiceHandler extends Handler {
         ServiceHandler(Looper looper) {
             super(looper);
         }
@@ -1258,24 +1234,13 @@
         public void handleMessage(Message msg) {
             Log.i(TAG, "RTT manager get message: " + msg.what);
             switch (msg.what) {
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
-                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
-                    } else {
-                        Log.e(TAG, "Failed to set up channel connection");
-                        // This will cause all further async API calls on the WifiManager
-                        // to fail and throw an exception
-                        sAsyncChannel = null;
-                    }
-                    return;
                 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
-                    sConnected.countDown();
                     return;
                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
                     Log.e(TAG, "Channel connection lost");
                     // This will cause all further async API calls on the WifiManager
                     // to fail and throw an exception
-                    sAsyncChannel = null;
+                    mAsyncChannel = null;
                     getLooper().quit();
                     return;
             }