Merge "Remove all icon bitmaps when a publisher is uninstalled." into nyc-dev
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7bf03de..a82b950 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2578,9 +2578,10 @@
             return true;
         }
 
-        case START_LOCK_TASK_BY_CURRENT_TRANSACTION: {
+        case START_SYSTEM_LOCK_TASK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
-            startLockTaskModeOnCurrent();
+            int taskId = data.readInt();
+            startSystemLockTaskMode(taskId);
             reply.writeNoException();
             return true;
         }
@@ -2592,9 +2593,9 @@
             return true;
         }
 
-        case STOP_LOCK_TASK_BY_CURRENT_TRANSACTION: {
+        case STOP_SYSTEM_LOCK_TASK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
-            stopLockTaskModeOnCurrent();
+            stopSystemLockTaskMode();
             reply.writeNoException();
             return true;
         }
@@ -6386,11 +6387,12 @@
     }
 
     @Override
-    public void startLockTaskModeOnCurrent() throws RemoteException {
+    public void startSystemLockTaskMode(int taskId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        mRemote.transact(START_LOCK_TASK_BY_CURRENT_TRANSACTION, data, reply, 0);
+        data.writeInt(taskId);
+        mRemote.transact(START_SYSTEM_LOCK_TASK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -6408,11 +6410,11 @@
     }
 
     @Override
-    public void stopLockTaskModeOnCurrent() throws RemoteException {
+    public void stopSystemLockTaskMode() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        mRemote.transact(STOP_LOCK_TASK_BY_CURRENT_TRANSACTION, data, reply, 0);
+        mRemote.transact(STOP_SYSTEM_LOCK_TASK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 6975116..1a4e98c 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -555,7 +555,7 @@
 
     public int getActivityDisplayId(IBinder activityToken) throws RemoteException;
 
-    public void startLockTaskModeOnCurrent() throws RemoteException;
+    public void startSystemLockTaskMode(int taskId) throws RemoteException;
 
     public void startLockTaskMode(int taskId) throws RemoteException;
 
@@ -563,7 +563,7 @@
 
     public void stopLockTaskMode() throws RemoteException;
 
-    public void stopLockTaskModeOnCurrent() throws RemoteException;
+    public void stopSystemLockTaskMode() throws RemoteException;
 
     public boolean isInLockTaskMode() throws RemoteException;
 
@@ -949,8 +949,8 @@
     int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
     int GET_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+219;
     int GET_APP_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+220;
-    int START_LOCK_TASK_BY_CURRENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+221;
-    int STOP_LOCK_TASK_BY_CURRENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+222;
+    int START_SYSTEM_LOCK_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+221;
+    int STOP_SYSTEM_LOCK_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+222;
     int FINISH_VOICE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+223;
     int IS_TOP_OF_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+224;
     int REQUEST_VISIBLE_BEHIND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+225;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index aef92cf..4c4f128 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -29,7 +29,6 @@
 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;
@@ -431,44 +430,37 @@
             @Nullable Configuration overrideConfig,
             @NonNull CompatibilityInfo compatInfo,
             @Nullable ClassLoader classLoader) {
-        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();
+        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();
-                }
-            }
-
-            // 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);
+        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);
     }
 
     /**
@@ -498,8 +490,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,
@@ -547,7 +539,6 @@
         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
@@ -622,21 +613,16 @@
             @Nullable Configuration overrideConfig,
             @NonNull CompatibilityInfo compatInfo,
             @Nullable ClassLoader 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);
-        }
+        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);
     }
 
     /**
@@ -650,104 +636,93 @@
      */
     public void updateResourcesForActivity(@NonNull IBinder activityToken,
             @Nullable Configuration overrideConfig) {
-        try {
-            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
-                    "ResourcesManager#updateResourcesForActivity");
-            synchronized (this) {
-                final ActivityResources activityResources =
-                        getOrCreateActivityResourcesStructLocked(activityToken);
+        synchronized (this) {
+            final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
+                    activityToken);
 
-                if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
-                    // They are the same, no work to do.
-                    return;
+            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;
                 }
 
-                // 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);
+                // 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;
+                }
 
-                // Update the Activity's base override.
+                // Build the new override configuration for this ResourcesKey.
+                final Configuration rebasedOverrideConfig = new Configuration();
                 if (overrideConfig != null) {
-                    activityResources.overrideConfig.setTo(overrideConfig);
-                } else {
-                    activityResources.overrideConfig.setToDefaults();
+                    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) {
-                    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);
+                    Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+                            + " to newKey=" + newKey);
                 }
 
-                final boolean activityHasOverrideConfig =
-                        !activityResources.overrideConfig.equals(Configuration.EMPTY);
+                ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
+                if (resourcesImpl == null) {
+                    resourcesImpl = createResourcesImpl(newKey);
+                    mResourceImpls.put(newKey, new WeakReference<>(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);
-                    }
+                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);
         }
     }
 
@@ -770,95 +745,86 @@
 
     public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
                                                              @Nullable CompatibilityInfo compat) {
-        try {
-            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
-                    "ResourcesManager#applyConfigurationToResourcesLocked");
-
-            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();
-
-            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);
-
-            ApplicationPackageManager.configurationChanged();
-            //Slog.i(TAG, "Configuration changed in " + currentPackageName());
-
-            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();
-                        }
-                        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);
-                    }
-                    //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;
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        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();
+
+        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);
+
+        ApplicationPackageManager.configurationChanged();
+        //Slog.i(TAG, "Configuration changed in " + currentPackageName());
+
+        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();
+                    }
+                    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);
+                }
+                //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;
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 269089e..7a18df6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1093,6 +1093,7 @@
      * returned.
      */
     public List<ComponentName> getActiveAdmins() {
+        throwIfParentInstance("getActiveAdmins");
         return getActiveAdminsAsUser(myUserId());
     }
 
@@ -1149,6 +1150,7 @@
      * @throws SecurityException if the caller is not in the owner application of {@code admin}.
      */
     public void removeActiveAdmin(@NonNull ComponentName admin) {
+        throwIfParentInstance("removeActiveAdmin");
         if (mService != null) {
             try {
                 mService.removeActiveAdmin(admin, myUserId());
@@ -1169,6 +1171,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public boolean hasGrantedPolicy(@NonNull ComponentName admin, int usesPolicy) {
+        throwIfParentInstance("hasGrantedPolicy");
         if (mService != null) {
             try {
                 return mService.hasGrantedPolicy(admin, usesPolicy, myUserId());
@@ -2216,9 +2219,7 @@
      *             that uses {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD}
      */
     public boolean resetPassword(String password, int flags) {
-        if (mParentInstance) {
-            throw new SecurityException("Reset password does not work across profiles.");
-        }
+        throwIfParentInstance("resetPassword");
         if (mService != null) {
             try {
                 return mService.resetPassword(password, flags);
@@ -2355,6 +2356,7 @@
      *             that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
      */
     public void wipeData(int flags) {
+        throwIfParentInstance("wipeData");
         if (mService != null) {
             try {
                 mService.wipeData(flags);
@@ -2388,6 +2390,7 @@
      */
     public ComponentName setGlobalProxy(@NonNull ComponentName admin, Proxy proxySpec,
             List<String> exclusionList ) {
+        throwIfParentInstance("setGlobalProxy");
         if (proxySpec == null) {
             throw new NullPointerException();
         }
@@ -2453,6 +2456,7 @@
      */
     public void setRecommendedGlobalProxy(@NonNull ComponentName admin, @Nullable ProxyInfo
             proxyInfo) {
+        throwIfParentInstance("setRecommendedGlobalProxy");
         if (mService != null) {
             try {
                 mService.setRecommendedGlobalProxy(admin, proxyInfo);
@@ -2603,6 +2607,7 @@
      *             {@link DeviceAdminInfo#USES_ENCRYPTED_STORAGE}
      */
     public int setStorageEncryption(@NonNull ComponentName admin, boolean encrypt) {
+        throwIfParentInstance("setStorageEncryption");
         if (mService != null) {
             try {
                 return mService.setStorageEncryption(admin, encrypt);
@@ -2623,6 +2628,7 @@
      * @return true if the admin(s) are requesting encryption, false if not.
      */
     public boolean getStorageEncryption(@Nullable ComponentName admin) {
+        throwIfParentInstance("getStorageEncryption");
         if (mService != null) {
             try {
                 return mService.getStorageEncryption(admin, myUserId());
@@ -2653,6 +2659,7 @@
      * or {@link #ENCRYPTION_STATUS_ACTIVE}.
      */
     public int getStorageEncryptionStatus() {
+        throwIfParentInstance("getStorageEncryptionStatus");
         return getStorageEncryptionStatus(myUserId());
     }
 
@@ -2718,6 +2725,7 @@
      *         owner.
      */
     public boolean installCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
+        throwIfParentInstance("installCaCert");
         if (mService != null) {
             try {
                 return mService.installCaCert(admin, certBuffer);
@@ -2738,6 +2746,7 @@
      *         owner.
      */
     public void uninstallCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
+        throwIfParentInstance("uninstallCaCert");
         if (mService != null) {
             try {
                 final String alias = getCaCertAlias(certBuffer);
@@ -2763,6 +2772,7 @@
      */
     public List<byte[]> getInstalledCaCerts(@Nullable ComponentName admin) {
         List<byte[]> certs = new ArrayList<byte[]>();
+        throwIfParentInstance("getInstalledCaCerts");
         if (mService != null) {
             try {
                 mService.enforceCanManageCaCerts(admin);
@@ -2791,6 +2801,7 @@
      *         owner.
      */
     public void uninstallAllUserCaCerts(@Nullable ComponentName admin) {
+        throwIfParentInstance("uninstallAllUserCaCerts");
         if (mService != null) {
             try {
                 mService.uninstallCaCerts(admin, new TrustedCertificateStore().userAliases()
@@ -2811,6 +2822,7 @@
      *         owner.
      */
     public boolean hasCaCertInstalled(@Nullable ComponentName admin, byte[] certBuffer) {
+        throwIfParentInstance("hasCaCertInstalled");
         if (mService != null) {
             try {
                 mService.enforceCanManageCaCerts(admin);
@@ -2879,6 +2891,7 @@
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
             @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
+        throwIfParentInstance("installKeyPair");
         try {
             final byte[] pemCert = Credentials.convertToPem(certs[0]);
             byte[] pemChain = null;
@@ -2911,6 +2924,7 @@
      *         owner.
      */
     public boolean removeKeyPair(@Nullable ComponentName admin, @NonNull String alias) {
+        throwIfParentInstance("removeKeyPair");
         try {
             return mService.removeKeyPair(admin, alias);
         } catch (RemoteException e) {
@@ -2951,6 +2965,7 @@
      */
     public void setCertInstallerPackage(@NonNull ComponentName admin, @Nullable String
             installerPackage) throws SecurityException {
+        throwIfParentInstance("setCertInstallerPackage");
         if (mService != null) {
             try {
                 mService.setCertInstallerPackage(admin, installerPackage);
@@ -2970,6 +2985,7 @@
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
      */
     public String getCertInstallerPackage(@NonNull ComponentName admin) throws SecurityException {
+        throwIfParentInstance("getCertInstallerPackage");
         if (mService != null) {
             try {
                 return mService.getCertInstallerPackage(admin);
@@ -3000,6 +3016,7 @@
      */
     public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage)
             throws NameNotFoundException, UnsupportedOperationException {
+        throwIfParentInstance("setAlwaysOnVpnPackage");
         if (mService != null) {
             try {
                 if (!mService.setAlwaysOnVpnPackage(admin, vpnPackage)) {
@@ -3021,6 +3038,7 @@
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
      */
     public String getAlwaysOnVpnPackage(@NonNull ComponentName admin) {
+        throwIfParentInstance("getAlwaysOnVpnPackage");
         if (mService != null) {
             try {
                 return mService.getAlwaysOnVpnPackage(admin);
@@ -3048,6 +3066,7 @@
      *             {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA}.
      */
     public void setCameraDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setCameraDisabled");
         if (mService != null) {
             try {
                 mService.setCameraDisabled(admin, disabled);
@@ -3064,6 +3083,7 @@
      * have disabled the camera
      */
     public boolean getCameraDisabled(@Nullable ComponentName admin) {
+        throwIfParentInstance("getCameraDisabled");
         return getCameraDisabled(admin, myUserId());
     }
 
@@ -3093,6 +3113,7 @@
      *             than the one managed by the device owner.
      */
     public boolean requestBugreport(@NonNull ComponentName admin) {
+        throwIfParentInstance("requestBugreport");
         if (mService != null) {
             try {
                 return mService.requestBugreport(admin);
@@ -3131,6 +3152,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setScreenCaptureDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setScreenCaptureDisabled");
         if (mService != null) {
             try {
                 mService.setScreenCaptureDisabled(admin, disabled);
@@ -3147,6 +3169,7 @@
      * have disabled screen capture.
      */
     public boolean getScreenCaptureDisabled(@Nullable ComponentName admin) {
+        throwIfParentInstance("getScreenCaptureDisabled");
         return getScreenCaptureDisabled(admin, myUserId());
     }
 
@@ -3176,6 +3199,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public void setAutoTimeRequired(@NonNull ComponentName admin, boolean required) {
+        throwIfParentInstance("setAutoTimeRequired");
         if (mService != null) {
             try {
                 mService.setAutoTimeRequired(admin, required);
@@ -3189,6 +3213,7 @@
      * @return true if auto time is required.
      */
     public boolean getAutoTimeRequired() {
+        throwIfParentInstance("getAutoTimeRequired");
         if (mService != null) {
             try {
                 return mService.getAutoTimeRequired();
@@ -3215,6 +3240,7 @@
      */
     public void setForceEphemeralUsers(
             @NonNull ComponentName admin, boolean forceEphemeralUsers) {
+        throwIfParentInstance("setForceEphemeralUsers");
         if (mService != null) {
             try {
                 mService.setForceEphemeralUsers(admin, forceEphemeralUsers);
@@ -3230,6 +3256,7 @@
      * @hide
      */
     public boolean getForceEphemeralUsers(@NonNull ComponentName admin) {
+        throwIfParentInstance("getForceEphemeralUsers");
         if (mService != null) {
             try {
                 return mService.getForceEphemeralUsers(admin);
@@ -3517,6 +3544,7 @@
      * @return whether or not the package is registered as the device owner app.
      */
     public boolean isDeviceOwnerApp(String packageName) {
+        throwIfParentInstance("isDeviceOwnerApp");
         return isDeviceOwnerAppOnCallingUser(packageName);
     }
 
@@ -3614,6 +3642,7 @@
      *             does not own the current device owner component.
      */
     public void clearDeviceOwnerApp(String packageName) {
+        throwIfParentInstance("clearDeviceOwnerApp");
         if (mService != null) {
             try {
                 mService.clearDeviceOwner(packageName);
@@ -3632,6 +3661,7 @@
      */
     @SystemApi
     public String getDeviceOwner() {
+        throwIfParentInstance("getDeviceOwner");
         final ComponentName name = getDeviceOwnerComponentOnCallingUser();
         return name != null ? name.getPackageName() : null;
     }
@@ -3657,6 +3687,7 @@
      */
     @SystemApi
     public String getDeviceOwnerNameOnAnyUser() {
+        throwIfParentInstance("getDeviceOwnerNameOnAnyUser");
         if (mService != null) {
             try {
                 return mService.getDeviceOwnerName();
@@ -3708,6 +3739,7 @@
     @SystemApi
     public boolean setActiveProfileOwner(@NonNull ComponentName admin, @Deprecated String ownerName)
             throws IllegalArgumentException {
+        throwIfParentInstance("setActiveProfileOwner");
         if (mService != null) {
             try {
                 final int myUserId = myUserId();
@@ -3731,6 +3763,7 @@
      * @throws SecurityException if {@code admin} is not an active profile owner.
      */
     public void clearProfileOwner(@NonNull ComponentName admin) {
+        throwIfParentInstance("clearProfileOwner");
         if (mService != null) {
             try {
                 mService.clearProfileOwner(admin);
@@ -3804,6 +3837,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public void setDeviceOwnerLockScreenInfo(@NonNull ComponentName admin, CharSequence info) {
+        throwIfParentInstance("setDeviceOwnerLockScreenInfo");
         if (mService != null) {
             try {
                 mService.setDeviceOwnerLockScreenInfo(admin, info);
@@ -3817,6 +3851,7 @@
      * @return The device owner information. If it is not set returns {@code null}.
      */
     public CharSequence getDeviceOwnerLockScreenInfo() {
+        throwIfParentInstance("getDeviceOwnerLockScreenInfo");
         if (mService != null) {
             try {
                 return mService.getDeviceOwnerLockScreenInfo();
@@ -3848,6 +3883,7 @@
      */
     public String[] setPackagesSuspended(@NonNull ComponentName admin, String[] packageNames,
             boolean suspended) {
+        throwIfParentInstance("setPackagesSuspended");
         if (mService != null) {
             try {
                 return mService.setPackagesSuspended(admin, packageNames, suspended);
@@ -3870,6 +3906,7 @@
      */
     public boolean isPackageSuspended(@NonNull ComponentName admin, String packageName)
             throws NameNotFoundException {
+        throwIfParentInstance("isPackageSuspended");
         if (mService != null) {
             try {
                 return mService.isPackageSuspended(admin, packageName);
@@ -3891,6 +3928,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setProfileEnabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("setProfileEnabled");
         if (mService != null) {
             try {
                 mService.setProfileEnabled(admin);
@@ -3912,6 +3950,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setProfileName(@NonNull ComponentName admin, String profileName) {
+        throwIfParentInstance("setProfileName");
         if (mService != null) {
             try {
                 mService.setProfileName(admin, profileName);
@@ -3930,6 +3969,7 @@
      * @return Whether or not the package is registered as the profile owner.
      */
     public boolean isProfileOwnerApp(String packageName) {
+        throwIfParentInstance("isProfileOwnerApp");
         if (mService != null) {
             try {
                 ComponentName profileOwner = mService.getProfileOwner(myUserId());
@@ -3950,6 +3990,7 @@
      */
     @SystemApi
     public ComponentName getProfileOwner() throws IllegalArgumentException {
+        throwIfParentInstance("getProfileOwner");
         return getProfileOwnerAsUser(Process.myUserHandle().getIdentifier());
     }
 
@@ -3994,6 +4035,7 @@
      */
     @SystemApi
     public String getProfileOwnerNameAsUser(int userId) throws IllegalArgumentException {
+        throwIfParentInstance("getProfileOwnerNameAsUser");
         if (mService != null) {
             try {
                 return mService.getProfileOwnerName(userId);
@@ -4024,6 +4066,7 @@
      */
     public void addPersistentPreferredActivity(@NonNull ComponentName admin, IntentFilter filter,
             @NonNull ComponentName activity) {
+        throwIfParentInstance("addPersistentPreferredActivity");
         if (mService != null) {
             try {
                 mService.addPersistentPreferredActivity(admin, filter, activity);
@@ -4046,6 +4089,7 @@
      */
     public void clearPackagePersistentPreferredActivities(@NonNull ComponentName admin,
             String packageName) {
+        throwIfParentInstance("clearPackagePersistentPreferredActivities");
         if (mService != null) {
             try {
                 mService.clearPackagePersistentPreferredActivities(admin, packageName);
@@ -4074,6 +4118,7 @@
      */
     public void setApplicationRestrictionsManagingPackage(@NonNull ComponentName admin,
             @Nullable String packageName) throws NameNotFoundException {
+        throwIfParentInstance("setApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
                 if (!mService.setApplicationRestrictionsManagingPackage(admin, packageName)) {
@@ -4095,6 +4140,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public String getApplicationRestrictionsManagingPackage(@NonNull ComponentName admin) {
+        throwIfParentInstance("getApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
                 return mService.getApplicationRestrictionsManagingPackage(admin);
@@ -4114,6 +4160,7 @@
      * that method.
      */
     public boolean isCallerApplicationRestrictionsManagingPackage() {
+        throwIfParentInstance("isCallerApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
                 return mService.isCallerApplicationRestrictionsManagingPackage();
@@ -4159,6 +4206,7 @@
      */
     public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
             Bundle settings) {
+        throwIfParentInstance("setApplicationRestrictions");
         if (mService != null) {
             try {
                 mService.setApplicationRestrictions(admin, packageName, settings);
@@ -4257,6 +4305,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setCrossProfileCallerIdDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setCrossProfileCallerIdDisabled");
         if (mService != null) {
             try {
                 mService.setCrossProfileCallerIdDisabled(admin, disabled);
@@ -4277,6 +4326,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean getCrossProfileCallerIdDisabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("getCrossProfileCallerIdDisabled");
         if (mService != null) {
             try {
                 return mService.getCrossProfileCallerIdDisabled(admin);
@@ -4317,6 +4367,7 @@
      */
     public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin,
             boolean disabled) {
+        throwIfParentInstance("setCrossProfileContactsSearchDisabled");
         if (mService != null) {
             try {
                 mService.setCrossProfileContactsSearchDisabled(admin, disabled);
@@ -4337,6 +4388,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("getCrossProfileContactsSearchDisabled");
         if (mService != null) {
             try {
                 return mService.getCrossProfileContactsSearchDisabled(admin);
@@ -4407,6 +4459,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setBluetoothContactSharingDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setBluetoothContactSharingDisabled");
         if (mService != null) {
             try {
                 mService.setBluetoothContactSharingDisabled(admin, disabled);
@@ -4429,6 +4482,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean getBluetoothContactSharingDisabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("getBluetoothContactSharingDisabled");
         if (mService != null) {
             try {
                 return mService.getBluetoothContactSharingDisabled(admin);
@@ -4472,6 +4526,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void addCrossProfileIntentFilter(@NonNull ComponentName admin, IntentFilter filter, int flags) {
+        throwIfParentInstance("addCrossProfileIntentFilter");
         if (mService != null) {
             try {
                 mService.addCrossProfileIntentFilter(admin, filter, flags);
@@ -4490,6 +4545,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void clearCrossProfileIntentFilters(@NonNull ComponentName admin) {
+        throwIfParentInstance("clearCrossProfileIntentFilters");
         if (mService != null) {
             try {
                 mService.clearCrossProfileIntentFilters(admin);
@@ -4519,6 +4575,7 @@
      */
     public boolean setPermittedAccessibilityServices(@NonNull ComponentName admin,
             List<String> packageNames) {
+        throwIfParentInstance("setPermittedAccessibilityServices");
         if (mService != null) {
             try {
                 return mService.setPermittedAccessibilityServices(admin, packageNames);
@@ -4540,6 +4597,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public List<String> getPermittedAccessibilityServices(@NonNull ComponentName admin) {
+        throwIfParentInstance("getPermittedAccessibilityServices");
         if (mService != null) {
             try {
                 return mService.getPermittedAccessibilityServices(admin);
@@ -4587,6 +4645,7 @@
      */
      @SystemApi
      public List<String> getPermittedAccessibilityServices(int userId) {
+        throwIfParentInstance("getPermittedAccessibilityServices");
         if (mService != null) {
             try {
                 return mService.getPermittedAccessibilityServicesForUser(userId);
@@ -4617,6 +4676,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean setPermittedInputMethods(@NonNull ComponentName admin, List<String> packageNames) {
+        throwIfParentInstance("setPermittedInputMethods");
         if (mService != null) {
             try {
                 return mService.setPermittedInputMethods(admin, packageNames);
@@ -4639,6 +4699,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public List<String> getPermittedInputMethods(@NonNull ComponentName admin) {
+        throwIfParentInstance("getPermittedInputMethods");
         if (mService != null) {
             try {
                 return mService.getPermittedInputMethods(admin);
@@ -4684,6 +4745,7 @@
      */
     @SystemApi
     public List<String> getPermittedInputMethodsForCurrentUser() {
+        throwIfParentInstance("getPermittedInputMethodsForCurrentUser");
         if (mService != null) {
             try {
                 return mService.getPermittedInputMethodsForCurrentUser();
@@ -4704,6 +4766,7 @@
      * @hide
      */
     public List<String> getKeepUninstalledPackages(@NonNull ComponentName admin) {
+        throwIfParentInstance("getKeepUninstalledPackages");
         if (mService != null) {
             try {
                 return mService.getKeepUninstalledPackages(admin);
@@ -4728,6 +4791,7 @@
      */
     public void setKeepUninstalledPackages(@NonNull ComponentName admin,
             @NonNull List<String> packageNames) {
+        throwIfParentInstance("setKeepUninstalledPackages");
         if (mService != null) {
             try {
                 mService.setKeepUninstalledPackages(admin, packageNames);
@@ -4834,6 +4898,7 @@
     public UserHandle createAndManageUser(@NonNull ComponentName admin, @NonNull String name,
             @NonNull ComponentName profileOwner, @Nullable PersistableBundle adminExtras,
             int flags) {
+        throwIfParentInstance("createAndManageUser");
         try {
             return mService.createAndManageUser(admin, name, profileOwner, adminExtras, flags);
         } catch (RemoteException re) {
@@ -4851,6 +4916,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean removeUser(@NonNull ComponentName admin, UserHandle userHandle) {
+        throwIfParentInstance("removeUser");
         try {
             return mService.removeUser(admin, userHandle);
         } catch (RemoteException re) {
@@ -4868,6 +4934,7 @@
      * @see Intent#ACTION_USER_FOREGROUND
      */
     public boolean switchUser(@NonNull ComponentName admin, @Nullable UserHandle userHandle) {
+        throwIfParentInstance("switchUser");
         try {
             return mService.switchUser(admin, userHandle);
         } catch (RemoteException re) {
@@ -4893,6 +4960,7 @@
      * @see {@link #setApplicationRestrictionsManagingPackage}
      */
     public Bundle getApplicationRestrictions(@Nullable ComponentName admin, String packageName) {
+        throwIfParentInstance("getApplicationRestrictions");
         if (mService != null) {
             try {
                 return mService.getApplicationRestrictions(admin, packageName);
@@ -4915,6 +4983,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void addUserRestriction(@NonNull ComponentName admin, String key) {
+        throwIfParentInstance("addUserRestriction");
         if (mService != null) {
             try {
                 mService.setUserRestriction(admin, key, true);
@@ -4936,6 +5005,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void clearUserRestriction(@NonNull ComponentName admin, String key) {
+        throwIfParentInstance("clearUserRestriction");
         if (mService != null) {
             try {
                 mService.setUserRestriction(admin, key, false);
@@ -4957,6 +5027,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public Bundle getUserRestrictions(@NonNull ComponentName admin) {
+        throwIfParentInstance("getUserRestrictions");
         Bundle ret = null;
         if (mService != null) {
             try {
@@ -5001,6 +5072,7 @@
      */
     public boolean setApplicationHidden(@NonNull ComponentName admin, String packageName,
             boolean hidden) {
+        throwIfParentInstance("setApplicationHidden");
         if (mService != null) {
             try {
                 return mService.setApplicationHidden(admin, packageName, hidden);
@@ -5020,6 +5092,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean isApplicationHidden(@NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("isApplicationHidden");
         if (mService != null) {
             try {
                 return mService.isApplicationHidden(admin, packageName);
@@ -5039,6 +5112,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void enableSystemApp(@NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("enableSystemApp");
         if (mService != null) {
             try {
                 mService.enableSystemApp(admin, packageName);
@@ -5059,6 +5133,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public int enableSystemApp(@NonNull ComponentName admin, Intent intent) {
+        throwIfParentInstance("enableSystemApp");
         if (mService != null) {
             try {
                 return mService.enableSystemAppWithIntent(admin, intent);
@@ -5091,6 +5166,7 @@
      */
     public void setAccountManagementDisabled(@NonNull ComponentName admin, String accountType,
             boolean disabled) {
+        throwIfParentInstance("setAccountManagementDisabled");
         if (mService != null) {
             try {
                 mService.setAccountManagementDisabled(admin, accountType, disabled);
@@ -5111,6 +5187,7 @@
      * @see #setAccountManagementDisabled
      */
     public String[] getAccountTypesWithManagementDisabled() {
+        throwIfParentInstance("getAccountTypesWithManagementDisabled");
         return getAccountTypesWithManagementDisabledAsUser(myUserId());
     }
 
@@ -5148,6 +5225,7 @@
      */
     public void setLockTaskPackages(@NonNull ComponentName admin, String[] packages)
             throws SecurityException {
+        throwIfParentInstance("setLockTaskPackages");
         if (mService != null) {
             try {
                 mService.setLockTaskPackages(admin, packages);
@@ -5164,6 +5242,7 @@
      * @hide
      */
     public String[] getLockTaskPackages(@NonNull ComponentName admin) {
+        throwIfParentInstance("getLockTaskPackages");
         if (mService != null) {
             try {
                 return mService.getLockTaskPackages(admin);
@@ -5180,6 +5259,7 @@
      * @param pkg The package to check
      */
     public boolean isLockTaskPermitted(String pkg) {
+        throwIfParentInstance("isLockTaskPermitted");
         if (mService != null) {
             try {
                 return mService.isLockTaskPermitted(pkg);
@@ -5228,6 +5308,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public void setGlobalSetting(@NonNull ComponentName admin, String setting, String value) {
+        throwIfParentInstance("setGlobalSetting");
         if (mService != null) {
             try {
                 mService.setGlobalSetting(admin, setting, value);
@@ -5260,6 +5341,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setSecureSetting(@NonNull ComponentName admin, String setting, String value) {
+        throwIfParentInstance("setSecureSetting");
         if (mService != null) {
             try {
                 mService.setSecureSetting(admin, setting, value);
@@ -5283,6 +5365,7 @@
      */
     public void setRestrictionsProvider(@NonNull ComponentName admin,
             @Nullable ComponentName provider) {
+        throwIfParentInstance("setRestrictionsProvider");
         if (mService != null) {
             try {
                 mService.setRestrictionsProvider(admin, provider);
@@ -5300,6 +5383,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setMasterVolumeMuted(@NonNull ComponentName admin, boolean on) {
+        throwIfParentInstance("setMasterVolumeMuted");
         if (mService != null) {
             try {
                 mService.setMasterVolumeMuted(admin, on);
@@ -5317,6 +5401,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean isMasterVolumeMuted(@NonNull ComponentName admin) {
+        throwIfParentInstance("isMasterVolumeMuted");
         if (mService != null) {
             try {
                 return mService.isMasterVolumeMuted(admin);
@@ -5337,6 +5422,7 @@
      */
     public void setUninstallBlocked(@NonNull ComponentName admin, String packageName,
             boolean uninstallBlocked) {
+        throwIfParentInstance("setUninstallBlocked");
         if (mService != null) {
             try {
                 mService.setUninstallBlocked(admin, packageName, uninstallBlocked);
@@ -5362,6 +5448,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean isUninstallBlocked(@Nullable ComponentName admin, String packageName) {
+        throwIfParentInstance("isUninstallBlocked");
         if (mService != null) {
             try {
                 return mService.isUninstallBlocked(admin, packageName);
@@ -5389,6 +5476,7 @@
      * @see #getCrossProfileWidgetProviders(android.content.ComponentName)
      */
     public boolean addCrossProfileWidgetProvider(@NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("addCrossProfileWidgetProvider");
         if (mService != null) {
             try {
                 return mService.addCrossProfileWidgetProvider(admin, packageName);
@@ -5416,6 +5504,7 @@
      */
     public boolean removeCrossProfileWidgetProvider(
             @NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("removeCrossProfileWidgetProvider");
         if (mService != null) {
             try {
                 return mService.removeCrossProfileWidgetProvider(admin, packageName);
@@ -5437,6 +5526,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public List<String> getCrossProfileWidgetProviders(@NonNull ComponentName admin) {
+        throwIfParentInstance("getCrossProfileWidgetProviders");
         if (mService != null) {
             try {
                 List<String> providers = mService.getCrossProfileWidgetProviders(admin);
@@ -5458,6 +5548,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setUserIcon(@NonNull ComponentName admin, Bitmap icon) {
+        throwIfParentInstance("setUserIcon");
         try {
             mService.setUserIcon(admin, icon);
         } catch (RemoteException re) {
@@ -5477,6 +5568,7 @@
      * @see SystemUpdatePolicy
      */
     public void setSystemUpdatePolicy(@NonNull ComponentName admin, SystemUpdatePolicy policy) {
+        throwIfParentInstance("setSystemUpdatePolicy");
         if (mService != null) {
             try {
                 mService.setSystemUpdatePolicy(admin, policy);
@@ -5492,6 +5584,7 @@
      * @return The current policy object, or {@code null} if no policy is set.
      */
     public SystemUpdatePolicy getSystemUpdatePolicy() {
+        throwIfParentInstance("getSystemUpdatePolicy");
         if (mService != null) {
             try {
                 return mService.getSystemUpdatePolicy();
@@ -5517,6 +5610,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean setKeyguardDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setKeyguardDisabled");
         try {
             return mService.setKeyguardDisabled(admin, disabled);
         } catch (RemoteException re) {
@@ -5535,6 +5629,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean setStatusBarDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setStatusBarDisabled");
         try {
             return mService.setStatusBarDisabled(admin, disabled);
         } catch (RemoteException re) {
@@ -5553,6 +5648,7 @@
      */
     @SystemApi
     public void notifyPendingSystemUpdate(long updateReceivedTime) {
+        throwIfParentInstance("notifyPendingSystemUpdate");
         if (mService != null) {
             try {
                 mService.notifyPendingSystemUpdate(updateReceivedTime);
@@ -5580,6 +5676,7 @@
      * @see #setPermissionGrantState
      */
     public void setPermissionPolicy(@NonNull ComponentName admin, int policy) {
+        throwIfParentInstance("setPermissionPolicy");
         try {
             mService.setPermissionPolicy(admin, policy);
         } catch (RemoteException re) {
@@ -5594,6 +5691,7 @@
      * @return the current policy for future permission requests.
      */
     public int getPermissionPolicy(ComponentName admin) {
+        throwIfParentInstance("getPermissionPolicy");
         try {
             return mService.getPermissionPolicy(admin);
         } catch (RemoteException re) {
@@ -5630,6 +5728,7 @@
      */
     public boolean setPermissionGrantState(@NonNull ComponentName admin, String packageName,
             String permission, int grantState) {
+        throwIfParentInstance("setPermissionGrantState");
         try {
             return mService.setPermissionGrantState(admin, packageName, permission, grantState);
         } catch (RemoteException re) {
@@ -5658,6 +5757,7 @@
      */
     public int getPermissionGrantState(@NonNull ComponentName admin, String packageName,
             String permission) {
+        throwIfParentInstance("getPermissionGrantState");
         try {
             return mService.getPermissionGrantState(admin, packageName, permission);
         } catch (RemoteException re) {
@@ -5673,6 +5773,7 @@
      * @throws IllegalArgumentException if the supplied action is not valid.
      */
     public boolean isProvisioningAllowed(String action) {
+        throwIfParentInstance("isProvisioningAllowed");
         try {
             return mService.isProvisioningAllowed(action);
         } catch (RemoteException re) {
@@ -5688,6 +5789,7 @@
      * @return if this user is a managed profile of another user.
      */
     public boolean isManagedProfile(@NonNull ComponentName admin) {
+        throwIfParentInstance("isManagedProfile");
         try {
             return mService.isManagedProfile(admin);
         } catch (RemoteException re) {
@@ -5721,6 +5823,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public String getWifiMacAddress(@NonNull ComponentName admin) {
+        throwIfParentInstance("getWifiMacAddress");
         try {
             return mService.getWifiMacAddress(admin);
         } catch (RemoteException re) {
@@ -5737,6 +5840,7 @@
      * @see TelephonyManager#CALL_STATE_IDLE
      */
     public void reboot(@NonNull ComponentName admin) {
+        throwIfParentInstance("reboot");
         try {
             mService.reboot(admin);
         } catch (RemoteException re) {
@@ -5763,6 +5867,7 @@
      */
     public void setShortSupportMessage(@NonNull ComponentName admin,
             @Nullable CharSequence message) {
+        throwIfParentInstance("setShortSupportMessage");
         if (mService != null) {
             try {
                 mService.setShortSupportMessage(admin, message);
@@ -5781,6 +5886,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public CharSequence getShortSupportMessage(@NonNull ComponentName admin) {
+        throwIfParentInstance("getShortSupportMessage");
         if (mService != null) {
             try {
                 return mService.getShortSupportMessage(admin);
@@ -5807,6 +5913,7 @@
      */
     public void setLongSupportMessage(@NonNull ComponentName admin,
             @Nullable CharSequence message) {
+        throwIfParentInstance("setLongSupportMessage");
         if (mService != null) {
             try {
                 mService.setLongSupportMessage(admin, message);
@@ -5825,6 +5932,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public CharSequence getLongSupportMessage(@NonNull ComponentName admin) {
+        throwIfParentInstance("getLongSupportMessage");
         if (mService != null) {
             try {
                 return mService.getLongSupportMessage(admin);
@@ -5922,6 +6030,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
+        throwIfParentInstance("getParentProfileInstance");
         try {
             if (!mService.isManagedProfile(admin)) {
                 throw new SecurityException("The current user does not have a parent profile.");
@@ -5948,6 +6057,7 @@
      * @see #retrieveSecurityLogs
      */
     public void setSecurityLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
+        throwIfParentInstance("setSecurityLoggingEnabled");
         try {
             mService.setSecurityLoggingEnabled(admin, enabled);
         } catch (RemoteException re) {
@@ -5966,6 +6076,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean isSecurityLoggingEnabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("isSecurityLoggingEnabled");
         try {
             return mService.isSecurityLoggingEnabled(admin);
         } catch (RemoteException re) {
@@ -5989,6 +6100,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public List<SecurityEvent> retrieveSecurityLogs(@NonNull ComponentName admin) {
+        throwIfParentInstance("retrieveSecurityLogs");
         try {
             ParceledListSlice<SecurityEvent> list = mService.retrieveSecurityLogs(admin);
             if (list != null) {
@@ -6034,6 +6146,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public List<SecurityEvent> retrievePreRebootSecurityLogs(@NonNull ComponentName admin) {
+        throwIfParentInstance("retrievePreRebootSecurityLogs");
         try {
             ParceledListSlice<SecurityEvent> list = mService.retrievePreRebootSecurityLogs(admin);
             return list.getList();
@@ -6055,6 +6168,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setOrganizationColor(@NonNull ComponentName admin, int color) {
+        throwIfParentInstance("setOrganizationColor");
         try {
             // always enforce alpha channel to have 100% opacity
             color |= 0xFF000000;
@@ -6094,6 +6208,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public @ColorInt int getOrganizationColor(@NonNull ComponentName admin) {
+        throwIfParentInstance("getOrganizationColor");
         try {
             return mService.getOrganizationColor(admin);
         } catch (RemoteException re) {
@@ -6129,6 +6244,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setOrganizationName(@NonNull ComponentName admin, @Nullable CharSequence title) {
+        throwIfParentInstance("setOrganizationName");
         try {
             mService.setOrganizationName(admin, title);
         } catch (RemoteException re) {
@@ -6145,6 +6261,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public CharSequence getOrganizationName(@NonNull ComponentName admin) {
+        throwIfParentInstance("getOrganizationName");
         try {
             return mService.getOrganizationName(admin);
         } catch (RemoteException re) {
@@ -6176,6 +6293,7 @@
     @SystemApi
     @UserProvisioningState
     public int getUserProvisioningState() {
+        throwIfParentInstance("getUserProvisioningState");
         if (mService != null) {
             try {
                 return mService.getUserProvisioningState();
@@ -6222,6 +6340,7 @@
      * @param ids A set of opaque affiliation ids.
      */
     public void setAffiliationIds(@NonNull ComponentName admin, Set<String> ids) {
+        throwIfParentInstance("setAffiliationIds");
         try {
             mService.setAffiliationIds(admin, new ArrayList<String>(ids));
         } catch (RemoteException e) {
@@ -6237,6 +6356,7 @@
      * @return whether this user/profile is affiliated with the device.
      */
     public boolean isAffiliatedUser() {
+        throwIfParentInstance("isAffiliatedUser");
         try {
             return mService != null && mService.isAffiliatedUser();
         } catch (RemoteException e) {
@@ -6270,4 +6390,10 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    private void throwIfParentInstance(String functionName) {
+        if (mParentInstance) {
+            throw new SecurityException(functionName + " cannot be called on the parent instance");
+        }
+    }
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 4756b372..271ec79 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -517,7 +517,8 @@
 
         if (mService != null) try {
             mEnrollmentCallback = callback;
-            mService.enroll(mToken, token, userId, mServiceReceiver, flags);
+            mService.enroll(mToken, token, userId, mServiceReceiver, flags,
+                    mContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.w(TAG, "Remote exception in enroll: ", e);
             if (callback != null) {
diff --git a/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl b/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl
index 9c13523..f40f8a3 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl
@@ -35,4 +35,6 @@
     int closeHal();
     void init(IFingerprintDaemonCallback callback);
     int postEnroll();
+    int enumerate();
+    int cancelEnumeration();
 }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 43d5577..d7915e3 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -35,7 +35,7 @@
 
     // Start fingerprint enrollment
     void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
-            int flags);
+            int flags, String opPackageName);
 
     // Cancel enrollment in progress
     void cancelEnrollment(IBinder token);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 5d008e3..9070ad9 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1131,7 +1131,8 @@
      * implementation+feature combination, except that the value {@code -1}
      * always indicates failure.
      *
-     * @deprecated Deprecated in favor of the cleaner {@link #unregisterNetworkCallback} API.
+     * @deprecated Deprecated in favor of the cleaner
+     *             {@link #unregisterNetworkCallback(NetworkCallback)} API.
      *             In {@link VERSION_CODES#M}, and above, this method is unsupported and will
      *             throw {@code UnsupportedOperationException} if called.
      */
@@ -2811,7 +2812,7 @@
      * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
      *
      * This {@link NetworkRequest} will live until released via
-     * {@link #unregisterNetworkCallback} or the calling application exits.
+     * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits.
      * Status of the request can be followed by listening to the various
      * callbacks described in {@link NetworkCallback}.  The {@link Network}
      * can be used to direct traffic to the network.
@@ -2848,7 +2849,7 @@
      * This function behaves identically to the non-timedout version, but if a suitable
      * network is not found within the given time (in milliseconds) the
      * {@link NetworkCallback#unavailable} callback is called.  The request must
-     * still be released normally by calling {@link unregisterNetworkCallback}.
+     * still be released normally by calling {@link unregisterNetworkCallback(NetworkCallback)}.
      *
      * <p>This method requires the caller to hold either the
      * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
@@ -2956,7 +2957,8 @@
     /**
      * Removes a request made via {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)}
      * <p>
-     * This method has the same behavior as {@link #unregisterNetworkCallback} with respect to
+     * This method has the same behavior as
+     * {@link #unregisterNetworkCallback(android.app.PendingIntent)} with respect to
      * releasing network resources and disconnecting.
      *
      * @param operation A PendingIntent equal (as defined by {@link Intent#filterEquals}) to the
@@ -2982,7 +2984,7 @@
     /**
      * Registers to receive notifications about all networks which satisfy the given
      * {@link NetworkRequest}.  The callbacks will continue to be called until
-     * either the application exits or {@link #unregisterNetworkCallback} is called
+     * either the application exits or link #unregisterNetworkCallback(NetworkCallback)} is called.
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
@@ -3035,9 +3037,9 @@
     }
 
     /**
-     * Registers to receive notifications about whichever network currently satisfies the
-     * system default {@link NetworkRequest}.  The callbacks will continue to be called until
-     * either the application exits or {@link #unregisterNetworkCallback} is called
+     * Registers to receive notifications about changes in the system default network. The callbacks
+     * will continue to be called until either the application exits or
+     * {@link #unregisterNetworkCallback(NetworkCallback)} is called.
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
@@ -3060,8 +3062,9 @@
      * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
      * network connection for updated bandwidth information. The caller will be notified via
      * {@link ConnectivityManager.NetworkCallback} if there is an update. Notice that this
-     * method assumes that the caller has previously called {@link #registerNetworkCallback} to
-     * listen for network changes.
+     * method assumes that the caller has previously called
+     * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} to listen for network
+     * changes.
      *
      * @param network {@link Network} specifying which network you're interested.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
@@ -3076,8 +3079,9 @@
 
     /**
      * Unregisters callbacks about and possibly releases networks originating from
-     * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and {@link #registerNetworkCallback}
-     * calls.  If the given {@code NetworkCallback} had previously been used with
+     * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and
+     * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} calls.
+     * If the given {@code NetworkCallback} had previously been used with
      * {@code #requestNetwork}, any networks that had been connected to only to satisfy that request
      * will be disconnected.
      *
diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java
new file mode 100644
index 0000000..200b816
--- /dev/null
+++ b/core/java/android/net/metrics/DnsEvent.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@hide}
+ */
+public class DnsEvent extends IpConnectivityEvent implements Parcelable {
+    public final int netId;
+
+    // The event type is currently only 1 or 2, so we store it as a byte.
+    public final byte[] eventTypes;
+    // Current getaddrinfo codes go from 1 to EAI_MAX = 15. gethostbyname returns errno, but there
+    // are fewer than 255 errno values. So we store the result code in a byte as well.
+    public final byte[] returnCodes;
+    // The latency is an integer because a) short arrays aren't parcelable and b) a short can only
+    // store a maximum latency of 32757 or 65535 ms, which is too short for pathologically slow
+    // queries.
+    public final int[] latenciesMs;
+
+    private DnsEvent(int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) {
+        this.netId = netId;
+        this.eventTypes = eventTypes;
+        this.returnCodes = returnCodes;
+        this.latenciesMs = latenciesMs;
+    }
+
+    private DnsEvent(Parcel in) {
+        netId = in.readInt();
+        eventTypes = in.createByteArray();
+        returnCodes = in.createByteArray();
+        latenciesMs = in.createIntArray();
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(netId);
+        out.writeByteArray(eventTypes);
+        out.writeByteArray(returnCodes);
+        out.writeIntArray(latenciesMs);
+    }
+
+    public static final Parcelable.Creator<DnsEvent> CREATOR = new Parcelable.Creator<DnsEvent>() {
+        @Override
+        public DnsEvent createFromParcel(Parcel in) {
+            return new DnsEvent(in);
+        }
+
+        @Override
+        public DnsEvent[] newArray(int size) {
+            return new DnsEvent[size];
+        }
+    };
+
+    public static void logEvent(
+            int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) {
+        IpConnectivityEvent.logEvent(IPCE_DNS_LOOKUPS,
+                new DnsEvent(netId, eventTypes, returnCodes, latenciesMs));
+    }
+}
diff --git a/core/java/android/net/metrics/IpConnectivityEvent.java b/core/java/android/net/metrics/IpConnectivityEvent.java
index 59c1cfe..f0a3c90 100644
--- a/core/java/android/net/metrics/IpConnectivityEvent.java
+++ b/core/java/android/net/metrics/IpConnectivityEvent.java
@@ -24,31 +24,39 @@
  * {@hide}
  */
 public class IpConnectivityEvent implements Parcelable {
-    // IPRM = IpReachabilityMonitor
-    // DHCP = DhcpClient
+    public static final String TAG = "IpConnectivityEvent";
+
+    // IPRM   = IpReachabilityMonitor
+    // DHCP   = DhcpClient
     // NETMON = NetworkMonitorEvent
     // CONSRV = ConnectivityServiceEvent
-    // IPMGR = IpManager
-    public static final String TAG = "IpConnectivityEvent";
-    public static final int IPCE_IPRM_BASE = 0*1024;
-    public static final int IPCE_DHCP_BASE = 1*1024;
-    public static final int IPCE_NETMON_BASE = 2*1024;
-    public static final int IPCE_CONSRV_BASE = 3*1024;
-    public static final int IPCE_IPMGR_BASE = 4*1024;
+    // IPMGR  = IpManager
+    public static final int IPCE_IPRM_BASE                 = 0 * 1024;
+    public static final int IPCE_DHCP_BASE                 = 1 * 1024;
+    public static final int IPCE_NETMON_BASE               = 2 * 1024;
+    public static final int IPCE_CONSRV_BASE               = 3 * 1024;
+    public static final int IPCE_IPMGR_BASE                = 4 * 1024;
+    public static final int IPCE_DNS_BASE                  = 5 * 1024;
 
-    public static final int IPCE_IPRM_PROBE_RESULT = IPCE_IPRM_BASE + 0;
-    public static final int IPCE_IPRM_MESSAGE_RECEIVED = IPCE_IPRM_BASE + 1;
-    public static final int IPCE_IPRM_REACHABILITY_LOST = IPCE_IPRM_BASE + 2;
-    public static final int IPCE_DHCP_RECV_ERROR = IPCE_DHCP_BASE + 0;
-    public static final int IPCE_DHCP_PARSE_ERROR = IPCE_DHCP_BASE + 1;
-    public static final int IPCE_DHCP_TIMEOUT = IPCE_DHCP_BASE + 2;
-    public static final int IPCE_DHCP_STATE_CHANGE = IPCE_DHCP_BASE + 3;
-    public static final int IPCE_NETMON_STATE_CHANGE = IPCE_NETMON_BASE + 0;
-    public static final int IPCE_NETMON_CHECK_RESULT = IPCE_NETMON_BASE + 1;
+    public static final int IPCE_IPRM_PROBE_RESULT         = IPCE_IPRM_BASE + 0;
+    public static final int IPCE_IPRM_MESSAGE_RECEIVED     = IPCE_IPRM_BASE + 1;
+    public static final int IPCE_IPRM_REACHABILITY_LOST    = IPCE_IPRM_BASE + 2;
+
+    public static final int IPCE_DHCP_RECV_ERROR           = IPCE_DHCP_BASE + 0;
+    public static final int IPCE_DHCP_PARSE_ERROR          = IPCE_DHCP_BASE + 1;
+    public static final int IPCE_DHCP_TIMEOUT              = IPCE_DHCP_BASE + 2;
+    public static final int IPCE_DHCP_STATE_CHANGE         = IPCE_DHCP_BASE + 3;
+
+    public static final int IPCE_NETMON_STATE_CHANGE       = IPCE_NETMON_BASE + 0;
+    public static final int IPCE_NETMON_CHECK_RESULT       = IPCE_NETMON_BASE + 1;
+
     public static final int IPCE_CONSRV_DEFAULT_NET_CHANGE = IPCE_CONSRV_BASE + 0;
-    public static final int IPCE_IPMGR_PROVISIONING_OK = IPCE_IPMGR_BASE + 0;
-    public static final int IPCE_IPMGR_PROVISIONING_FAIL = IPCE_IPMGR_BASE + 1;
-    public static final int IPCE_IPMGR_COMPLETE_LIFECYCLE = IPCE_IPMGR_BASE + 2;
+
+    public static final int IPCE_IPMGR_PROVISIONING_OK     = IPCE_IPMGR_BASE + 0;
+    public static final int IPCE_IPMGR_PROVISIONING_FAIL   = IPCE_IPMGR_BASE + 1;
+    public static final int IPCE_IPMGR_COMPLETE_LIFECYCLE  = IPCE_IPMGR_BASE + 2;
+
+    public static final int IPCE_DNS_LOOKUPS               = IPCE_DNS_BASE + 0;
 
     private static ConnectivityMetricsLogger mMetricsLogger = new ConnectivityMetricsLogger();
 
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 67d3959..55b0d2a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -20,6 +20,7 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.content.pm.UserInfo;
+import android.content.IntentSender;
 import android.content.RestrictionEntry;
 import android.graphics.Bitmap;
 import android.os.ParcelFileDescriptor;
@@ -70,6 +71,7 @@
     boolean markGuestForDeletion(int userHandle);
     void setQuietModeEnabled(int userHandle, boolean enableQuietMode);
     boolean isQuietModeEnabled(int userHandle);
+    boolean trySetQuietModeDisabled(int userHandle, in IntentSender target);
     void setSeedAccountData(int userHandle, in String accountName,
             in String accountType, in PersistableBundle accountOptions, boolean persist);
     String getSeedAccountName();
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d5b3b35..086a977 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -29,6 +29,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -1691,6 +1692,23 @@
     }
 
     /**
+     * Tries disabling quiet mode for a given user. If the user is still locked, we unlock the user
+     * first by showing the confirm credentials screen and disable quiet mode upon successful
+     * unlocking. If the user is already unlocked, we call through to {@link #setQuietModeEnabled}
+     * directly.
+     *
+     * @return true if the quiet mode was disabled immediately
+     * @hide
+     */
+    public boolean trySetQuietModeDisabled(@UserIdInt int userHandle, IntentSender target) {
+        try {
+            return mService.trySetQuietModeDisabled(userHandle, target);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * If the target user is a managed profile of the calling user or the caller
      * is itself a managed profile, then this returns a badged copy of the given
      * icon to be able to distinguish it from the original icon. For badging an
diff --git a/core/java/android/view/IDockedStackListener.aidl b/core/java/android/view/IDockedStackListener.aidl
index 88ac271..36a81db 100644
--- a/core/java/android/view/IDockedStackListener.aidl
+++ b/core/java/android/view/IDockedStackListener.aidl
@@ -44,6 +44,15 @@
     void onDockedStackMinimizedChanged(boolean minimized, long animDuration);
 
     /**
+     * Called when window manager decides to adjust the divider for IME. Like the minimized state,
+     * the divider should make itself not interactable and shrink a bit, but in a different way.s
+     *
+     * @param minimized Whether the stacks are currently adjusted for the IME
+     * @param animDuration The duration of the animation for changing the adjusted state.
+     */
+    void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration);
+
+    /**
      * Called when window manager repositioned the docked stack after a screen rotation change.
      */
     void onDockSideChanged(int newDockSide);
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 27588e9..d24cefe 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -107,9 +107,8 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE && which == DialogInterface.BUTTON_POSITIVE) {
-            UserManager.get(this).setQuietModeEnabled(mUserId, false);
-
-            if (mTarget != null) {
+            if (UserManager.get(this).trySetQuietModeDisabled(mUserId, mTarget)
+                    && mTarget != null) {
                 try {
                     startIntentSenderForResult(mTarget, -1, null, 0, 0, 0);
                 } catch (IntentSender.SendIntentException e) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 9e5c238..36e21b9 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -63,7 +63,7 @@
     void toggleSplitScreen();
     void preloadRecentApps();
     void cancelPreloadRecentApps();
-    void showScreenPinningRequest();
+    void showScreenPinningRequest(int taskId);
 
     void toggleKeyboardShortcutsMenu(int deviceId);
 
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 22a81d4..d681246 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -752,17 +752,32 @@
             getPremulBitmapCreateFlags(isMutable));
 }
 
-static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) {
-    SkBitmap src;
-    reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+static Bitmap* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& dstCT) {
     SkBitmap result;
 
     AshmemPixelAllocator allocator(env);
-    if (!src.copyTo(&result, &allocator)) {
+    if (!src.copyTo(&result, dstCT, &allocator)) {
         return NULL;
     }
     Bitmap* bitmap = allocator.getStorageObjAndReset();
     bitmap->peekAtPixelRef()->setImmutable();
+    return bitmap;
+}
+
+static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) {
+    SkBitmap src;
+    reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+    SkColorType dstCT = src.colorType();
+    Bitmap* bitmap = Bitmap_copyAshmemImpl(env, src, dstCT);
+    jobject ret = GraphicsJNI::createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
+    return ret;
+}
+
+static jobject Bitmap_copyAshmemConfig(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle) {
+    SkBitmap src;
+    reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+    SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
+    Bitmap* bitmap = Bitmap_copyAshmemImpl(env, src, dstCT);
     jobject ret = GraphicsJNI::createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
     return ret;
 }
@@ -1355,6 +1370,8 @@
         (void*)Bitmap_copy },
     {   "nativeCopyAshmem",         "(J)Landroid/graphics/Bitmap;",
         (void*)Bitmap_copyAshmem },
+    {   "nativeCopyAshmemConfig",   "(JI)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_copyAshmemConfig },
     {   "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer },
     {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },
     {   "nativeReconfigure",        "(JIIIIZ)V", (void*)Bitmap_reconfigure },
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index f46f45c..898cf77 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -815,10 +815,15 @@
             }
 
             if (prevCp != kStartOfString &&
-                ((0xFE00 <= cp && cp <= 0xFE0F) || (0xE0100 <= cp && cp <= 0xE01EF)) &&
-                !MinikinUtils::hasVariationSelector(typeface, prevCp, cp)) {
-                // No font has a glyph for the code point and variation selector pair.
-                return false;
+                ((0xFE00 <= cp && cp <= 0xFE0F) || (0xE0100 <= cp && cp <= 0xE01EF))) {
+                bool hasVS = MinikinUtils::hasVariationSelector(typeface, prevCp, cp);
+                if (!hasVS) {
+                    // No font has a glyph for the code point and variation selector pair.
+                    return false;
+                } else if (nChars == 1 && i + 1 == str.size()) {
+                    // The string is just a codepoint and a VS, we have an authoritative answer
+                    return true;
+                }
             }
             nChars++;
             prevCp = cp;
diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml
index 117774a..575404d 100644
--- a/core/res/res/anim/input_method_exit.xml
+++ b/core/res/res/anim/input_method_exit.xml
@@ -17,10 +17,10 @@
 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
 	android:shareInterpolator="false">
-    <translate android:fromYDelta="0" android:toYDelta="10%"
-			android:interpolator="@interpolator/accelerate_quint"
-            android:duration="@android:integer/config_shortAnimTime"/>
+    <translate android:fromYDelta="0" android:toYDelta="8%"
+			android:interpolator="@interpolator/fast_out_linear_in"
+            android:duration="150"/>
     <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-			android:interpolator="@interpolator/accelerate_cubic"
-            android:duration="@android:integer/config_shortAnimTime"/>
+			android:interpolator="@interpolator/fast_out_linear_in"
+            android:duration="150"/>
 </set>
diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml
index b652127..65a5015 100644
--- a/core/res/res/layout/notification_template_right_icon.xml
+++ b/core/res/res/layout/notification_template_right_icon.xml
@@ -16,8 +16,8 @@
   -->
 
 <ImageView android:id="@+id/right_icon" xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/notification_large_icon_width"
-    android:layout_height="@dimen/notification_large_icon_width"
+    android:layout_width="40dp"
+    android:layout_height="40dp"
     android:layout_marginEnd="@dimen/notification_content_margin_end"
     android:layout_marginTop="36dp"
     android:layout_gravity="top|end"
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 9178305..8200df8 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -162,9 +162,9 @@
     <dimen name="notification_min_height">92dp</dimen>
 
     <!-- The width of the big icons in notifications. -->
-    <dimen name="notification_large_icon_width">40dp</dimen>
+    <dimen name="notification_large_icon_width">64dp</dimen>
     <!-- The width of the big icons in notifications. -->
-    <dimen name="notification_large_icon_height">40dp</dimen>
+    <dimen name="notification_large_icon_height">64dp</dimen>
 
     <!-- The minimum width of the app name in the header if it shrinks -->
     <dimen name="notification_header_shrink_min_width">72dp</dimen>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 169ef0b..ac2a88a 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -25,14 +25,14 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.io.OutputStream;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 import java.nio.ShortBuffer;
 
-import libcore.util.NativeAllocationRegistry;
-
 public final class Bitmap implements Parcelable {
     private static final String TAG = "Bitmap";
 
@@ -614,6 +614,22 @@
     }
 
     /**
+     * Creates a new immutable bitmap backed by ashmem which can efficiently
+     * be passed between processes.
+     *
+     * @hide
+     */
+    public Bitmap createAshmemBitmap(Config config) {
+        checkRecycled("Can't copy a recycled bitmap");
+        Bitmap b = nativeCopyAshmemConfig(mNativePtr, config.nativeInt);
+        if (b != null) {
+            b.setPremultiplied(mRequestPremultiplied);
+            b.mDensity = mDensity;
+        }
+        return b;
+    }
+
+    /**
      * Creates a new bitmap, scaled from an existing bitmap, when possible. If the
      * specified width and height are the same as the current width and height of
      * the source bitmap, the source bitmap is returned and no new bitmap is
@@ -1675,6 +1691,7 @@
     private static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig,
                                             boolean isMutable);
     private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap);
+    private static native Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig);
     private static native long nativeGetNativeFinalizer();
     private static native boolean nativeRecycle(long nativeBitmap);
     private static native void nativeReconfigure(long nativeBitmap, int width, int height,
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 715c875..6913f43 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -34,7 +34,9 @@
 #include <utils/String8.h>
 #include <utils/threads.h>
 #include <utils/Timers.h>
-#include <utils/Trace.h>
+#ifdef __ANDROID__
+#include <cutils/trace.h>
+#endif
 
 #include <assert.h>
 #include <dirent.h>
@@ -52,6 +54,14 @@
     _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;
@@ -613,7 +623,7 @@
     ResTable* sharedRes = NULL;
     bool shared = true;
     bool onlyEmptyResources = true;
-    ATRACE_NAME(ap.path.string());
+    MY_TRACE_BEGIN(ap.path.string());
     Asset* idmap = openIdmapLocked(ap);
     size_t nextEntryIdx = mResources->getTableCount();
     ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
@@ -693,6 +703,8 @@
     if (idmap != NULL) {
         delete idmap;
     }
+    MY_TRACE_END();
+
     return onlyEmptyResources;
 }
 
@@ -740,7 +752,6 @@
 
 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 15cb684..1ccc59a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -24,7 +24,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <algorithm>
 #include <limits>
 #include <memory>
 #include <type_traits>
@@ -5811,10 +5810,6 @@
     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();
@@ -5845,11 +5840,17 @@
                     ResTable_config cfg;
                     memset(&cfg, 0, sizeof(ResTable_config));
                     cfg.copyFromDtoH(config->config);
-
-                    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));
+                    // 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);
                     }
                 }
             }
@@ -5857,10 +5858,6 @@
     }
 }
 
-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;
@@ -5875,11 +5872,15 @@
     char locale[RESTABLE_MAX_LOCALE_LEN];
     for (size_t i=0; i<I; i++) {
         configs[i].getBcp47Locale(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));
+        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));
         }
     }
 }
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index b8b4625..7cd7fb5 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -39,20 +39,8 @@
  */
 #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));
@@ -336,25 +324,4 @@
     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 ff9be16..ac80d88 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 a.compare(b) == 0;
+    return memcmp(&a, &b, sizeof(a)) == 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 6a31fb8..27f25fe 100644
--- a/libs/androidfw/tests/data/system/R.h
+++ b/libs/androidfw/tests/data/system/R.h
@@ -33,12 +33,6 @@
     };
 }
 
-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
deleted file mode 100644
index b97bdb6..0000000
--- a/libs/androidfw/tests/data/system/res/values-sv/values.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<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 b0dab6b..215ecae 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, 0xf8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x0c, 0x00, 0x18, 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, 0xd0, 0x03, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf0, 0x02, 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,33 +25,26 @@
   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,
-  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,
+  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,
   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, 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,
+  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,
   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,
@@ -62,27 +55,15 @@
   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, 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,
+  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,
   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, 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
+  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
 };
-unsigned int system_arsc_len = 1016;
+unsigned int system_arsc_len = 792;
diff --git a/media/jni/android_media_ExifInterface.cpp b/media/jni/android_media_ExifInterface.cpp
index 418a3f2..ae23720 100644
--- a/media/jni/android_media_ExifInterface.cpp
+++ b/media/jni/android_media_ExifInterface.cpp
@@ -136,7 +136,8 @@
 
     KeyedVector<String8, String8> map;
 
-    if (image_data.thumbnail.length > 0) {
+    if (image_data.thumbnail.length > 0
+            && image_data.thumbnail.format == ::piex::Image::kJpegCompressed) {
         map.add(String8("hasThumbnail"), String8("true"));
         map.add(String8("thumbnailOffset"), String8::format("%d", image_data.thumbnail.offset));
         map.add(String8("thumbnailLength"), String8::format("%d", image_data.thumbnail.length));
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 39f2a32..d6bf9ad 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -932,8 +932,9 @@
                     break;
                 }
 
-                if (image_data.thumbnail.length == 0) {
-                    // No thumbnail.
+                if (image_data.thumbnail.length == 0
+                        || image_data.thumbnail.format == ::piex::Image::kJpegCompressed) {
+                    // No thumbnail or non jpeg thumbnail.
                     break;
                 }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 0e27622..2be93b8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -17,6 +17,7 @@
 package com.android.documentsui;
 
 import static com.android.documentsui.Shared.TAG;
+import static com.android.documentsui.State.ACTION_CREATE;
 
 import android.app.Fragment;
 import android.app.FragmentManager;
@@ -107,8 +108,9 @@
                 mAdapter.update(data);
 
                 // When launched into empty recents, show drawer
-                if (mAdapter.isEmpty() && !state.hasLocationChanged() &&
-                        context instanceof DocumentsActivity) {
+                if (mAdapter.isEmpty() && !state.hasLocationChanged()
+                        && state.action != ACTION_CREATE
+                        && context instanceof DocumentsActivity) {
                     ((DocumentsActivity) context).setRootsDrawerOpen(true);
                 }
             }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index e6217b2..e3eae33 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -90,6 +90,10 @@
      */
     private static final class DocumentsTuner extends FragmentTuner {
 
+        // We use this to keep track of whether a model has been previously loaded or not so we can
+        // open the drawer on empty directories on first launch
+        private boolean mModelPreviousLoaded;
+
         public DocumentsTuner(Context context, State state) {
             super(context, state);
         }
@@ -178,10 +182,12 @@
                 showDrawer = true;
             }
 
-            if (showDrawer && !mState.hasInitialLocationChanged() && !isSearch) {
+            if (showDrawer && !mState.hasInitialLocationChanged() && !isSearch
+                    && !mModelPreviousLoaded) {
                 // This noops on layouts without drawer, so no need to guard.
                 ((BaseActivity) mContext).setRootsDrawerOpen(true);
             }
+            mModelPreviousLoaded = true;
         }
 
         @Override
diff --git a/packages/SettingsLib/res/drawable/notification_auto_importance.xml b/packages/SettingsLib/res/drawable/notification_auto_importance.xml
new file mode 100644
index 0000000..a63e911b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/notification_auto_importance.xml
@@ -0,0 +1,27 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+            android:fillColor="#FFFFFFFF"
+            android:pathData="M11.2,13.6l1.6,0l-0.8,-2.6z"/>
+    <path
+            android:fillColor="#FF000000"
+            android:pathData="M22.5,9.6L15,9l-3,-7L9,9L1.5,9.6l5.7,5L5.5,22l6.5,-3.9l6.5,3.9l-1.7,-7.4L22.5,9.6zM13.6,16l-0.5,-1.4h-2.3L10.4,16H9l2.3,-6.4h1.4L15,16H13.6z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 062ae35..e1424f0 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -32,7 +32,7 @@
     <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingTop="14dp"
+            android:paddingTop="15dp"
             android:paddingEnd="8dp"
             android:id="@+id/notification_guts_header"
             android:orientation="horizontal"
@@ -99,6 +99,7 @@
             android:clickable="false"
             android:focusable="false"
             android:paddingEnd="8dp"
+            android:paddingTop="4dp"
             android:visibility="gone">
         <TextView
                 android:id="@+id/title"
@@ -123,21 +124,20 @@
 
         <FrameLayout
                 android:layout_width="match_parent"
-                android:layout_height="48dp"
+                android:layout_height="wrap_content"
                 android:paddingTop="8dp" >
 
             <ImageView
-                    android:id="@+id/low_importance"
-                    android:src="@*android:drawable/ic_notification_block"
+                    android:id="@+id/auto_importance"
+                    android:src="@drawable/notification_auto_importance"
                     android:layout_gravity="center_vertical|start"
-                    android:layout_width="24dp"
-                    android:layout_height="24dp"
-                    android:tint="@color/notification_guts_icon_tint"/>
+                    android:layout_width="48dp"
+                    android:layout_height="48dp" />
 
             <SeekBar
                     android:id="@+id/seekbar"
                     android:layout_marginStart="56dp"
-                    android:layout_marginEnd="56dp"
+                    android:layout_marginEnd="32dp"
                     android:layout_gravity="center_vertical"
                     android:layout_width="match_parent"
                     android:layout_height="48dp"
@@ -149,14 +149,6 @@
                     style="@android:style/Widget.Material.SeekBar.Discrete"
                     android:tickMarkTint="@android:color/black" />
 
-            <ImageView
-                    android:id="@+id/max_importance"
-                    android:src="@*android:drawable/ic_notification_alert"
-                    android:layout_gravity="center_vertical|end"
-                    android:layout_width="24dp"
-                    android:layout_height="24dp"
-                    android:tint="@color/notification_guts_icon_tint" />
-
         </FrameLayout>
     </LinearLayout>
     <!-- buttons -->
diff --git a/packages/SystemUI/res/layout/power_notification_controls_settings.xml b/packages/SystemUI/res/layout/power_notification_controls_settings.xml
new file mode 100644
index 0000000..83c8a51
--- /dev/null
+++ b/packages/SystemUI/res/layout/power_notification_controls_settings.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <include layout="@layout/switch_bar" />
+
+    <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:padding="16dp"
+            android:text="@string/power_notification_controls_description"/>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 2df57bf..5ee242d 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -43,7 +43,8 @@
         android:singleLine="true"
         android:maxLines="1"
         android:ellipsize="marquee"
-        android:fadingEdge="horizontal" />
+        android:fadingEdge="horizontal"
+        android:forceHasOverlappingRendering="false" />
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/move_task"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d26fb06..c75741c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -108,6 +108,7 @@
     <!-- The "inside" of a notification, reached via longpress -->
     <color name="notification_guts_bg_color">#eeeeee</color>
     <color name="notification_guts_slider_color">@*android:color/material_deep_teal_500</color>
+    <color name="notification_guts_disabled_slider_color">@*android:color/material_grey_300</color>
     <color name="notification_guts_secondary_slider_color">#858383</color>
     <color name="notification_guts_icon_tint">#8a000000</color>
     <color name="notification_guts_disabled_icon_tint">#4d000000</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a4d7a18..37b00bb 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1225,38 +1225,69 @@
     <string name="do_not_silence_block">Don\'t silence or block</string>
 
     <!-- [CHAR LIMIT=NONE] Importance Tuner setting title -->
-    <string name="tuner_full_importance_settings">Show full importance settings</string>
+    <string name="tuner_full_importance_settings">Power notification controls</string>
+    <string name="tuner_full_importance_settings_on">On</string>
+    <string name="tuner_full_importance_settings_off">Off</string>
+    <string name="power_notification_controls_description">With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications.
+        \n\n<b>Level 5</b>
+        \n- Show at the top of the notification list
+        \n- Allow full screen interruption
+        \n- Always peek
+        \n\n<b>Level 4</b>
+        \n- Prevent full screen interruption
+        \n- Always peek
+        \n\n<b>Level 3</b>
+        \n- Prevent full screen interruption
+        \n- Never peek
+        \n\n<b>Level 2</b>
+        \n- Prevent full screen interruption
+        \n- Never peek
+        \n- Never make sound and vibration
+        \n\n<b>Level 1</b>
+        \n- Prevent full screen interruption
+        \n- Never peek
+        \n- Never make sound or vibrate
+        \n- Hide from lock screen and status bar
+        \n- Show at the bottom of the notification list
+        \n\n<b>Level 0</b>
+        \n- Block all notifications from the app
+    </string>
 
+    <!-- Notification importance title, user unspecified status-->
+    <string name="user_unspecified_importance">Importance: Automatic</string>
     <!-- Notification importance title, blocked status-->
-    <string name="blocked_importance">Blocked</string>
+    <string name="blocked_importance">Importance: Level 0</string>
     <!-- Notification importance title, min status-->
-    <string name="min_importance">Min importance</string>
+    <string name="min_importance">Importance: Level 1</string>
     <!-- Notification importance title, low status-->
-    <string name="low_importance">Low importance</string>
+    <string name="low_importance">Importance: Level 2</string>
     <!-- Notification importance title, normal status-->
-    <string name="default_importance">Normal importance</string>
+    <string name="default_importance">Importance: Level 3</string>
     <!-- Notification importance title, high status-->
-    <string name="high_importance">High importance</string>
+    <string name="high_importance">Importance: Level 4</string>
     <!-- Notification importance title, max status-->
-    <string name="max_importance">Urgent importance</string>
+    <string name="max_importance">Importance: Level 5</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description -->
-    <string name="notification_importance_blocked">Never show these notifications</string>
+    <string name="notification_importance_user_unspecified">App determines importance for each notification.</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description -->
+    <string name="notification_importance_blocked">Never show notifications from this app.</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance slider: min importance level description -->
-    <string name="notification_importance_min">Silently show at the bottom of the notification list</string>
+    <string name="notification_importance_min">No full screen interruption, peeking, sound, or vibration. Hide from lock screen and status bar.</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance slider: low importance level description -->
-    <string name="notification_importance_low">Silently show these notifications</string>
+    <string name="notification_importance_low">No full screen interruption, peeking, sound, or vibration.</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance slider: normal importance level description -->
-    <string name="notification_importance_default">Allow these notification to make sounds</string>
+    <string name="notification_importance_default">No full screen interruption or peeking.</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance slider: high importance level description -->
-    <string name="notification_importance_high">Peek onto the screen and allow sound and allow sound</string>
+    <string name="notification_importance_high">Always peek. No full screen interruption.</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description -->
-    <string name="notification_importance_max">Show at the top of the notifications list, peek onto the screen and allow sound</string>
+    <string name="notification_importance_max">Always peek, and allow full screen interruption.</string>
 
     <!-- Notification: Control panel: Label for button that launches notification settings. [CHAR LIMIT=NONE] -->
     <string name="notification_more_settings">More settings</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 0730083..aeb484f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -33,6 +33,7 @@
         <item name="android:windowBackground">@color/transparent</item>
         <item name="android:colorBackgroundCacheHint">@null</item>
         <item name="android:windowShowWallpaper">true</item>
+        <item name="android:windowDisablePreview">true</item>
     </style>
 
     <!-- Performance optimized Recents theme (no wallpaper) -->
diff --git a/packages/SystemUI/res/xml/other_settings.xml b/packages/SystemUI/res/xml/other_settings.xml
new file mode 100644
index 0000000..3c872fa
--- /dev/null
+++ b/packages/SystemUI/res/xml/other_settings.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+                  xmlns:sysui="http://schemas.android.com/apk/res-auto"
+                  android:title="@string/other">
+
+    <com.android.systemui.tuner.TunerSwitch
+            android:key="overview_nav_bar_gesture"
+            android:title="@string/overview_nav_bar_gesture"
+            android:summary="@string/overview_nav_bar_gesture_desc" />
+
+    <!-- importance -->
+    <Preference
+            android:key="power_notification_controls"
+            android:title="@string/tuner_full_importance_settings"
+            android:fragment="com.android.systemui.tuner.PowerNotificationControlsFragment"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 1af9075..116bc69 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -135,21 +135,10 @@
         android:fragment="com.android.systemui.tuner.NavBarTuner" />
     -->
 
-    <PreferenceScreen
-        android:key="other"
-        android:title="@string/other" >
-
-        <com.android.systemui.tuner.TunerSwitch
-            android:key="overview_nav_bar_gesture"
-            android:title="@string/overview_nav_bar_gesture"
-            android:summary="@string/overview_nav_bar_gesture_desc" />
-
-        <!-- importance -->
-        <com.android.systemui.tuner.TunerSwitch
-                android:key="show_importance_slider"
-                android:title="@string/tuner_full_importance_settings" />
-
-    </PreferenceScreen>
+    <Preference
+            android:key="other"
+            android:title="@string/other"
+            android:fragment="com.android.systemui.tuner.OtherPrefs" />
 
     <!-- Warning, this goes last. -->
     <Preference
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1abd073..4d8e33d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1215,7 +1215,6 @@
 
     private void lockProfile(int userId) {
         mTrustManager.setDeviceLockedForUser(userId, true);
-        notifyLockedProfile(userId);
     }
 
     private boolean shouldWaitForProvisioning() {
@@ -1546,13 +1545,6 @@
         }
     }
 
-    private void notifyLockedProfile(@UserIdInt int userId) {
-        try {
-            ActivityManagerNative.getDefault().notifyLockedProfile(userId);
-        } catch (RemoteException e) {
-        }
-    }
-
     /**
      * Handle message sent by {@link #showLocked}.
      * @see #SHOW
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
index 9a00d95..1240e05 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -27,7 +27,7 @@
     void registerNonSystemUserCallbacks(IBinder nonSystemUserCallbacks, int userId);
 
     void updateRecentsVisibility(boolean visible);
-    void startScreenPinning();
+    void startScreenPinning(int taskId);
     void sendRecentsDrawnEvent();
     void sendDockingTopTaskEvent(int dragMode, in Rect initialRect);
     void sendLaunchRecentsEvent();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index b2d7b48..a227e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -598,13 +598,13 @@
     public final void onBusEvent(final ScreenPinningRequestEvent event) {
         int processUser = sSystemServicesProxy.getProcessUser();
         if (sSystemServicesProxy.isSystemUser(processUser)) {
-            mImpl.onStartScreenPinning(event.applicationContext);
+            mImpl.onStartScreenPinning(event.applicationContext, event.taskId);
         } else {
             postToSystemUser(new Runnable() {
                 @Override
                 public void run() {
                     try {
-                        mUserToSystemCallbacks.startScreenPinning();
+                        mUserToSystemCallbacks.startScreenPinning(event.taskId);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Callback failed", e);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index c230cd8..618a2e2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -221,11 +221,11 @@
      * visibility change events through to the system user via
      * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
      */
-    public void onStartScreenPinning(Context context) {
+    public void onStartScreenPinning(Context context, int taskId) {
         SystemUIApplication app = (SystemUIApplication) context;
         PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
         if (statusBar != null) {
-            statusBar.showScreenPinningRequest(false);
+            statusBar.showScreenPinningRequest(taskId, false);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
index ffeb4a1..913da18 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -82,8 +82,8 @@
     }
 
     @Override
-    public void startScreenPinning() {
-        mImpl.onStartScreenPinning(mContext);
+    public void startScreenPinning(int taskId) {
+        mImpl.onStartScreenPinning(mContext, taskId);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 13d4acb..7a604ca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -52,6 +52,9 @@
 
     private RequestWindowView mRequestWindow;
 
+    // Id of task to be pinned or locked.
+    private int taskId;
+
     public ScreenPinningRequest(Context context) {
         mContext = context;
         mAccessibilityService = (AccessibilityManager)
@@ -67,9 +70,11 @@
         }
     }
 
-    public void showPrompt(boolean allowCancel) {
+    public void showPrompt(int taskId, boolean allowCancel) {
         clearPrompt();
 
+        this.taskId = taskId;
+
         mRequestWindow = new RequestWindowView(mContext, allowCancel);
 
         mRequestWindow.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
@@ -106,7 +111,7 @@
     public void onClick(View v) {
         if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) {
             try {
-                ActivityManagerNative.getDefault().startLockTaskModeOnCurrent();
+                ActivityManagerNative.getDefault().startSystemLockTaskMode(taskId);
             } catch (RemoteException e) {}
         }
         clearPrompt();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
index 75e459a..d460917 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
@@ -26,8 +26,10 @@
 public class ScreenPinningRequestEvent extends EventBus.Event {
 
     public final Context applicationContext;
+    public final int taskId;
 
-    public ScreenPinningRequestEvent(Context context) {
+    public ScreenPinningRequestEvent(Context context, int taskId) {
         this.applicationContext = context.getApplicationContext();
+        this.taskId = taskId;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 2d5addb..63bcc2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -99,6 +99,7 @@
     static {
         sBitmapOptions = new BitmapFactory.Options();
         sBitmapOptions.inMutable = true;
+        sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
     }
 
     final static List<String> sRecentsBlacklist;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index fd8df99..db5413f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -77,12 +77,17 @@
     private Handler mHandler;
     private TaskViewTransform mTmpTransform = new TaskViewTransform();
 
-    private Runnable mStartScreenPinningRunnable = new Runnable() {
+    private class StartScreenPinningRunnableRunnable implements Runnable {
+
+        private int taskId = -1;
+
         @Override
         public void run() {
-            EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext));
+            EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext, taskId));
         }
-    };
+    }
+    private StartScreenPinningRunnableRunnable mStartScreenPinningRunnable
+            = new StartScreenPinningRunnableRunnable();
 
     public RecentsTransitionHelper(Context context) {
         mContext = context;
@@ -120,6 +125,7 @@
 
                     if (screenPinningRequested) {
                         // Request screen pinning after the animation runs
+                        mStartScreenPinningRunnable.taskId = task.key.id;
                         mHandler.postDelayed(mStartScreenPinningRunnable, 350);
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 2bf0b40..5d1a61d 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -39,6 +39,7 @@
     private DockDividerVisibilityListener mDockDividerVisibilityListener;
     private boolean mVisible = false;
     private boolean mMinimized = false;
+    private boolean mAdjustedForIme = false;
     private ForcedResizableInfoActivityController mForcedResizableController;
 
     @Override
@@ -84,7 +85,7 @@
         addDivider(configuration);
         if (mMinimized) {
             mView.setMinimizedDockStack(true);
-            mWindowManager.setTouchable(false);
+            updateTouchable();
         }
     }
 
@@ -109,7 +110,7 @@
             public void run() {
                 if (mMinimized != minimized) {
                     mMinimized = minimized;
-                    mWindowManager.setTouchable(!minimized);
+                    updateTouchable();
                     if (animDuration > 0) {
                         mView.setMinimizedDockStack(minimized, animDuration);
                     } else {
@@ -129,6 +130,10 @@
         });
     }
 
+    private void updateTouchable() {
+        mWindowManager.setTouchable(!mMinimized && !mAdjustedForIme);
+    }
+
     class DockDividerVisibilityListener extends IDockedStackListener.Stub {
 
         @Override
@@ -148,6 +153,22 @@
         }
 
         @Override
+        public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)
+                throws RemoteException {
+            mView.post(() -> {
+                if (mAdjustedForIme != adjustedForIme) {
+                    mAdjustedForIme = adjustedForIme;
+                    updateTouchable();
+                    if (animDuration > 0) {
+                        mView.setAdjustedForIme(adjustedForIme, animDuration);
+                    } else {
+                        mView.setAdjustedForIme(adjustedForIme);
+                    }
+                }
+            });
+        }
+
+        @Override
         public void onDockSideChanged(final int newDockSide) throws RemoteException {
             mView.post(() -> mView.notifyDockSideChanged(newDockSide));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 66a413c..4d1c6ba 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -31,7 +31,6 @@
 import android.graphics.Region.Op;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
-import android.os.Vibrator;
 import android.util.AttributeSet;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -61,7 +60,6 @@
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.recents.Constants.Metrics;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
@@ -98,6 +96,7 @@
      * How much the background gets scaled when we are in the minimized dock state.
      */
     private static final float MINIMIZE_DOCK_SCALE = 0f;
+    private static final float ADJUSTED_FOR_IME_SCALE = 0.5f;
 
     private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
             new PathInterpolator(0.5f, 1f, 0.5f, 1f);
@@ -147,6 +146,7 @@
     private int mExitStartPosition;
     private GestureDetector mGestureDetector;
     private boolean mDockedStackMinimized;
+    private boolean mAdjustedForIme;
 
     private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
         @Override
@@ -657,6 +657,40 @@
         mDockedStackMinimized = minimized;
     }
 
+    public void setAdjustedForIme(boolean adjustedForIme) {
+        updateDockSide();
+        mHandle.setAlpha(adjustedForIme ? 0f : 1f);
+        if (!adjustedForIme) {
+            resetBackground();
+        } else if (mDockSide == WindowManager.DOCKED_TOP) {
+            mBackground.setPivotY(0);
+            mBackground.setScaleY(MINIMIZE_DOCK_SCALE);
+        }
+        mAdjustedForIme = adjustedForIme;
+    }
+
+    public void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
+        updateDockSide();
+        mHandle.animate()
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .setDuration(animDuration)
+                .alpha(adjustedForIme ? 0f : 1f)
+                .start();
+        if (mDockSide == WindowManager.DOCKED_TOP) {
+            mBackground.setPivotY(0);
+            mBackground.animate()
+                    .scaleY(adjustedForIme ? MINIMIZE_DOCK_SCALE : 1f);
+        }
+        if (!adjustedForIme) {
+            mBackground.animate().withEndAction(mResetBackgroundRunnable);
+        }
+        mBackground.animate()
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .setDuration(animDuration)
+                .start();
+        mAdjustedForIme = adjustedForIme;
+    }
+
     private void resetBackground() {
         mBackground.setPivotX(mBackground.getWidth() / 2);
         mBackground.setPivotY(mBackground.getHeight() / 2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 99b6397..cc8e3bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -114,7 +114,7 @@
         void buzzBeepBlinked();
         void notificationLightOff();
         void notificationLightPulse(int argb, int onMillis, int offMillis);
-        void showScreenPinningRequest();
+        void showScreenPinningRequest(int taskId);
         void appTransitionPending();
         void appTransitionCancelled();
         void appTransitionStarting(long startTime, long duration);
@@ -298,9 +298,10 @@
         }
     }
 
-    public void showScreenPinningRequest() {
+    public void showScreenPinningRequest(int taskId) {
         synchronized (mLock) {
-            mHandler.sendEmptyMessage(MSG_SHOW_SCREEN_PIN_REQUEST);
+            mHandler.obtainMessage(MSG_SHOW_SCREEN_PIN_REQUEST, taskId, 0, null)
+                    .sendToTarget();
         }
     }
 
@@ -450,7 +451,7 @@
                     mCallbacks.notificationLightPulse((Integer) msg.obj, msg.arg1, msg.arg2);
                     break;
                 case MSG_SHOW_SCREEN_PIN_REQUEST:
-                    mCallbacks.showScreenPinningRequest();
+                    mCallbacks.showScreenPinningRequest(msg.arg1);
                     break;
                 case MSG_APP_TRANSITION_PENDING:
                     mCallbacks.appTransitionPending();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 3c464d5..057b020 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -22,12 +22,14 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.View;
@@ -60,10 +62,18 @@
     private int mActualHeight;
     private boolean mExposed;
     private INotificationManager mINotificationManager;
-    private int mStartingImportance;
+    private int mStartingUserImportance;
+    private int mNotificationImportance;
     private boolean mShowSlider;
 
     private SeekBar mSeekBar;
+    private ImageView mAutoButton;
+    private ColorStateList mActiveSliderTint;
+    private ColorStateList mInactiveSliderTint;
+    private TextView mImportanceSummary;
+    private TextView mImportanceTitle;
+    private boolean mAuto;
+
     private RadioButton mBlock;
     private RadioButton mSilent;
     private RadioButton mReset;
@@ -145,9 +155,14 @@
 
     void bindImportance(final PackageManager pm, final StatusBarNotification sbn,
             final ExpandableNotificationRow row, final int importance) {
-        mStartingImportance = importance;
         mINotificationManager = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mStartingUserImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+        try {
+            mStartingUserImportance =
+                    mINotificationManager.getImportance(sbn.getPackageName(), sbn.getUid());
+        } catch (RemoteException e) {}
+        mNotificationImportance = importance;
         boolean systemApp = false;
         try {
             final PackageInfo info =
@@ -160,29 +175,25 @@
         final View importanceSlider = row.findViewById(R.id.importance_slider);
         final View importanceButtons = row.findViewById(R.id.importance_buttons);
         if (mShowSlider) {
-            bindSlider(importanceSlider, sbn, systemApp);
+            bindSlider(importanceSlider, systemApp);
             importanceSlider.setVisibility(View.VISIBLE);
             importanceButtons.setVisibility(View.GONE);
         } else {
-            mStartingImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
-            try {
-                mStartingImportance =
-                        mINotificationManager.getImportance(sbn.getPackageName(), sbn.getUid());
-            } catch (RemoteException e) {}
-            bindToggles(importanceButtons, mStartingImportance, systemApp);
+
+            bindToggles(importanceButtons, mStartingUserImportance, systemApp);
             importanceButtons.setVisibility(View.VISIBLE);
             importanceSlider.setVisibility(View.GONE);
         }
     }
 
     public boolean hasImportanceChanged() {
-        return mStartingImportance != getSelectedImportance();
+        return mStartingUserImportance != getSelectedImportance();
     }
 
     void saveImportance(final StatusBarNotification sbn) {
         int progress = getSelectedImportance();
         MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
-                progress - mStartingImportance);
+                progress - mStartingUserImportance);
         try {
             mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(), progress);
         } catch (RemoteException e) {
@@ -192,14 +203,18 @@
 
     private int getSelectedImportance() {
         if (mSeekBar!= null && mSeekBar.isShown()) {
-            return mSeekBar.getProgress();
+            if (mSeekBar.isEnabled()) {
+                return mSeekBar.getProgress();
+            } else {
+                return Ranking.IMPORTANCE_UNSPECIFIED;
+            }
         } else {
             if (mBlock.isChecked()) {
-                return NotificationListenerService.Ranking.IMPORTANCE_NONE;
+                return Ranking.IMPORTANCE_NONE;
             } else if (mSilent.isChecked()) {
-                return NotificationListenerService.Ranking.IMPORTANCE_LOW;
+                return Ranking.IMPORTANCE_LOW;
             } else {
-                return NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+                return Ranking.IMPORTANCE_UNSPECIFIED;
             }
         }
     }
@@ -229,16 +244,14 @@
         }
     }
 
-    private void bindSlider(final View importanceSlider, final StatusBarNotification sbn,
-            final boolean systemApp) {
-        final TextView importanceSummary = ((TextView) importanceSlider.findViewById(R.id.summary));
-        final TextView importanceTitle = ((TextView) importanceSlider.findViewById(R.id.title));
+    private void bindSlider(final View importanceSlider, final boolean systemApp) {
+        mActiveSliderTint = loadColorStateList(R.color.notification_guts_slider_color);
+        mInactiveSliderTint = loadColorStateList(R.color.notification_guts_disabled_slider_color);
+
+        mImportanceSummary = ((TextView) importanceSlider.findViewById(R.id.summary));
+        mImportanceTitle = ((TextView) importanceSlider.findViewById(R.id.title));
         mSeekBar = (SeekBar) importanceSlider.findViewById(R.id.seekbar);
 
-        if (systemApp) {
-            ((ImageView) importanceSlider.findViewById(R.id.low_importance)).getDrawable().setTint(
-                    mContext.getColor(R.color.notification_guts_disabled_icon_tint));
-        }
         final int minProgress = systemApp ?
                 NotificationListenerService.Ranking.IMPORTANCE_MIN
                 : NotificationListenerService.Ranking.IMPORTANCE_NONE;
@@ -267,42 +280,80 @@
                 // no-op
             }
 
-            private void updateTitleAndSummary(int progress) {
-                switch (progress) {
-                    case NotificationListenerService.Ranking.IMPORTANCE_NONE:
-                        importanceSummary.setText(mContext.getString(
-                                R.string.notification_importance_blocked));
-                        importanceTitle.setText(mContext.getString(R.string.blocked_importance));
-                        break;
-                    case NotificationListenerService.Ranking.IMPORTANCE_MIN:
-                        importanceSummary.setText(mContext.getString(
-                                R.string.notification_importance_min));
-                        importanceTitle.setText(mContext.getString(R.string.min_importance));
-                        break;
-                    case NotificationListenerService.Ranking.IMPORTANCE_LOW:
-                        importanceSummary.setText(mContext.getString(
-                                R.string.notification_importance_low));
-                        importanceTitle.setText(mContext.getString(R.string.low_importance));
-                        break;
-                    case NotificationListenerService.Ranking.IMPORTANCE_DEFAULT:
-                        importanceSummary.setText(mContext.getString(
-                                R.string.notification_importance_default));
-                        importanceTitle.setText(mContext.getString(R.string.default_importance));
-                        break;
-                    case NotificationListenerService.Ranking.IMPORTANCE_HIGH:
-                        importanceSummary.setText(mContext.getString(
-                                R.string.notification_importance_high));
-                        importanceTitle.setText(mContext.getString(R.string.high_importance));
-                        break;
-                    case NotificationListenerService.Ranking.IMPORTANCE_MAX:
-                        importanceSummary.setText(mContext.getString(
-                                R.string.notification_importance_max));
-                        importanceTitle.setText(mContext.getString(R.string.max_importance));
-                        break;
-                }
+
+        });
+        mSeekBar.setProgress(mNotificationImportance);
+
+        mAutoButton = (ImageView) importanceSlider.findViewById(R.id.auto_importance);
+        mAutoButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mAuto = !mAuto;
+                applyAuto();
             }
         });
-        mSeekBar.setProgress(mStartingImportance);
+        mAuto = mStartingUserImportance == Ranking.IMPORTANCE_UNSPECIFIED;
+        applyAuto();
+    }
+
+    private void applyAuto() {
+        mSeekBar.setEnabled(!mAuto);
+
+        final ColorStateList tint = mAuto ? mInactiveSliderTint : mActiveSliderTint;
+        Drawable icon = mAutoButton.getDrawable().mutate();
+        icon.setTintList(tint);
+        mAutoButton.setImageDrawable(icon);
+        mSeekBar.setProgressTintList(tint);
+        mSeekBar.setThumbTintList(tint);
+
+        if (mAuto) {
+            mSeekBar.setProgress(mNotificationImportance);
+            mImportanceSummary.setText(mContext.getString(
+                    R.string.notification_importance_user_unspecified));
+            mImportanceTitle.setText(mContext.getString(
+                    R.string.user_unspecified_importance));
+        } else {
+            updateTitleAndSummary(mSeekBar.getProgress());
+        }
+    }
+
+    private void updateTitleAndSummary(int progress) {
+        switch (progress) {
+            case Ranking.IMPORTANCE_NONE:
+                mImportanceSummary.setText(mContext.getString(
+                        R.string.notification_importance_blocked));
+                mImportanceTitle.setText(mContext.getString(R.string.blocked_importance));
+                break;
+            case Ranking.IMPORTANCE_MIN:
+                mImportanceSummary.setText(mContext.getString(
+                        R.string.notification_importance_min));
+                mImportanceTitle.setText(mContext.getString(R.string.min_importance));
+                break;
+            case Ranking.IMPORTANCE_LOW:
+                mImportanceSummary.setText(mContext.getString(
+                        R.string.notification_importance_low));
+                mImportanceTitle.setText(mContext.getString(R.string.low_importance));
+                break;
+            case Ranking.IMPORTANCE_DEFAULT:
+                mImportanceSummary.setText(mContext.getString(
+                        R.string.notification_importance_default));
+                mImportanceTitle.setText(mContext.getString(R.string.default_importance));
+                break;
+            case Ranking.IMPORTANCE_HIGH:
+                mImportanceSummary.setText(mContext.getString(
+                        R.string.notification_importance_high));
+                mImportanceTitle.setText(mContext.getString(R.string.high_importance));
+                break;
+            case Ranking.IMPORTANCE_MAX:
+                mImportanceSummary.setText(mContext.getString(
+                        R.string.notification_importance_max));
+                mImportanceTitle.setText(mContext.getString(R.string.max_importance));
+                break;
+        }
+    }
+
+    private ColorStateList loadColorStateList(int colorResId) {
+        return ColorStateList.valueOf(mContext.getColor(colorResId));
     }
 
     public void closeControls(int x, int y, boolean notify) {
@@ -353,7 +404,6 @@
 
     @Override
     public boolean hasOverlappingRendering() {
-
         // Prevents this view from creating a layer when alpha is animating.
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 3812429..92f3585 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -21,8 +21,12 @@
 import android.app.IWallpaperManagerCallback;
 import android.app.WallpaperManager;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.DrawableWrapper;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.ParcelFileDescriptor;
@@ -151,4 +155,57 @@
         getBitmap();
         mBar.updateMediaMetaData(true /* metaDataChanged */, true /* allowEnterAnimation */);
     }
+
+    /**
+     * Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
+     */
+    public static class WallpaperDrawable extends DrawableWrapper {
+
+        private Bitmap mBackground;
+        private Rect mTmpRect = new Rect();
+
+        public WallpaperDrawable(Resources r, Bitmap b) {
+            super(new BitmapDrawable(r, b));
+            mBackground = b;
+        }
+
+        @Override
+        public int getIntrinsicWidth() {
+            return -1;
+        }
+
+        @Override
+        public int getIntrinsicHeight() {
+            return -1;
+        }
+
+        @Override
+        protected void onBoundsChange(Rect bounds) {
+            int vwidth = getBounds().width();
+            int vheight = getBounds().height();
+            int dwidth = mBackground.getWidth();
+            int dheight = mBackground.getHeight();
+            float scale;
+            float dx = 0, dy = 0;
+
+            if (dwidth * vheight > vwidth * dheight) {
+                scale = (float) vheight / (float) dheight;
+            } else {
+                scale = (float) vwidth / (float) dwidth;
+            }
+
+            if (scale <= 1f) {
+                scale = 1f;
+            }
+            dy = (vheight - dheight * scale) * 0.5f;
+
+            mTmpRect.set(
+                    bounds.left,
+                    bounds.top + Math.round(dy),
+                    bounds.left + Math.round(dwidth * scale),
+                    bounds.top + Math.round(dheight * scale + dy));
+
+            super.onBoundsChange(mTmpRect);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
index 41eed56..951b096 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
@@ -15,6 +15,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.app.ActivityManager;
+import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -57,10 +58,18 @@
         }
     }
 
-    public void setWorkModeEnabled(boolean enabled) {
+    public void setWorkModeEnabled(boolean enableWorkMode) {
         synchronized (mProfiles) {
             for (UserInfo ui : mProfiles) {
-                mUserManager.setQuietModeEnabled(ui.id, !enabled);
+                if (enableWorkMode) {
+                    if (!mUserManager.trySetQuietModeDisabled(ui.id, null)) {
+                        StatusBarManager statusBarManager = (StatusBarManager) mContext
+                                .getSystemService(android.app.Service.STATUS_BAR_SERVICE);
+                        statusBarManager.collapsePanels();
+                    }
+                } else {
+                    mUserManager.setQuietModeEnabled(ui.id, true);
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index d7fa567..4b7d56b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -531,6 +531,11 @@
                 }
 
                 @Override
+                public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)
+                        throws RemoteException {
+                }
+
+                @Override
                 public void onDockSideChanged(int newDockSide) throws RemoteException {
                 }
             });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 3ca0a6d..82806fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -46,6 +46,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.InputMethodService;
@@ -1928,19 +1929,27 @@
                     + " state=" + mState);
         }
 
-        Bitmap artworkBitmap = null;
+        Drawable artworkDrawable = null;
         if (mMediaMetadata != null) {
+            Bitmap artworkBitmap = null;
             artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
             if (artworkBitmap == null) {
                 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
                 // might still be null
             }
+            if (artworkBitmap != null) {
+                artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap);
+            }
         }
-        if (ENABLE_LOCKSCREEN_WALLPAPER && artworkBitmap == null) {
-            artworkBitmap = mLockscreenWallpaper.getBitmap();
+        if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
+            Bitmap lockWallpaper = mLockscreenWallpaper.getBitmap();
+            if (lockWallpaper != null) {
+                artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
+                        mBackdropBack.getResources(), lockWallpaper);
+            }
         }
 
-        final boolean hasArtwork = artworkBitmap != null;
+        final boolean hasArtwork = artworkDrawable != null;
 
         if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && mState != StatusBarState.SHADE
                 && mFingerprintUnlockController.getMode()
@@ -1985,7 +1994,7 @@
                     mBackdropBack.setBackgroundColor(0xFFFFFFFF);
                     mBackdropBack.setImageDrawable(new ColorDrawable(c));
                 } else {
-                    mBackdropBack.setImageBitmap(artworkBitmap);
+                    mBackdropBack.setImageDrawable(artworkDrawable);
                 }
                 if (mScrimSrcModeEnabled) {
                     mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode);
@@ -4314,7 +4323,7 @@
         try {
             IActivityManager activityManager = ActivityManagerNative.getDefault();
             if (activityManager.isInLockTaskMode()) {
-                activityManager.stopLockTaskModeOnCurrent();
+                activityManager.stopSystemLockTaskMode();
 
                 // When exiting refresh disabled flags.
                 mNavigationBarView.setDisabledFlags(mDisabled1, true);
@@ -4337,17 +4346,17 @@
     }
 
     @Override
-    public void showScreenPinningRequest() {
+    public void showScreenPinningRequest(int taskId) {
         if (mKeyguardMonitor.isShowing()) {
             // Don't allow apps to trigger this from keyguard.
             return;
         }
         // Show screen pinning request, since this comes from an app, show 'no thanks', button.
-        showScreenPinningRequest(true);
+        showScreenPinningRequest(taskId, true);
     }
 
-    public void showScreenPinningRequest(boolean allowCancel) {
-        mScreenPinningRequest.showPrompt(allowCancel);
+    public void showScreenPinningRequest(int taskId, boolean allowCancel) {
+        mScreenPinningRequest.showPrompt(taskId, allowCancel);
     }
 
     public boolean hasActiveNotifications() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index acef81b..f9202c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -165,7 +165,7 @@
     }
 
     @Override
-    public void showScreenPinningRequest() {
+    public void showScreenPinningRequest(int taskId) {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java b/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java
new file mode 100644
index 0000000..205db32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.tuner;
+
+import android.os.Bundle;
+import android.support.v14.preference.PreferenceFragment;
+import com.android.systemui.R;
+
+public class OtherPrefs extends PreferenceFragment {
+    @Override
+    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+        addPreferencesFromResource(R.xml.other_settings);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java
new file mode 100644
index 0000000..14fccf2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java
@@ -0,0 +1,93 @@
+/**
+ * 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.
+ */
+package com.android.systemui.tuner;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Switch;
+import android.widget.TextView;
+
+public class PowerNotificationControlsFragment extends Fragment {
+
+    private static final String KEY_SHOW_PNC = "show_importance_slider";
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.power_notification_controls_settings, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        final View switchBar = view.findViewById(R.id.switch_bar);
+        final Switch switchWidget = (Switch) switchBar.findViewById(android.R.id.switch_widget);
+        final TextView switchText = (TextView) switchBar.findViewById(R.id.switch_text);
+        switchWidget.setChecked(isEnabled());
+        switchText.setText(isEnabled()
+                ? getString(R.string.switch_bar_on)
+                : getString(R.string.switch_bar_off));
+
+        switchWidget.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                boolean newState = !isEnabled();
+                MetricsLogger.action(getContext(),
+                        MetricsEvent.ACTION_TUNER_POWER_NOTIFICATION_CONTROLS, newState);
+                Settings.Secure.putInt(getContext().getContentResolver(),
+                        KEY_SHOW_PNC, newState ? 1 : 0);
+                switchWidget.setChecked(newState);
+                switchText.setText(newState
+                        ? getString(R.string.switch_bar_on)
+                        : getString(R.string.switch_bar_off));
+            }
+        });
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        MetricsLogger.visibility(
+                getContext(), MetricsEvent.TUNER_POWER_NOTIFICATION_CONTROLS, true);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        MetricsLogger.visibility(
+                getContext(), MetricsEvent.TUNER_POWER_NOTIFICATION_CONTROLS, false);
+    }
+
+    private boolean isEnabled() {
+        int setting = Settings.Secure.getInt(getContext().getContentResolver(), KEY_SHOW_PNC, 0);
+        return setting == 1;
+    }
+
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index afea7f3..64b4842 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2152,6 +2152,12 @@
     // User tried to dock an unresizable app.
     ACTION_WINDOW_DOCK_UNRESIZABLE = 391;
 
+    // System UI Tuner > Other > Power notification controls
+    TUNER_POWER_NOTIFICATION_CONTROLS = 392;
+
+    // System UI Tuner > Other > Power notification controls > Toggle on/off
+    ACTION_TUNER_POWER_NOTIFICATION_CONTROLS = 393;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 99c5dd6..a248aa3 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -4,11 +4,14 @@
 
 LOCAL_MODULE := services.core
 
+LOCAL_AIDL_INCLUDES := system/netd/server/binder
+
 LOCAL_SRC_FILES += \
     $(call all-java-files-under,java) \
     java/com/android/server/EventLogTags.logtags \
     java/com/android/server/am/EventLogTags.logtags \
-    ../../../../system/netd/server/binder/android/net/INetd.aidl
+    ../../../../system/netd/server/binder/android/net/INetd.aidl \
+    ../../../../system/netd/server/binder/android/net/metrics/IDnsEventListener.aidl \
 
 LOCAL_JAVA_LIBRARIES := services.net telephony-common
 LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index f2b4e52..c1bacfa 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -261,7 +261,9 @@
                     showEncryptionNotification(userHandle);
                 } else {
                     UserInfo parent = mUserManager.getProfileParent(user.id);
-                    if (parent != null && mUserManager.isUserUnlocked(parent.getUserHandle())) {
+                    if (parent != null &&
+                            mUserManager.isUserUnlocked(parent.getUserHandle()) &&
+                            !mUserManager.isQuietModeEnabled(userHandle)) {
                         // Only show notifications for managed profiles once their parent
                         // user is unlocked.
                         showEncryptionNotificationForProfile(userHandle);
@@ -348,7 +350,8 @@
             UserInfo profile = profiles.get(i);
             if (profile.isManagedProfile()) {
                 UserHandle userHandle = profile.getUserHandle();
-                if (!mUserManager.isUserUnlocked(userHandle)) {
+                if (!mUserManager.isUserUnlocked(userHandle) &&
+                        !mUserManager.isQuietModeEnabled(userHandle)) {
                     showEncryptionNotificationForProfile(userHandle);
                 }
             }
@@ -702,6 +705,12 @@
             }
         };
 
+        // Check if the user is currently in quiet mode and start it otherwise
+        if (mUserManager.isQuietModeEnabled(new UserHandle(userId))
+                && mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+            mUserManager.setQuietModeEnabled(userId, false);
+        }
+
         try {
             ActivityManagerNative.getDefault().unlockUser(userId, token, secret, listener);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9703d13..c971262 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9873,7 +9873,6 @@
         boolean isSystemInitiated = callingUid == Process.SYSTEM_UID;
         long ident = Binder.clearCallingIdentity();
         try {
-            final ActivityStack stack = mStackSupervisor.getFocusedStack();
             if (!isSystemInitiated) {
                 task.mLockTaskUid = callingUid;
                 if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
@@ -9882,11 +9881,12 @@
                     StatusBarManagerInternal statusBarManager =
                             LocalServices.getService(StatusBarManagerInternal.class);
                     if (statusBarManager != null) {
-                        statusBarManager.showScreenPinningRequest();
+                        statusBarManager.showScreenPinningRequest(task.taskId);
                     }
                     return;
                 }
 
+                final ActivityStack stack = mStackSupervisor.getFocusedStack();
                 if (stack == null || task != stack.topTask()) {
                     throw new IllegalArgumentException("Invalid task, not in foreground");
                 }
@@ -9927,15 +9927,13 @@
     }
 
     @Override
-    public void startLockTaskModeOnCurrent() throws RemoteException {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startLockTaskModeOnCurrent");
+    public void startSystemLockTaskMode(int taskId) throws RemoteException {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
+        // This makes inner call to look as if it was initiated by system.
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                ActivityRecord r = mStackSupervisor.topRunningActivityLocked();
-                if (r != null) {
-                    startLockTaskModeLocked(r.task);
-                }
+                startLockTaskMode(taskId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -9979,8 +9977,9 @@
     }
 
     @Override
-    public void stopLockTaskModeOnCurrent() throws RemoteException {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopLockTaskModeOnCurrent");
+    public void stopSystemLockTaskMode() throws RemoteException {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
+        // This makes inner call to look as if it was initiated by system.
         long ident = Binder.clearCallingIdentity();
         try {
             stopLockTaskMode();
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 43e1bdf..0331470 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -98,13 +98,16 @@
      *                   launch
      * @param componentName the component name of the activity being launched
      * @param processRunning whether the process that will contains the activity is already running
+     * @param processSwitch whether the process that will contain the activity didn't have any
+     *                      activity that was stopped, i.e. the started activity is "switching"
+     *                      processes
      */
     void notifyActivityLaunched(int resultCode, @Nullable String componentName,
-            boolean processRunning) {
+            boolean processRunning, boolean processSwitch) {
 
-        if (resultCode < 0 || componentName == null) {
+        if (resultCode < 0 || componentName == null || !processSwitch) {
 
-            // Failed to launch, don't track anything.
+            // Failed to launch or it was not a process switch, so we don't care about the timing.
             reset();
             return;
         }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 6fba8c8..a96f23a 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -137,6 +137,7 @@
 
     // Share state variable among methods when starting an activity.
     private ActivityRecord mStartActivity;
+    private ActivityRecord mReusedActivity;
     private Intent mIntent;
     private int mCallingUid;
     private ActivityOptions mOptions;
@@ -785,15 +786,40 @@
 
             final String componentName = outRecord[0] != null ? outRecord[0].shortComponentName
                     : null;
-            final boolean processRunning = outRecord[0] != null &&
-                    mService.mProcessNames.get(outRecord[0].processName,
-                            outRecord[0].appInfo.uid) != null;
+            final ActivityRecord launchedActivity = mReusedActivity != null
+                    ? mReusedActivity : outRecord[0];
+            final ProcessRecord processRecord = launchedActivity != null
+                    ? mService.mProcessNames.get(launchedActivity.processName,
+                            launchedActivity.appInfo.uid)
+                    : null;
+            final boolean processRunning = processRecord != null;
+
+            // We consider this a "process switch" if the process of the activity that gets launched
+            // didn't have an activity that was in started state. In this case, we assume that lot
+            // of caches might be purged so the time until it produces the first frame is very
+            // interesting.
+            final boolean processSwitch = processRecord == null
+                    || !hasStartedActivity(processRecord, launchedActivity);
             mSupervisor.mActivityMetricsLogger.notifyActivityLaunched(res, componentName,
-                    processRunning);
+                    processRunning, processSwitch);
             return res;
         }
     }
 
+    final boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) {
+        final ArrayList<ActivityRecord> activities = record.activities;
+        for (int i = activities.size() - 1; i >= 0; i--) {
+            final ActivityRecord activity = activities.get(i);
+            if (launchedActivity == activity) {
+                continue;
+            }
+            if (!activity.stopped) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo,
             Bundle bOptions, int userId) {
@@ -883,16 +909,16 @@
 
         mIntent.setFlags(mLaunchFlags);
 
-        ActivityRecord intentActivity = getReusableIntentActivity();
+        mReusedActivity = getReusableIntentActivity();
 
         final int preferredLaunchStackId =
                 (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
 
-        if (intentActivity != null) {
+        if (mReusedActivity != null) {
             // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
             // still needs to be a lock task mode violation since the task gets cleared out and
             // the device would otherwise leave the locked task.
-            if (mSupervisor.isLockTaskModeViolation(intentActivity.task,
+            if (mSupervisor.isLockTaskModeViolation(mReusedActivity.task,
                     (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                             == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
                 mSupervisor.showLockTaskToast();
@@ -901,12 +927,12 @@
             }
 
             if (mStartActivity.task == null) {
-                mStartActivity.task = intentActivity.task;
+                mStartActivity.task = mReusedActivity.task;
             }
-            if (intentActivity.task.intent == null) {
+            if (mReusedActivity.task.intent == null) {
                 // This task was started because of movement of the activity based on affinity...
                 // Now that we are actually launching it, we can assign the base intent.
-                intentActivity.task.setIntent(mStartActivity);
+                mReusedActivity.task.setIntent(mStartActivity);
             }
 
             // This code path leads to delivering a new intent, we want to make sure we schedule it
@@ -917,7 +943,7 @@
                 // In this situation we want to remove all activities from the task up to the one
                 // being started. In most cases this means we are resetting the task to its initial
                 // state.
-                final ActivityRecord top = intentActivity.task.performClearTaskForReuseLocked(
+                final ActivityRecord top = mReusedActivity.task.performClearTaskForReuseLocked(
                         mStartActivity, mLaunchFlags);
                 if (top != null) {
                     if (top.frontOfTask) {
@@ -931,7 +957,7 @@
                 }
             }
 
-            intentActivity = setTargetStackAndMoveToFrontIfNeeded(intentActivity);
+            mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);
 
             if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                 // We don't need to start a new activity, and the client said not to do anything
@@ -941,7 +967,7 @@
                 return START_RETURN_INTENT_TO_CALLER;
             }
 
-            setTaskFromIntentActivity(intentActivity);
+            setTaskFromIntentActivity(mReusedActivity);
 
             if (!mAddingToTask && mReuseTask == null) {
                 // We didn't do anything...  but it was needed (a.k.a., client don't use that
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index e69c662..08d2399 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1501,22 +1501,22 @@
                 Math.min((int)(mTmpStableBounds.width() / density), serviceConfig.screenWidthDp);
         config.screenHeightDp =
                 Math.min((int)(mTmpStableBounds.height() / density), serviceConfig.screenHeightDp);
-        config.smallestScreenWidthDp = Math.min(config.screenWidthDp, config.screenHeightDp);
 
         // TODO: Orientation?
         config.orientation = (config.screenWidthDp <= config.screenHeightDp)
                 ? Configuration.ORIENTATION_PORTRAIT
                 : Configuration.ORIENTATION_LANDSCAPE;
 
-        // For calculating screen layout, we need to use the non-decor inset screen area for the
-        // calculation for compatibility reasons, i.e. screen area without system bars that could
-        // never go away in Honeycomb.
+        // For calculating screen layout and smallest screen width, we need to use the non-decor
+        // inset screen area for the calculation for compatibility reasons, i.e. screen area without
+        // system bars that could never go away in Honeycomb.
         final int compatScreenWidthDp = (int)(mTmpNonDecorBounds.width() / density);
         final int compatScreenHeightDp = (int)(mTmpNonDecorBounds.height() / density);
         final int sl = Configuration.resetScreenLayout(serviceConfig.screenLayout);
         final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
-        final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
-        config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
+        config.smallestScreenWidthDp = Math.min(compatScreenHeightDp, compatScreenWidthDp);
+        config.screenLayout = Configuration.reduceScreenLayout(sl, longSize,
+                config.smallestScreenWidthDp);
         return config;
     }
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 5ebb9a7..4292fcf 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -895,7 +895,10 @@
         synchronized (mService) {
             // Bail if already running unlocked, or if not running at all
             final UserState uss = mStartedUsers.get(userId);
-            if (uss == null) return false;
+            if (uss == null) {
+                progress.finish();
+                return false;
+            }
             switch (uss.state) {
                 case STATE_RUNNING_UNLOCKING:
                 case STATE_RUNNING_UNLOCKED:
diff --git a/services/core/java/com/android/server/connectivity/DnsEventListenerService.java b/services/core/java/com/android/server/connectivity/DnsEventListenerService.java
new file mode 100644
index 0000000..d3f8af0
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/DnsEventListenerService.java
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+package com.android.server.connectivity;
+
+import android.content.Context;
+import android.net.metrics.DnsEvent;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkRequest;
+import android.net.metrics.IDnsEventListener;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+
+/**
+ * Implementation of the IDnsEventListener interface.
+ */
+public class DnsEventListenerService extends IDnsEventListener.Stub {
+
+    public static final String SERVICE_NAME = "dns_listener";
+
+    private static final String TAG = DnsEventListenerService.class.getSimpleName();
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
+
+    private static final int MAX_LOOKUPS_PER_DNS_EVENT = 100;
+
+    // Stores the results of a number of consecutive DNS lookups on the same network.
+    // This class is not thread-safe and it is the responsibility of the service to call its methods
+    // on one thread at a time.
+    private static class DnsEventBatch {
+        private final int mNetId;
+
+        private final byte[] mEventTypes = new byte[MAX_LOOKUPS_PER_DNS_EVENT];
+        private final byte[] mReturnCodes = new byte[MAX_LOOKUPS_PER_DNS_EVENT];
+        private final int[] mLatenciesMs = new int[MAX_LOOKUPS_PER_DNS_EVENT];
+        private int mEventCount;
+
+        public DnsEventBatch(int netId) {
+            mNetId = netId;
+        }
+
+        public void addResult(byte eventType, byte returnCode, int latencyMs) {
+            mEventTypes[mEventCount] = eventType;
+            mReturnCodes[mEventCount] = returnCode;
+            mLatenciesMs[mEventCount] = latencyMs;
+            mEventCount++;
+            if (mEventCount == MAX_LOOKUPS_PER_DNS_EVENT) {
+                logAndClear();
+            }
+        }
+
+        public void logAndClear() {
+            // Did we lose a race with addResult?
+            if (mEventCount == 0) {
+                return;
+            }
+
+            byte[] returnCodes = Arrays.copyOf(mReturnCodes, mEventCount);
+            int[] latenciesMs = Arrays.copyOf(mLatenciesMs, mEventCount);
+            DnsEvent.logEvent(mNetId, mEventTypes, mReturnCodes, mLatenciesMs);
+            maybeLog(String.format("Logging %d results for netId %d", mEventCount, mNetId));
+            mEventCount = 0;
+        }
+
+        // For debugging and unit tests only.
+        public String toString() {
+            return String.format("%s %d %d", getClass().getSimpleName(), mNetId, mEventCount);
+        }
+    }
+
+    // Only sorted for ease of debugging. Because we only typically have a handful of networks up
+    // at any given time, performance is not a concern.
+    @GuardedBy("this")
+    private SortedMap<Integer, DnsEventBatch> mEventBatches = new TreeMap<>();
+
+    // We register a NetworkCallback to ensure that when a network disconnects, we flush the DNS
+    // queries we've logged on that network. Because we do not do this periodically, we might lose
+    // up to MAX_LOOKUPS_PER_DNS_EVENT lookup stats on each network when the system is shutting
+    // down. We believe this to be sufficient for now.
+    private final ConnectivityManager mCm;
+    private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+        @Override
+        public void onLost(Network network) {
+            synchronized (DnsEventListenerService.this) {
+                DnsEventBatch batch = mEventBatches.remove(network.netId);
+                if (batch != null) {
+                    batch.logAndClear();
+                }
+            }
+        }
+    };
+
+    public DnsEventListenerService(Context context) {
+        // We are started when boot is complete, so ConnectivityService should already be running.
+        final NetworkRequest request = new NetworkRequest.Builder()
+            .clearCapabilities()
+            .build();
+        mCm = context.getSystemService(ConnectivityManager.class);
+        mCm.registerNetworkCallback(request, mNetworkCallback);
+    }
+
+    @Override
+    // Called concurrently by multiple binder threads.
+    public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs) {
+        maybeVerboseLog(String.format("onDnsEvent(%d, %d, %d, %d)",
+                netId, eventType, returnCode, latencyMs));
+
+        DnsEventBatch batch = mEventBatches.get(netId);
+        if (batch == null) {
+            batch = new DnsEventBatch(netId);
+            mEventBatches.put(netId, batch);
+        }
+        batch.addResult((byte) eventType, (byte) returnCode, latencyMs);
+    }
+
+    public synchronized void dump(PrintWriter writer) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+        pw.println(TAG + ":");
+        pw.increaseIndent();
+        for (DnsEventBatch batch : mEventBatches.values()) {
+            pw.println(batch.toString());
+        }
+        pw.decreaseIndent();
+    }
+
+    private static void maybeLog(String s) {
+        if (DBG) Log.d(TAG, s);
+    }
+
+    private static void maybeVerboseLog(String s) {
+        if (VDBG) Log.d(TAG, s);
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
index f91db78..0c259ae 100644
--- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
+++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
@@ -55,6 +55,8 @@
             if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
             publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE,
                     mBinder);
+            mDnsListener = new DnsEventListenerService(getContext());
+            publishBinderService(mDnsListener.SERVICE_NAME, mDnsListener);
         }
     }
 
@@ -89,6 +91,8 @@
 
     private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>();
 
+    private DnsEventListenerService mDnsListener;
+
     private void enforceConnectivityInternalPermission() {
         getContext().enforceCallingOrSelfPermission(
                 android.Manifest.permission.CONNECTIVITY_INTERNAL,
@@ -159,10 +163,12 @@
 
             synchronized (mEvents) {
                 pw.println("Number of events: " + mEvents.size());
-                pw.println("Time span: " +
-                        DateUtils.formatElapsedTime(
-                                (System.currentTimeMillis() - mEvents.peekFirst().timestamp)
-                                        / 1000));
+                if (mEvents.size() > 0) {
+                    pw.println("Time span: " +
+                            DateUtils.formatElapsedTime(
+                                    (System.currentTimeMillis() - mEvents.peekFirst().timestamp)
+                                            / 1000));
+                }
 
                 if (dumpSerializedSize) {
                     long dataSize = 0;
@@ -193,6 +199,9 @@
                     pw.println(pi.toString());
                 }
             }
+
+            pw.println();
+            mDnsListener.dump(pw);
         }
 
         public long logEvent(ConnectivityMetricsEvent event) {
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
new file mode 100644
index 0000000..d2f015f
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -0,0 +1,156 @@
+/**
+ * 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.
+ */
+
+package com.android.server.fingerprint;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+
+import android.content.Context;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the authentication state for a given client.
+ */
+public abstract class AuthenticationClient extends ClientMonitor {
+    private long mOpId;
+
+    public abstract boolean handleFailedAttempt();
+    public abstract void resetFailedAttempts();
+
+    public AuthenticationClient(Context context, long halDeviceId, IBinder token,
+            IFingerprintServiceReceiver receiver, int userId, int groupId, long opId,
+            boolean restricted, String owner) {
+        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
+        mOpId = opId;
+    }
+
+    @Override
+    public boolean onAuthenticated(int fingerId, int groupId) {
+        boolean result = false;
+        boolean authenticated = fingerId != 0;
+
+        IFingerprintServiceReceiver receiver = getReceiver();
+        if (receiver != null) {
+            try {
+                MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
+                        authenticated);
+                if (!authenticated) {
+                    receiver.onAuthenticationFailed(getHalDeviceId());
+                } else {
+                    if (DEBUG) {
+                        Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
+                                + ", id=" + fingerId + ", gp=" + groupId + ")");
+                    }
+                    Fingerprint fp = !getIsRestricted()
+                            ? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
+                            : null;
+                    receiver.onAuthenticationSucceeded(getHalDeviceId(), fp);
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to notify Authenticated:", e);
+                result = true; // client failed
+            }
+        } else {
+            result = true; // client not listening
+        }
+        if (fingerId == 0) {
+            if (receiver != null) {
+                FingerprintUtils.vibrateFingerprintError(getContext());
+            }
+            // allow system-defined limit of number of attempts before giving up
+            result |= handleFailedAttempt();
+        } else {
+            if (receiver != null) {
+                FingerprintUtils.vibrateFingerprintSuccess(getContext());
+            }
+            result |= true; // we have a valid fingerprint, done
+            resetFailedAttempts();
+        }
+        return result;
+    }
+
+    /**
+     * Start authentication
+     */
+    @Override
+    public int start() {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "start authentication: no fingeprintd!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.authenticate(mOpId, getGroupId());
+            if (result != 0) {
+                Slog.w(TAG, "startAuthentication failed, result=" + result);
+                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+                return result;
+            }
+            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startAuthentication failed", e);
+            return ERROR_ESRCH;
+        }
+        return 0; // success
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "stopAuthentication: no fingeprintd!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.cancelAuthentication();
+            if (result != 0) {
+                Slog.w(TAG, "stopAuthentication failed, result=" + result);
+                return result;
+            }
+            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
+        } catch (RemoteException e) {
+            Slog.e(TAG, "stopAuthentication failed", e);
+            return ERROR_ESRCH;
+        }
+        return 0; // success
+    }
+
+    @Override
+    public boolean onEnrollResult(int fingerId, int groupId, int rem) {
+        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+
+    @Override
+    public boolean onRemoved(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+
+    @Override
+    public boolean onEnumerationResult(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+}
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
new file mode 100644
index 0000000..90998ed
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
@@ -0,0 +1,211 @@
+/**
+ * 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.
+ */
+
+package com.android.server.fingerprint;
+
+import android.Manifest;
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Abstract base class for keeping track and dispatching events from fingerprintd to the
+ * the current client.  Subclasses are responsible for coordinating the interaction with
+ * fingerprintd for the specific action (e.g. authenticate, enroll, enumerate, etc.).
+ */
+public abstract class ClientMonitor implements IBinder.DeathRecipient {
+    protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
+    protected static final int ERROR_ESRCH = 3; // Likely fingerprintd is dead. See errno.h.
+    protected static final boolean DEBUG = FingerprintService.DEBUG;
+    private IBinder mToken;
+    private IFingerprintServiceReceiver mReceiver;
+    private int mUserId;
+    private int mGroupId;
+    private boolean mIsRestricted; // True if client does not have MANAGE_FINGERPRINT permission
+    private String mOwner;
+    private Context mContext;
+    private long mHalDeviceId;
+
+    /**
+     * @param context context of FingerprintService
+     * @param halDeviceId the HAL device ID of the associated fingerprint hardware
+     * @param token a unique token for the client
+     * @param receiver recipient of related events (e.g. authentication)
+     * @param userId userId for the fingerprint set
+     * @param groupId groupId for the fingerprint set
+     * @param restricted whether or not client has the {@link Manifest#MANAGE_FINGERPRINT}
+     * permission
+     * @param owner name of the client that owns this
+     */
+    public ClientMonitor(Context context, long halDeviceId, IBinder token,
+            IFingerprintServiceReceiver receiver, int userId, int groupId,boolean restricted,
+            String owner) {
+        mContext = context;
+        mHalDeviceId = halDeviceId;
+        mToken = token;
+        mReceiver = receiver;
+        mUserId = userId;
+        mGroupId = groupId;
+        mIsRestricted = restricted;
+        mOwner = owner;
+        try {
+            token.linkToDeath(this, 0);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+        }
+    }
+
+    /**
+     * Contacts fingerprintd to start the client.
+     * @return 0 on succes, errno from driver on failure
+     */
+    public abstract int start();
+
+    /**
+     * Contacts fingerprintd to stop the client.
+     * @param initiatedByClient whether the operation is at the request of a client
+     */
+    public abstract int stop(boolean initiatedByClient);
+
+    /**
+     * Method to explicitly poke powermanager on events
+     */
+    public abstract void notifyUserActivity();
+
+    /**
+     * Gets the fingerprint daemon from the cached state in the container class.
+     */
+    public abstract IFingerprintDaemon getFingerprintDaemon();
+
+    // Event callbacks from driver. Inappropriate calls is flagged/logged by the
+    // respective client (e.g. enrolling shouldn't get authenticate events).
+    // All of these return 'true' if the operation is completed and it's ok to move
+    // to the next client (e.g. authentication accepts or rejects a fingerprint).
+    public abstract boolean onEnrollResult(int fingerId, int groupId, int rem);
+    public abstract boolean onAuthenticated(int fingerId, int groupId);
+    public abstract boolean onRemoved(int fingerId, int groupId);
+    public abstract boolean onEnumerationResult(int fingerId, int groupId);
+
+    /**
+     * Called when we get notification from fingerprintd that an image has been acquired.
+     * Common to authenticate and enroll.
+     * @param acquiredInfo info about the current image acquisition
+     * @return true if client should be removed
+     */
+    public boolean onAcquired(int acquiredInfo) {
+        if (mReceiver == null)
+            return true; // client not connected
+        try {
+            mReceiver.onAcquired(getHalDeviceId(), acquiredInfo);
+            return false; // acquisition continues...
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to invoke sendAcquired:", e);
+            return true; // client failed
+        } finally {
+            // Good scans will keep the device awake
+            if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+                notifyUserActivity();
+            }
+        }
+    }
+
+    /**
+     * Called when we get notification from fingerprintd that an error has occurred with the
+     * current operation. Common to authenticate, enroll, enumerate and remove.
+     * @param error
+     * @return true if client should be removed
+     */
+    public boolean onError(int error) {
+        if (mReceiver != null) {
+            try {
+                mReceiver.onError(getHalDeviceId(), error);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to invoke sendError:", e);
+            }
+        }
+        return true; // errors always remove current client
+    }
+
+    public void destroy() {
+        if (mToken != null) {
+            try {
+                mToken.unlinkToDeath(this, 0);
+            } catch (NoSuchElementException e) {
+                // TODO: remove when duplicate call bug is found
+                Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
+            }
+            mToken = null;
+        }
+        mReceiver = null;
+    }
+
+    @Override
+    public void binderDied() {
+        mToken = null;
+        mReceiver = null;
+        onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mToken != null) {
+                if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
+                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public final Context getContext() {
+        return mContext;
+    }
+
+    public final long getHalDeviceId() {
+        return mHalDeviceId;
+    }
+
+    public final String getOwnerString() {
+        return mOwner;
+    }
+
+    public final IFingerprintServiceReceiver getReceiver() {
+        return mReceiver;
+    }
+
+    public final boolean getIsRestricted() {
+        return mIsRestricted;
+    }
+
+    public final int getUserId() {
+        return mUserId;
+    }
+
+    public final int getGroupId() {
+        return mGroupId;
+    }
+
+    public final IBinder getToken() {
+        return mToken;
+    }
+}
diff --git a/services/core/java/com/android/server/fingerprint/EnrollClient.java b/services/core/java/com/android/server/fingerprint/EnrollClient.java
new file mode 100644
index 0000000..ce5b890
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/EnrollClient.java
@@ -0,0 +1,136 @@
+/**
+ * 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.
+ */
+
+package com.android.server.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+
+import java.util.Arrays;
+
+/**
+ * A class to keep track of the enrollment state for a given client.
+ */
+public abstract class EnrollClient extends ClientMonitor {
+    private static final long MS_PER_SEC = 1000;
+    private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
+    private byte[] mCryptoToken;
+
+    public EnrollClient(Context context, long halDeviceId, IBinder token,
+            IFingerprintServiceReceiver receiver, int userId, int groupId, byte [] cryptoToken,
+            boolean restricted, String owner) {
+        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
+        mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
+    }
+
+    @Override
+    public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
+        if (remaining == 0) {
+            FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId,
+                    getUserId());
+        }
+        return sendEnrollResult(fingerId, groupId, remaining);
+    }
+
+    /*
+     * @return true if we're done.
+     */
+    private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
+        IFingerprintServiceReceiver receiver = getReceiver();
+        if (receiver == null)
+            return true; // client not listening
+
+        FingerprintUtils.vibrateFingerprintSuccess(getContext());
+        MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_ENROLL);
+        try {
+            receiver.onEnrollResult(getHalDeviceId(), fpId, groupId, remaining);
+            return remaining == 0;
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to notify EnrollResult:", e);
+            return true;
+        }
+    }
+
+    @Override
+    public int start() {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "enroll: no fingeprintd!");
+            return ERROR_ESRCH;
+        }
+        final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
+        try {
+            final int result = daemon.enroll(mCryptoToken, getGroupId(), timeout);
+            if (result != 0) {
+                Slog.w(TAG, "startEnroll failed, result=" + result);
+                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startEnroll failed", e);
+        }
+        return 0; // success
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "stopEnrollment: no fingeprintd!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.cancelEnrollment();
+            if (result != 0) {
+                Slog.w(TAG, "startEnrollCancel failed, result = " + result);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "stopEnrollment failed", e);
+        }
+        if (initiatedByClient) {
+            onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
+        return 0;
+    }
+
+    @Override
+    public boolean onRemoved(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onRemoved() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+    @Override
+    public boolean onEnumerationResult(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+    @Override
+    public boolean onAuthenticated(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+}
diff --git a/services/core/java/com/android/server/fingerprint/EnumerateClient.java b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
new file mode 100644
index 0000000..b2e4099
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
@@ -0,0 +1,92 @@
+/**
+ * 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.
+ */
+
+package com.android.server.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the enumeration state for a given client.
+ */
+public abstract class EnumerateClient extends ClientMonitor {
+    public EnumerateClient(Context context, long halDeviceId, IBinder token,
+            IFingerprintServiceReceiver receiver, int userId, int groupId,
+            boolean restricted, String owner) {
+        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
+    }
+
+    @Override
+    public int start() {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        // The fingerprint template ids will be removed when we get confirmation from the HAL
+        try {
+            final int result = daemon.enumerate();
+            if (result != 0) {
+                Slog.w(TAG, "start enumerate for user " + getUserId()
+                    + " failed, result=" + result);
+                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startRemove failed", e);
+        }
+        return 0;
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "stopAuthentication: no fingeprintd!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.cancelEnumeration();
+            if (result != 0) {
+                Slog.w(TAG, "stop enumeration failed, result=" + result);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "stop enumeration failed", e);
+            return ERROR_ESRCH;
+        }
+        // We don't actually stop enumerate, but inform the client that the cancel operation
+        // succeeded so we can start the next operation.
+        if (initiatedByClient) {
+            onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
+        return 0; // success
+    }
+
+    @Override
+    public boolean onEnumerationResult(int fingerId, int groupId) {
+        IFingerprintServiceReceiver receiver = getReceiver();
+        if (receiver == null)
+            return true; // client not listening
+        try {
+            receiver.onRemoved(getHalDeviceId(), fingerId, groupId);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to notify enumerated:", e);
+        }
+        return fingerId == 0; // done when id hits 0
+    }
+}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index e3f3849..2dafa3e 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -19,7 +19,6 @@
 import android.Manifest;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningAppProcessInfo;
-import android.app.trust.TrustManager;
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
@@ -48,7 +47,6 @@
 import android.util.Slog;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.server.SystemService;
 
 import org.json.JSONArray;
@@ -75,7 +73,6 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.NoSuchElementException;
 
 /**
  * A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -85,31 +82,36 @@
  * @hide
  */
 public class FingerprintService extends SystemService implements IBinder.DeathRecipient {
-    private static final String TAG = "FingerprintService";
-    private static final boolean DEBUG = true;
+    static final String TAG = "FingerprintService";
+    static final boolean DEBUG = true;
     private static final String FP_DATA_DIR = "fpdata";
     private static final String FINGERPRINTD = "android.hardware.fingerprint.IFingerprintDaemon";
     private static final int MSG_USER_SWITCHING = 10;
-    private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
     private static final String ACTION_LOCKOUT_RESET =
             "com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
 
-    private ClientMonitor mAuthClient = null;
-    private ClientMonitor mEnrollClient = null;
-    private ClientMonitor mRemoveClient = null;
     private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
             new ArrayList<>();
     private final AppOpsManager mAppOps;
 
-    private static final long MS_PER_SEC = 1000;
     private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
     private static final int MAX_FAILED_ATTEMPTS = 5;
-    private static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+    private static final long CANCEL_TIMEOUT_LIMIT = 300; // max wait for onCancel() from HAL,in ms
     private final String mKeyguardPackage;
     private int mCurrentUserId = UserHandle.USER_CURRENT;
-    private int mUserIdForRemove = UserHandle.USER_NULL;
+    private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
+    private Context mContext;
+    private long mHalDeviceId;
+    private int mFailedAttempts;
+    private IFingerprintDaemon mDaemon;
+    private final PowerManager mPowerManager;
+    private final AlarmManager mAlarmManager;
+    private final UserManager mUserManager;
+    private ClientMonitor mCurrentClient;
+    private ClientMonitor mPendingClient;
+    private long mCurrentAuthenticatorId;
 
-    Handler mHandler = new Handler() {
+    private Handler mHandler = new Handler() {
         @Override
         public void handleMessage(android.os.Message msg) {
             switch (msg.what) {
@@ -123,15 +125,6 @@
         }
     };
 
-    private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
-    private Context mContext;
-    private long mHalDeviceId;
-    private int mFailedAttempts;
-    private IFingerprintDaemon mDaemon;
-    private final PowerManager mPowerManager;
-    private final AlarmManager mAlarmManager;
-    private final UserManager mUserManager;
-
     private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -148,6 +141,26 @@
         }
     };
 
+    private final Runnable mResetClientState = new Runnable() {
+        @Override
+        public void run() {
+            // Warning: if we get here, the driver never confirmed our call to cancel the current
+            // operation (authenticate, enroll, remove, enumerate, etc), which is
+            // really bad.  The result will be a 3-second delay in starting each new client.
+            // If you see this on a device, make certain the driver notifies with
+            // {@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} in response to cancel()
+            // once it has successfully switched to the IDLE state in the fingerprint HAL.
+            // Additionally,{@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} should only be sent
+            // in response to an actual cancel() call.
+            Slog.w(TAG, "Client "
+                    + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
+                    + " failed to respond to cancel, starting client "
+                    + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
+            mCurrentClient = null;
+            startClient(mPendingClient, false);
+        }
+    };
+
     public FingerprintService(Context context) {
         super(context);
         mContext = context;
@@ -203,64 +216,49 @@
         // TODO: update fingerprint/name pairs
     }
 
-    protected void handleRemoved(long deviceId, int fingerId, int groupId) {
-        final ClientMonitor client = mRemoveClient;
-        if (fingerId != 0) {
-            removeTemplateForUser(mUserIdForRemove, fingerId);
-        } else {
-            mUserIdForRemove = UserHandle.USER_NULL;
+    protected void handleError(long deviceId, int error) {
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onError(error)) {
+            removeClient(client);
         }
-        if (client != null && client.sendRemoved(fingerId, groupId)) {
+        if (DEBUG) Slog.v(TAG, "handleError(client="
+                + 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);
+            if (mPendingClient != null) {
+                if (DEBUG) Slog.v(TAG, "start pending client " + mPendingClient.getOwnerString());
+                startClient(mPendingClient, false);
+                mPendingClient = null;
+            }
+        }
+    }
+
+    protected void handleRemoved(long deviceId, int fingerId, int groupId) {
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onRemoved(fingerId, groupId)) {
             removeClient(client);
         }
     }
 
-    protected void handleError(long deviceId, int error) {
-        if (mEnrollClient != null) {
-            final IBinder token = mEnrollClient.token;
-            if (mEnrollClient.sendError(error)) {
-                stopEnrollment(token, false);
-            }
-        } else if (mAuthClient != null) {
-            final IBinder token = mAuthClient.token;
-            if (mAuthClient.sendError(error)) {
-                stopAuthentication(token, false);
-            }
-        } else if (mRemoveClient != null) {
-            if (mRemoveClient.sendError(error)) removeClient(mRemoveClient);
-        }
-    }
-
     protected void handleAuthenticated(long deviceId, int fingerId, int groupId) {
-        if (mAuthClient != null) {
-            final IBinder token = mAuthClient.token;
-            if (mAuthClient.sendAuthenticated(fingerId, groupId)) {
-                stopAuthentication(token, false);
-                removeClient(mAuthClient);
-            }
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onAuthenticated(fingerId, groupId)) {
+            removeClient(client);
         }
     }
 
     protected void handleAcquired(long deviceId, int acquiredInfo) {
-        if (mEnrollClient != null) {
-            if (mEnrollClient.sendAcquired(acquiredInfo)) {
-                removeClient(mEnrollClient);
-            }
-        } else if (mAuthClient != null) {
-            if (mAuthClient.sendAcquired(acquiredInfo)) {
-                removeClient(mAuthClient);
-            }
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onAcquired(acquiredInfo)) {
+            removeClient(client);
         }
     }
 
     protected void handleEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
-        if (mEnrollClient != null) {
-            if (mEnrollClient.sendEnrollResult(fingerId, groupId, remaining)) {
-                if (remaining == 0) {
-                    addTemplateForUser(mEnrollClient, fingerId);
-                    removeClient(mEnrollClient);
-                }
-            }
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onEnrollResult(fingerId, groupId, remaining)) {
+            removeClient(client);
         }
     }
 
@@ -274,14 +272,16 @@
     }
 
     private void removeClient(ClientMonitor client) {
-        if (client == null) return;
-        client.destroy();
-        if (client == mAuthClient) {
-            mAuthClient = null;
-        } else if (client == mEnrollClient) {
-            mEnrollClient = null;
-        } else if (client == mRemoveClient) {
-            mRemoveClient = null;
+        if (client != null) {
+            client.destroy();
+            if (client != mCurrentClient && mCurrentClient != null) {
+                Slog.w(TAG, "Unexpected client: " + client.getOwnerString() + "expected: "
+                        + mCurrentClient != null ? mCurrentClient.getOwnerString() : "null");
+            }
+        }
+        if (mCurrentClient != null) {
+            if (DEBUG) Slog.v(TAG, "Done with client: " + client.getOwnerString());
+            mCurrentClient = null;
         }
     }
 
@@ -303,60 +303,6 @@
                 new Intent(ACTION_LOCKOUT_RESET), PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
-    private void resetFailedAttempts() {
-        if (DEBUG && inLockoutMode()) {
-            Slog.v(TAG, "Reset fingerprint lockout");
-        }
-        mFailedAttempts = 0;
-        // If we're asked to reset failed attempts externally (i.e. from Keyguard), the alarm might
-        // still be pending; remove it.
-        cancelLockoutReset();
-        notifyLockoutResetMonitors();
-    }
-
-    private boolean handleFailedAttempt(ClientMonitor clientMonitor) {
-        mFailedAttempts++;
-        if (inLockoutMode()) {
-            // Failing multiple times will continue to push out the lockout time.
-            scheduleLockoutReset();
-            if (clientMonitor != null
-                    && !clientMonitor.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
-                Slog.w(TAG, "Cannot send lockout message to client");
-            }
-            return true;
-        }
-        return false;
-    }
-
-    private void removeTemplateForUser(int userId, int fingerId) {
-        mFingerprintUtils.removeFingerprintIdForUser(mContext, fingerId, userId);
-    }
-
-    private void addTemplateForUser(ClientMonitor clientMonitor, int fingerId) {
-        mFingerprintUtils.addFingerprintForUser(mContext, fingerId, clientMonitor.userId);
-    }
-
-    void startEnrollment(IBinder token, byte[] cryptoToken, int groupId,
-            IFingerprintServiceReceiver receiver, int flags, boolean restricted) {
-        IFingerprintDaemon daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "enroll: no fingeprintd!");
-            return;
-        }
-        stopPendingOperations(true);
-        mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted, token.toString());
-        final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
-        try {
-            final int result = daemon.enroll(cryptoToken, groupId, timeout);
-            if (result != 0) {
-                Slog.w(TAG, "startEnroll failed, result=" + result);
-                handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startEnroll failed", e);
-        }
-    }
-
     public long startPreEnroll(IBinder token) {
         IFingerprintDaemon daemon = getFingerprintDaemon();
         if (daemon == null) {
@@ -385,123 +331,52 @@
         return 0;
     }
 
-    private void stopPendingOperations(boolean initiatedByClient) {
-        if (mEnrollClient != null) {
-            stopEnrollment(mEnrollClient.token, initiatedByClient);
-        }
-        if (mAuthClient != null) {
-            stopAuthentication(mAuthClient.token, initiatedByClient);
-        }
-        // mRemoveClient is allowed to continue
-    }
-
     /**
-     * Stop enrollment in progress and inform client if they initiated it.
-     *
-     * @param token token for client
-     * @param initiatedByClient if this call is the result of client action (e.g. calling cancel)
+     * Calls fingerprintd to switch states to the new task. If there's already a current task,
+     * it calls cancel() and sets mPendingClient to begin when the current task finishes
+     * ({@link FingerprintManager#FINGERPRINT_ERROR_CANCELED}).
+     * @param newClient the new client that wants to connect
+     * @param initiatedByClient true for authenticate, remove and enroll
      */
-    void stopEnrollment(IBinder token, boolean initiatedByClient) {
-        IFingerprintDaemon daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "stopEnrollment: no fingeprintd!");
-            return;
-        }
-        final ClientMonitor client = mEnrollClient;
-        if (client == null || client.token != token) return;
-        if (initiatedByClient) {
-            try {
-                int result = daemon.cancelEnrollment();
-                if (result != 0) {
-                    Slog.w(TAG, "startEnrollCancel failed, result = " + result);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "stopEnrollment failed", e);
-            }
-            client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
-        }
-        removeClient(mEnrollClient);
-    }
-
-    void startAuthentication(IBinder token, long opId, int realUserId, int groupId,
-            IFingerprintServiceReceiver receiver, int flags, boolean restricted,
-            String opPackageName) {
-        IFingerprintDaemon daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "startAuthentication: no fingeprintd!");
-            return;
-        }
-        stopPendingOperations(true);
-        updateActiveGroup(groupId, opPackageName);
-        mAuthClient = new ClientMonitor(token, receiver, groupId, restricted, opPackageName);
-        if (inLockoutMode()) {
-            Slog.v(TAG, "In lockout mode; disallowing authentication");
-            if (!mAuthClient.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
-                Slog.w(TAG, "Cannot send timeout message to client");
-            }
-            mAuthClient = null;
-            return;
-        }
-        try {
-            final int result = daemon.authenticate(opId, groupId);
-            if (result != 0) {
-                Slog.w(TAG, "startAuthentication failed, result=" + result);
-                handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startAuthentication failed", e);
+    private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
+        ClientMonitor currentClient = mCurrentClient;
+        if (currentClient != null) {
+            if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
+            currentClient.stop(initiatedByClient);
+            mPendingClient = newClient;
+            mHandler.removeCallbacks(mResetClientState);
+            mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
+        } else if (newClient != null) {
+            mCurrentClient = newClient;
+            if (DEBUG) Slog.v(TAG, "starting client "
+                    + newClient.getClass().getSuperclass().getSimpleName()
+                    + "(" + newClient.getOwnerString() + ")"
+                    + ", initiatedByClient = " + initiatedByClient + ")");
+            newClient.start();
         }
     }
 
-    /**
-     * Stop authentication in progress and inform client if they initiated it.
-     *
-     * @param token token for client
-     * @param initiatedByClient if this call is the result of client action (e.g. calling cancel)
-     */
-    void stopAuthentication(IBinder token, boolean initiatedByClient) {
-        IFingerprintDaemon daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "stopAuthentication: no fingeprintd!");
-            return;
-        }
-        final ClientMonitor client = mAuthClient;
-        if (client == null || client.token != token) return;
-        if (initiatedByClient) {
-            try {
-                int result = daemon.cancelAuthentication();
-                if (result != 0) {
-                    Slog.w(TAG, "stopAuthentication failed, result=" + result);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "stopAuthentication failed", e);
-            }
-            client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
-        }
-        removeClient(mAuthClient);
-    }
-
-    void startRemove(IBinder token, int fingerId, int userId,
+    void startRemove(IBinder token, int fingerId, int userId, int groupId,
             IFingerprintServiceReceiver receiver, boolean restricted) {
         IFingerprintDaemon daemon = getFingerprintDaemon();
         if (daemon == null) {
             Slog.w(TAG, "startRemove: no fingeprintd!");
             return;
         }
-
-        stopPendingOperations(true);
-        mRemoveClient = new ClientMonitor(token, receiver, userId, restricted, token.toString());
-        mUserIdForRemove = mCurrentUserId;
-        // The fingerprint template ids will be removed when we get confirmation from the HAL
-        try {
-            final int result = daemon.remove(fingerId, userId);
-            if (result != 0) {
-                Slog.w(TAG, "startRemove with id = " + fingerId + " failed, result=" + result);
-                handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+        RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
+                receiver, userId, groupId, fingerId, restricted, token.toString()) {
+            @Override
+            public void notifyUserActivity() {
+                FingerprintService.this.userActivity();
             }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startRemove failed", e);
-        }
+
+            @Override
+            public IFingerprintDaemon getFingerprintDaemon() {
+                FingerprintService.this.getFingerprintDaemon();
+                return null;
+            }
+        };
+        startClient(client, true);
     }
 
     public List<Fingerprint> getEnrolledFingerprints(int userId) {
@@ -572,10 +447,9 @@
      * @param foregroundOnly only allow this call while app is in the foreground
      * @return true if caller can use fingerprint API
      */
-    private boolean canUseFingerprint(String opPackageName, boolean foregroundOnly) {
+    private boolean canUseFingerprint(String opPackageName, boolean foregroundOnly, int uid,
+            int pid) {
         checkPermission(USE_FINGERPRINT);
-        final int uid = Binder.getCallingUid();
-        final int pid = Binder.getCallingPid();
         if (isKeyguard(opPackageName)) {
             return true; // Keyguard is always allowed
         }
@@ -620,165 +494,83 @@
         }
     }
 
-    private class ClientMonitor implements IBinder.DeathRecipient {
-        IBinder token;
-        IFingerprintServiceReceiver receiver;
-        int userId;
-        boolean restricted; // True if client does not have MANAGE_FINGERPRINT permission
-        String owner;
+    private void startAuthentication(IBinder token, long opId, int realUserId, int groupId,
+                IFingerprintServiceReceiver receiver, int flags, boolean restricted,
+                String opPackageName) {
+        updateActiveGroup(groupId, opPackageName);
 
-        public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId,
-                boolean restricted, String owner) {
-            this.token = token;
-            this.receiver = receiver;
-            this.userId = userId;
-            this.restricted = restricted;
-            this.owner = owner; // name of the client that owns this - for debugging
-            try {
-                token.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
-            }
-        }
+        if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
 
-        public void destroy() {
-            if (token != null) {
-                try {
-                    token.unlinkToDeath(this, 0);
-                } catch (NoSuchElementException e) {
-                    // TODO: remove when duplicate call bug is found
-                    Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
+        AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
+                receiver, realUserId, groupId, opId, restricted, opPackageName) {
+            @Override
+            public boolean handleFailedAttempt() {
+                mFailedAttempts++;
+                if (inLockoutMode()) {
+                    // Failing multiple times will continue to push out the lockout time.
+                    scheduleLockoutReset();
+                    return true;
                 }
-                token = null;
+                return false;
             }
-            receiver = null;
-        }
 
-        @Override
-        public void binderDied() {
-            token = null;
-            removeClient(this);
-            receiver = null;
-        }
+            @Override
+            public void resetFailedAttempts() {
+                FingerprintService.this.resetFailedAttempts();
+            }
 
-        @Override
-        protected void finalize() throws Throwable {
-            try {
-                if (token != null) {
-                    if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
-                    removeClient(this);
-                }
-            } finally {
-                super.finalize();
+            @Override
+            public void notifyUserActivity() {
+                FingerprintService.this.userActivity();
             }
-        }
 
-        /*
-         * @return true if we're done.
-         */
-        private boolean sendRemoved(int fingerId, int groupId) {
-            if (receiver == null) return true; // client not listening
-            try {
-                receiver.onRemoved(mHalDeviceId, fingerId, groupId);
-                return fingerId == 0;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to notify Removed:", e);
+            @Override
+            public IFingerprintDaemon getFingerprintDaemon() {
+                return FingerprintService.this.getFingerprintDaemon();
             }
-            return false;
-        }
+        };
 
-        /*
-         * @return true if we're done.
-         */
-        private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
-            if (receiver == null) return true; // client not listening
-            FingerprintUtils.vibrateFingerprintSuccess(getContext());
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_FINGERPRINT_ENROLL);
-            try {
-                receiver.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
-                return remaining == 0;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to notify EnrollResult:", e);
-                return true;
+        if (inLockoutMode()) {
+            Slog.v(TAG, "In lockout mode; disallowing authentication");
+            // Don't bother starting the client. Just send the error message.
+            if (!client.onError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
+                Slog.w(TAG, "Cannot send timeout message to client");
             }
+            return;
         }
+        startClient(client, true /* initiatedByClient */);
+    }
 
-        /*
-         * @return true if we're done.
-         */
-        private boolean sendAuthenticated(int fpId, int groupId) {
-            boolean result = false;
-            boolean authenticated = fpId != 0;
-            if (receiver != null) {
-                try {
-                    MetricsLogger.action(mContext, MetricsEvent.ACTION_FINGERPRINT_AUTH,
-                            authenticated);
-                    if (!authenticated) {
-                        receiver.onAuthenticationFailed(mHalDeviceId);
-                    } else {
-                        if (DEBUG) {
-                            Slog.v(TAG, "onAuthenticated(owner=" + mAuthClient.owner
-                                    + ", id=" + fpId + ", gp=" + groupId + ")");
-                        }
-                        Fingerprint fp = !restricted ?
-                                new Fingerprint("" /* TODO */, groupId, fpId, mHalDeviceId) : null;
-                        receiver.onAuthenticationSucceeded(mHalDeviceId, fp);
-                    }
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to notify Authenticated:", e);
-                    result = true; // client failed
-                }
-            } else {
-                result = true; // client not listening
-	    }
-	    if (fpId == 0) {
-                if (receiver != null) {
-                    FingerprintUtils.vibrateFingerprintError(getContext());
-                }
-                result |= handleFailedAttempt(this);
-            } else {
-                if (receiver != null) {
-                    FingerprintUtils.vibrateFingerprintSuccess(getContext());
-                }
-                result |= true; // we have a valid fingerprint
-                resetFailedAttempts();
-            }
-            return result;
-        }
+    private void startEnrollment(IBinder token, byte [] cryptoToken, int userId, int groupId,
+            IFingerprintServiceReceiver receiver, int flags, boolean restricted,
+            String opPackageName) {
+        updateActiveGroup(groupId, opPackageName);
 
-        /*
-         * @return true if we're done.
-         */
-        private boolean sendAcquired(int acquiredInfo) {
-            if (receiver == null) return true; // client not listening
-            try {
-                receiver.onAcquired(mHalDeviceId, acquiredInfo);
-                return false; // acquisition continues...
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to invoke sendAcquired:", e);
-                return true; // client failed
-            }
-            finally {
-                // Good scans will keep the device awake
-                if (acquiredInfo == FINGERPRINT_ACQUIRED_GOOD) {
-                    userActivity();
-                }
-            }
-        }
+        EnrollClient client = new EnrollClient(getContext(), mHalDeviceId, token, receiver,
+                userId, groupId, cryptoToken, restricted, opPackageName) {
 
-        /*
-         * @return true if we're done.
-         */
-        private boolean sendError(int error) {
-            if (receiver != null) {
-                try {
-                    receiver.onError(mHalDeviceId, error);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to invoke sendError:", e);
-                }
+            @Override
+            public IFingerprintDaemon getFingerprintDaemon() {
+                return FingerprintService.this.getFingerprintDaemon();
             }
-            return true; // errors always terminate progress
+
+            @Override
+            public void notifyUserActivity() {
+                FingerprintService.this.userActivity();
+            }
+        };
+        startClient(client, true /* initiatedByClient */);
+    }
+
+    protected void resetFailedAttempts() {
+        if (DEBUG && inLockoutMode()) {
+            Slog.v(TAG, "Reset fingerprint lockout");
         }
+        mFailedAttempts = 0;
+        // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+        // the alarm might still be pending; remove it.
+        cancelLockoutReset();
+        notifyLockoutResetMonitors();
     }
 
     private class FingerprintServiceLockoutResetMonitor {
@@ -876,8 +668,6 @@
     };
 
     private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
-        private static final String KEYGUARD_PACKAGE = "com.android.systemui";
-
         @Override // Binder call
         public long preEnroll(IBinder token) {
             checkPermission(MANAGE_FINGERPRINT);
@@ -892,7 +682,8 @@
 
         @Override // Binder call
         public void enroll(final IBinder token, final byte[] cryptoToken, final int groupId,
-                final IFingerprintServiceReceiver receiver, final int flags) {
+                final IFingerprintServiceReceiver receiver, final int flags,
+                final String opPackageName) {
             checkPermission(MANAGE_FINGERPRINT);
             final int limit =  mContext.getResources().getInteger(
                     com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
@@ -903,7 +694,6 @@
                 Slog.w(TAG, "Too many fingerprints registered");
                 return;
             }
-            final byte [] cryptoClone = Arrays.copyOf(cryptoToken, cryptoToken.length);
 
             // Group ID is arbitrarily set to parent profile user ID. It just represents
             // the default fingerprints for the user.
@@ -915,7 +705,8 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    startEnrollment(token, cryptoClone, groupId, receiver, flags, restricted);
+                    startEnrollment(token, cryptoToken, userId, groupId, receiver, flags,
+                            restricted, opPackageName);
                 }
             });
         }
@@ -932,7 +723,10 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    stopEnrollment(token, true);
+                    ClientMonitor client = mCurrentClient;
+                    if (client instanceof EnrollClient && client.getToken() == token) {
+                        client.stop(client.getToken() == token);
+                    }
                 }
             });
         }
@@ -941,17 +735,18 @@
         public void authenticate(final IBinder token, final long opId, final int groupId,
                 final IFingerprintServiceReceiver receiver, final int flags,
                 final String opPackageName) {
-            if (!canUseFingerprint(opPackageName, true /* foregroundOnly */)) {
-                if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
-                return;
-            }
             final int realUserId = Binder.getCallingUid();
-
+            final int pid = Binder.getCallingPid();
             final boolean restricted = isRestricted();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
+                    if (!canUseFingerprint(opPackageName, true /* foregroundOnly */,
+                            realUserId, pid)) {
+                        if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
+                        return;
+                    }
                     startAuthentication(token, opId, realUserId, groupId, receiver,
                             flags, restricted, opPackageName);
                 }
@@ -959,14 +754,29 @@
         }
 
         @Override // Binder call
-        public void cancelAuthentication(final IBinder token, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
-                return;
-            }
+        public void cancelAuthentication(final IBinder token, final String opPackageName) {
+            final int uid = Binder.getCallingUid();
+            final int pid = Binder.getCallingPid();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    stopAuthentication(token, true);
+                    if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, uid, pid)) {
+                        if (DEBUG) Slog.v(TAG, "cancelAuthentication(): reject " + opPackageName);
+                    } else {
+                        ClientMonitor client = mCurrentClient;
+                        if (client instanceof AuthenticationClient) {
+                            if (client.getToken() == token) {
+                                if (DEBUG) Slog.v(TAG, "stop client " + client.getOwnerString());
+                                client.stop(client.getToken() == token);
+                            } else {
+                                if (DEBUG) Slog.v(TAG, "can't stop client "
+                                        + client.getOwnerString() + " since tokens don't match");
+                            }
+                        } else if (client != null) {
+                            if (DEBUG) Slog.v(TAG, "can't cancel non-authenticating client "
+                                    + client.getOwnerString());
+                        }
+                    }
                 }
             });
         }
@@ -987,10 +797,11 @@
                 final IFingerprintServiceReceiver receiver) {
             checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
             final boolean restricted = isRestricted();
+            final int realUserId = Binder.getCallingUid();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    startRemove(token, fingerId, groupId, receiver, restricted);
+                    startRemove(token, fingerId, realUserId, groupId, receiver, restricted);
                 }
             });
 
@@ -998,7 +809,8 @@
 
         @Override // Binder call
         public boolean isHardwareDetected(long deviceId, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
+            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid())) {
                 return false;
             }
             return mHalDeviceId != 0;
@@ -1021,7 +833,8 @@
 
         @Override // Binder call
         public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
+            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid())) {
                 return Collections.emptyList();
             }
             if (!isCurrentUserOrProfile(userId)) {
@@ -1033,7 +846,8 @@
 
         @Override // Binder call
         public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
+            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid())) {
                 return false;
             }
 
@@ -1061,7 +875,7 @@
             // The permission check should be restored once Android Keystore no longer invokes this
             // method from inside app processes.
 
-            return FingerprintService.this.getAuthenticatorId();
+            return FingerprintService.this.getAuthenticatorId(opPackageName);
         }
 
         @Override // Binder call
@@ -1154,6 +968,7 @@
                     }
                     daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes());
                     mCurrentUserId = userId;
+                    mCurrentAuthenticatorId = daemon.getAuthenticatorId();
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to setActiveGroup():", e);
@@ -1204,14 +1019,12 @@
         }
     }
 
-    public long getAuthenticatorId() {
-        IFingerprintDaemon daemon = getFingerprintDaemon();
-        if (daemon != null) {
-            try {
-                return daemon.getAuthenticatorId();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "getAuthenticatorId failed", e);
-            }
+    public long getAuthenticatorId(String opPackageName) {
+        if (canUseFingerprint(opPackageName, false /* foregroundOnly */,
+                Binder.getCallingUid(), Binder.getCallingPid())) {
+            return mCurrentAuthenticatorId;
+        } else {
+            Slog.w(TAG, "Client isn't current, returning authenticator_id=0");
         }
         return 0;
     }
diff --git a/services/core/java/com/android/server/fingerprint/RemovalClient.java b/services/core/java/com/android/server/fingerprint/RemovalClient.java
new file mode 100644
index 0000000..69a96e1
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/RemovalClient.java
@@ -0,0 +1,117 @@
+/**
+ * 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.
+ */
+
+package com.android.server.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the remove state for a given client.
+ */
+public abstract class RemovalClient extends ClientMonitor {
+    private int mFingerId;
+    private int mUserIdForRemove;
+
+    public RemovalClient(Context context, long halDeviceId, IBinder token,
+            IFingerprintServiceReceiver receiver, int userId, int groupId, int fingerId,
+            boolean restricted, String owner) {
+        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
+        mFingerId = fingerId;
+        mUserIdForRemove = userId;
+    }
+
+    @Override
+    public int start() {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        // The fingerprint template ids will be removed when we get confirmation from the HAL
+        try {
+            final int result = daemon.remove(mFingerId, getUserId());
+            if (result != 0) {
+                Slog.w(TAG, "startRemove with id = " + mFingerId + " failed, result=" + result);
+                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startRemove failed", e);
+        }
+        return 0;
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        // We don't actually stop remove, but inform the client that the cancel operation succeeded
+        // so we can start the next operation.
+        if (initiatedByClient) {
+            onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
+        return 0;
+    }
+
+    /*
+     * @return true if we're done.
+     */
+    private boolean sendRemoved(int fingerId, int groupId) {
+        IFingerprintServiceReceiver receiver = getReceiver();
+        if (receiver == null)
+            return true; // client not listening
+        try {
+            receiver.onRemoved(getHalDeviceId(), fingerId, groupId);
+            return fingerId == 0;
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to notify Removed:", e);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onRemoved(int fingerId, int groupId) {
+        if (fingerId != 0) {
+            if (fingerId != mFingerId)
+            FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(), fingerId,
+                    mUserIdForRemove);
+        } else {
+            mUserIdForRemove = UserHandle.USER_NULL;
+        }
+        return sendRemoved(fingerId, getGroupId());
+    }
+
+    @Override
+    public boolean onEnrollResult(int fingerId, int groupId, int rem) {
+        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for remove!");
+        return true; // Invalid for Remove
+    }
+
+    @Override
+    public boolean onAuthenticated(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onAuthenticated() called for remove!");
+        return true; // Invalid for Remove.
+    }
+
+    @Override
+    public boolean onEnumerationResult(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for remove!");
+        return false; // Invalid for Remove.
+    }
+
+
+}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 60ea254..9b918f3 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -17,6 +17,9 @@
 
 package com.android.server.pm;
 
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,10 +30,12 @@
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
+import android.app.KeyguardManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IntentSender;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
@@ -693,6 +698,37 @@
     }
 
     @Override
+    public boolean trySetQuietModeDisabled(int userHandle, IntentSender target) {
+        if (mContext.getSystemService(StorageManager.class).isUserKeyUnlocked(userHandle)
+                || !mLockPatternUtils.isSecure(userHandle)
+                || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
+            // if the user is already unlocked, no need to show a profile challenge
+            setQuietModeEnabled(userHandle, false);
+            return true;
+        }
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            // otherwise, we show a profile challenge to trigger decryption of the user
+            final KeyguardManager km = (KeyguardManager) mContext.getSystemService(
+                    Context.KEYGUARD_SERVICE);
+            final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null,
+                    userHandle);
+            if (unlockIntent == null) {
+                return false;
+            }
+            if (target != null) {
+                unlockIntent.putExtra(Intent.EXTRA_INTENT, target);
+            }
+            unlockIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            mContext.startActivity(unlockIntent);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return false;
+    }
+
+    @Override
     public void setUserEnabled(int userId) {
         checkManageUsersPermission("enable user");
         synchronized (mPackagesLock) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2e81132..8f259db 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4754,7 +4754,7 @@
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
         if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleOrBehindKeyguardLw()
-                && !win.getGivenInsetsPendingLw()) {
+                && win.isDisplayedLw() && !win.getGivenInsetsPendingLw()) {
             setLastInputMethodWindowLw(null, null);
             offsetInputMethodWindowLw(win);
         }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 9614417..95923fe 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -26,7 +26,7 @@
     void buzzBeepBlinked();
     void notificationLightPulse(int argb, int onMillis, int offMillis);
     void notificationLightOff();
-    void showScreenPinningRequest();
+    void showScreenPinningRequest(int taskId);
     void showAssistDisclosure();
     void startAssist(Bundle args);
     void onCameraLaunchGestureDetected(int source);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index dbbaa5e..e71bdb8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -153,10 +153,10 @@
         }
 
         @Override
-        public void showScreenPinningRequest() {
+        public void showScreenPinningRequest(int taskId) {
             if (mBar != null) {
                 try {
-                    mBar.showScreenPinningRequest();
+                    mBar.showScreenPinningRequest(taskId);
                 } catch (RemoteException e) {
                 }
             }
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 3c84fc2..728e244 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -26,6 +26,7 @@
 
 import android.Manifest;
 import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.ITrustListener;
 import android.app.trust.ITrustManager;
@@ -318,6 +319,12 @@
             synchronized (mDeviceLockedForUser) {
                 mDeviceLockedForUser.put(userId, locked);
             }
+            if (locked) {
+                try {
+                    ActivityManagerNative.getDefault().notifyLockedProfile(userId);
+                } catch (RemoteException e) {
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 10e30ed..91b6914 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -982,6 +982,7 @@
                     wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
                     final WallpaperData fallback = new WallpaperData(wallpaper.userId,
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
+                    ensureSaneWallpaperData(fallback);
                     bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
                     mWaitingForUnlock = true;
                 }
@@ -1773,12 +1774,14 @@
                     wallpaper = new WallpaperData(userId,
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
                     mLockWallpaperMap.put(userId, wallpaper);
+                    ensureSaneWallpaperData(wallpaper);
                 } else {
                     // sanity fallback: we're in bad shape, but establishing a known
                     // valid system+lock WallpaperData will keep us from dying.
                     Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
                     wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
                     mWallpaperMap.put(userId, wallpaper);
+                    ensureSaneWallpaperData(wallpaper);
                 }
             }
         }
@@ -1871,6 +1874,8 @@
             wallpaper.cropHint.set(0, 0, 0, 0);
             wallpaper.padding.set(0, 0, 0, 0);
             wallpaper.name = "";
+
+            mLockWallpaperMap.remove(userId);
         } else {
             if (wallpaper.wallpaperId <= 0) {
                 wallpaper.wallpaperId = makeWallpaperIdLocked();
@@ -1881,6 +1886,14 @@
             }
         }
 
+        ensureSaneWallpaperData(wallpaper);
+        WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
+        if (lockWallpaper != null) {
+            ensureSaneWallpaperData(lockWallpaper);
+        }
+    }
+
+    private void ensureSaneWallpaperData(WallpaperData wallpaper) {
         // We always want to have some reasonable width hint.
         int baseSize = getMaximumSizeDimension();
         if (wallpaper.width < baseSize) {
@@ -2072,11 +2085,11 @@
         }
 
         synchronized (mLock) {
-            pw.println("Current Wallpaper Service state:");
+            pw.println("System wallpaper state:");
             for (int i = 0; i < mWallpaperMap.size(); i++) {
                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
                 pw.print(" User "); pw.print(wallpaper.userId);
-                pw.print(": id="); pw.println(wallpaper.wallpaperId);
+                    pw.print(": id="); pw.println(wallpaper.wallpaperId);
                 pw.print("  mWidth=");
                     pw.print(wallpaper.width);
                     pw.print(" mHeight=");
@@ -2104,6 +2117,18 @@
                     pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
                 }
             }
+            pw.println("Lock wallpaper state:");
+            for (int i = 0; i < mLockWallpaperMap.size(); i++) {
+                WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
+                pw.print(" User "); pw.print(wallpaper.userId);
+                    pw.print(": id="); pw.println(wallpaper.wallpaperId);
+                pw.print("  mWidth="); pw.print(wallpaper.width);
+                    pw.print(" mHeight="); pw.println(wallpaper.height);
+                pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
+                pw.print("  mPadding="); pw.println(wallpaper.padding);
+                pw.print("  mName=");  pw.println(wallpaper.name);
+            }
+
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 4ec297e..545b9db 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -499,7 +499,7 @@
                 + " with replacing child windows.");
         for (int i = allAppWindows.size() - 1; i >= 0; i--) {
             final WindowState w = allAppWindows.get(i);
-            if (w.isChildWindow()) {
+            if (w.shouldBeReplacedWithChildren()) {
                 w.setReplacing(false /* animate */);
             }
         }
@@ -539,8 +539,9 @@
         for (int i = allAppWindows.size() - 1; i >= 0; i--) {
             WindowState candidate = allAppWindows.get(i);
             if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null &&
-                    candidate.getWindowTag().equals(w.getWindowTag().toString())) {
+                    candidate.getWindowTag().toString().equals(w.getWindowTag().toString())) {
                 candidate.mReplacingWindow = w;
+                w.mSkipEnterAnimationForSeamlessReplacement = !candidate.mAnimateReplacingWindow;
 
                 // if we got a replacement window, reset the timeout to give drawing more time
                 service.mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
@@ -575,6 +576,9 @@
                 continue;
             }
             candidate.mWillReplaceWindow = false;
+            if (candidate.mReplacingWindow != null) {
+                candidate.mReplacingWindow.mSkipEnterAnimationForSeamlessReplacement = false;
+            }
             // Since the window already timed out, remove it immediately now.
             // Use removeWindowInnerLocked() instead of removeWindowLocked(), as the latter
             // delays removal on certain conditions, which will leave the stale window in the
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 3ec02b9..c240d07 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -169,6 +169,10 @@
                 + " dimLayerUser=" + dimLayerUser.toShortString()
                 + " state.continueDimming=" + state.continueDimming
                 + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
+        if (state.animator != null && state.animator.mWin.mWillReplaceWindow) {
+            return;
+        }
+
         if (!state.continueDimming && state.dimLayer.isDimming()) {
             state.animator = null;
             dimLayerUser.getDimBounds(mTmpBounds);
@@ -303,7 +307,7 @@
         applyDim(dimLayerUser, animator, true /* aboveApp */);
     }
 
-    private void applyDim(
+    void applyDim(
             DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
         if (dimLayerUser == null) {
             Slog.e(TAG, "Trying to apply dim layer for: " + this
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ff537be..3bd7a96 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -24,6 +24,7 @@
 import static android.view.WindowManager.DOCKED_TOP;
 import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
 import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
+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;
 
@@ -38,8 +39,10 @@
 import android.view.SurfaceControl;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 
 import com.android.server.wm.DimLayer.DimLayerUser;
+import com.android.server.wm.WindowManagerService.H;
 
 import java.util.ArrayList;
 
@@ -74,6 +77,13 @@
      */
     private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f;
 
+    private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR =
+            new PathInterpolator(0.2f, 0f, 0.1f, 1f);
+
+    private static final long IME_ADJUST_ANIM_DURATION = 280;
+
+    private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
+
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
     private int mDividerWindowWidth;
@@ -95,11 +105,13 @@
     private float mAnimationStart;
     private float mAnimationTarget;
     private long mAnimationDuration;
+    private boolean mAnimationStartDelayed;
     private final Interpolator mMinimizedDockInterpolator;
     private float mMaximizeMeetFraction;
     private final Rect mTouchRegion = new Rect();
     private boolean mAnimatingForIme;
     private boolean mAdjustedForIme;
+    private WindowState mDelayedImeWin;
 
     DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
@@ -186,10 +198,16 @@
         return mLastVisibility;
     }
 
-    void setAdjustedForIme(boolean adjusted, boolean animate) {
+    void setAdjustedForIme(boolean adjusted, boolean animate, WindowState imeWin) {
         if (mAdjustedForIme != adjusted) {
-            mAnimatingForIme = animate;
             mAdjustedForIme = adjusted;
+            if (animate) {
+                startImeAdjustAnimation(adjusted, imeWin);
+            } else {
+
+                // Animation might be delayed, so only notify if we don't run an animation.
+                notifyAdjustedForImeChanged(adjusted, 0 /* duration */);
+            }
         }
     }
 
@@ -283,12 +301,27 @@
         mDockedStackListeners.finishBroadcast();
     }
 
+    void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
+        final int size = mDockedStackListeners.beginBroadcast();
+        for (int i = 0; i < size; ++i) {
+            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
+            try {
+                listener.onAdjustedForImeChanged(adjustedForIme, animDuration);
+            } catch (RemoteException e) {
+                Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e);
+            }
+        }
+        mDockedStackListeners.finishBroadcast();
+    }
+
     void registerDockedStackListener(IDockedStackListener listener) {
         mDockedStackListeners.register(listener);
         notifyDockedDividerVisibilityChanged(wasVisible());
         notifyDockedStackExistsChanged(
                 mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
         notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */);
+        notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
+
     }
 
     void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
@@ -371,7 +404,7 @@
             return;
         }
 
-        mAnimatingForIme = false;
+        clearImeAdjustAnimation();
         if (minimizedDock) {
             if (animate) {
                 startAdjustAnimation(0f, 1f);
@@ -387,6 +420,17 @@
         }
     }
 
+    private void clearImeAdjustAnimation() {
+        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
+        for (int i = stacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = stacks.get(i);
+            if (stack != null && stack.isAdjustedForIme()) {
+                stack.resetAdjustedForIme(true /* adjustBoundsNow */);
+            }
+        }
+        mAnimatingForIme = false;
+    }
+
     private void startAdjustAnimation(float from, float to) {
         mAnimatingForMinimizedDockedStack = true;
         mAnimationStarted = false;
@@ -394,6 +438,48 @@
         mAnimationTarget = to;
     }
 
+    private void startImeAdjustAnimation(boolean adjusted, WindowState imeWin) {
+        mAnimatingForIme = true;
+        mAnimationStarted = false;
+        mAnimationStart = adjusted ? 0 : 1;
+        mAnimationTarget = adjusted ? 1 : 0;
+
+        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
+        for (int i = stacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = stacks.get(i);
+            if (stack.isVisibleLocked() && stack.isAdjustedForIme()) {
+                stack.beginImeAdjustAnimation();
+            }
+        }
+
+        // We put all tasks into drag resizing mode - wait until all of them have completed the
+        // drag resizing switch.
+        if (!mService.mWaitingForDrawn.isEmpty()) {
+            mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
+            mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT,
+                    IME_ADJUST_DRAWN_TIMEOUT);
+            mAnimationStartDelayed = true;
+            if (imeWin != null) {
+
+                // There might be an old window delaying the animation start - clear it.
+                if (mDelayedImeWin != null) {
+                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
+                }
+                mDelayedImeWin = imeWin;
+                imeWin.mWinAnimator.startDelayingAnimationStart();
+            }
+            mService.mWaitingForDrawnCallback = () -> {
+                mAnimationStartDelayed = false;
+                if (mDelayedImeWin != null) {
+                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
+                }
+                notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION);
+            };
+        } else {
+            notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION);
+        }
+    }
+
     private void setMinimizedDockedStack(boolean minimized) {
         final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
         notifyDockedStackMinimizedChanged(minimized, 0);
@@ -413,39 +499,48 @@
         if (mAnimatingForMinimizedDockedStack) {
             return animateForMinimizedDockedStack(now);
         } else if (mAnimatingForIme) {
-            return animateForIme();
+            return animateForIme(now);
         } else {
             return false;
         }
     }
 
-    private boolean animateForIme() {
-        boolean updated = false;
-        boolean animating = false;
-
+    private boolean animateForIme(long now) {
+        if (!mAnimationStarted || mAnimationStartDelayed) {
+            mAnimationStarted = true;
+            mAnimationStartTime = now;
+            mAnimationDuration = (long)
+                    (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
+        }
+        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
+        t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
+                .getInterpolation(t);
         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
+        boolean updated = false;
         for (int i = stacks.size() - 1; i >= 0; --i) {
             final TaskStack stack = stacks.get(i);
             if (stack != null && stack.isAdjustedForIme()) {
-                updated |= stack.updateAdjustForIme();
-                animating |= stack.isAnimatingForIme();
-            }
-        }
-
-        if (updated) {
-            mService.mWindowPlacerLocked.performSurfacePlacement();
-        }
-
-        if (!animating) {
-            mAnimatingForIme = false;
-            for (int i = stacks.size() - 1; i >= 0; --i) {
-                final TaskStack stack = stacks.get(i);
-                if (stack != null) {
-                    stack.clearImeGoingAway();
+                if (t >= 1f && mAnimationTarget == 0f) {
+                    stack.resetAdjustedForIme(true /* adjustBoundsNow */);
+                    updated = true;
+                } else {
+                    updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t),
+                            false /* force */);
+                }
+                if (t >= 1f) {
+                    stack.endImeAdjustAnimation();
                 }
             }
         }
-        return animating;
+        if (updated) {
+            mService.mWindowPlacerLocked.performSurfacePlacement();
+        }
+        if (t >= 1.0f) {
+            mAnimatingForIme = false;
+            return false;
+        } else {
+            return true;
+        }
     }
 
     private boolean animateForMinimizedDockedStack(long now) {
@@ -478,11 +573,15 @@
         }
     }
 
+    private float getInterpolatedAnimationValue(float t) {
+        return t * mAnimationTarget + (1 - t) * mAnimationStart;
+    }
+
     /**
      * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
      */
     private float getMinimizeAmount(TaskStack stack, float t) {
-        final float naturalAmount = t * mAnimationTarget + (1 - t) * mAnimationStart;
+        final float naturalAmount = getInterpolatedAnimationValue(t);
         if (isAnimationMaximizing()) {
             return adjustMaximizeAmount(stack, t, naturalAmount);
         } else {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7b16dbe..46a8dff 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,6 +27,7 @@
 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.RESIZE_TASK;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 import android.app.ActivityManager.StackId;
 import android.content.pm.ActivityInfo;
@@ -456,6 +457,11 @@
     /** Bounds of the task to be used for dimming, as well as touch related tests. */
     @Override
     public void getDimBounds(Rect out) {
+        final DisplayContent displayContent = mStack.getDisplayContent();
+        // It doesn't matter if we in particular are part of the resize, since we couldn't have
+        // a DimLayer anyway if we weren't visible.
+        final boolean dockedResizing = displayContent != null ?
+                displayContent.mDividerControllerLocked.isResizing() : false;
         if (useCurrentBounds()) {
             if (inFreeformWorkspace() && getMaxVisibleBounds(out)) {
                 return;
@@ -464,8 +470,16 @@
             if (!mFullscreen) {
                 // When minimizing the docked stack when going home, we don't adjust the task bounds
                 // so we need to intersect the task bounds with the stack bounds here.
-                mStack.getBounds(mTmpRect);
-                mTmpRect.intersect(mBounds);
+                //
+                // If we are Docked Resizing with snap points, the task bounds could be smaller than the stack
+                // bounds and so we don't even want to use them. Even if the app should not be resized the Dim
+                // should keep up with the divider.
+                if (dockedResizing) {
+                    mStack.getBounds(out);
+                } else {
+                    mStack.getBounds(mTmpRect);
+                    mTmpRect.intersect(mBounds);
+                }
                 out.set(mTmpRect);
             } else {
                 out.set(mBounds);
@@ -476,7 +490,7 @@
         // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
         // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
         // system.
-        mStack.getDisplayContent().getLogicalDisplayRect(out);
+        displayContent.getLogicalDisplayRect(out);
     }
 
     void setDragResizing(boolean dragResizing, int dragResizeMode) {
@@ -509,6 +523,22 @@
         return mDragResizeMode;
     }
 
+    /**
+     * Adds all of the tasks windows to {@link WindowManagerService#mWaitingForDrawn} if drag
+     * resizing state of the window has been changed.
+     */
+    void addWindowsWaitingForDrawnIfResizingChanged() {
+        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
+            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                final WindowState win = windows.get(winNdx);
+                if (win.isDragResizeChanged()) {
+                    mService.mWaitingForDrawn.add(win);
+                }
+            }
+        }
+    }
+
     void updateDisplayInfo(final DisplayContent displayContent) {
         if (displayContent == null) {
             return;
@@ -561,7 +591,16 @@
 
                     // If we are not drag resizing, force recreating of a new surface so updating
                     // the content and positioning that surface will be in sync.
-                    if (!win.computeDragResizing()) {
+                    //
+                    // As we use this flag as a hint to freeze surface boundary updates,
+                    // we'd like to only apply this to TYPE_BASE_APPLICATION,
+                    // windows of TYPE_APPLICATION like dialogs, could appear
+                    // to not be drag resizing while they resize, but we'd
+                    // still like to manipulate their frame to update crop, etc...
+                    //
+                    // Anyway we don't need to synchronize position and content updates for these
+                    // windows since they aren't at the base layer and could be moved around anyway.
+                    if (!win.computeDragResizing() && win.mAttrs.type == TYPE_BASE_APPLICATION) {
                         win.mResizedWhileNotDragResizing = true;
                     }
                 }
@@ -619,6 +658,16 @@
         return false;
     }
 
+    boolean isVisible() {
+        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+            final AppWindowToken appToken = mAppTokens.get(i);
+            if (appToken.isVisible()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     boolean inHomeStack() {
         return mStack != null && mStack.mStackId == HOME_STACK_ID;
     }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7074a83..c322cd8 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -26,6 +26,7 @@
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
@@ -40,6 +41,7 @@
 import android.util.SparseArray;
 import android.view.DisplayInfo;
 import android.view.Surface;
+import android.view.animation.PathInterpolator;
 
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
@@ -52,14 +54,6 @@
 public class TaskStack implements DimLayer.DimLayerUser,
         BoundsAnimationController.AnimateBoundsUser {
 
-    // If the stack should be resized to fullscreen.
-    private static final boolean FULLSCREEN = true;
-
-    // When we have a top-bottom split screen, we shift the bottom stack up to accommodate
-    // the IME window. The static flag below controls whether to run animation when the
-    // IME window goes away.
-    private static final boolean ANIMATE_IME_GOING_AWAY = false;
-
     /** Unique identifier */
     final int mStackId;
 
@@ -83,6 +77,12 @@
     /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
     private final Rect mAdjustedBounds = new Rect();
 
+    /**
+     * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
+     * represent the state when the animation has ended.
+     */
+    private final Rect mFullyAdjustedImeBounds = new Rect();
+
     /** Whether mBounds is fullscreen */
     private boolean mFullscreen = true;
 
@@ -118,6 +118,7 @@
     private boolean mImeGoingAway;
     private WindowState mImeWin;
     private float mMinimizeAmount;
+    private float mAdjustImeAmount;
     private final int mDockedStackMinimizeThickness;
 
     // If this is true, the task will be down or upscaled
@@ -211,21 +212,26 @@
      * the normal task bounds.
      *
      * @param bounds The adjusted bounds.
-     * @param keepInsets Whether to keep the insets from the original bounds or to calculate new
-     *                   ones depending on the adjusted bounds.
-     * @return true if the adjusted bounds has changed.
      */
-    private boolean setAdjustedBounds(Rect bounds, boolean keepInsets) {
-        if (mAdjustedBounds.equals(bounds)) {
-            return false;
+    private void setAdjustedBounds(Rect bounds) {
+        if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
+            return;
         }
 
         mAdjustedBounds.set(bounds);
         final boolean adjusted = !mAdjustedBounds.isEmpty();
-        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds,
-                adjusted && keepInsets ? mBounds : null);
+        Rect insetBounds = null;
+        if (adjusted && isAdjustedForMinimizedDock()) {
+            insetBounds = mBounds;
+        } else if (adjusted && isAdjustedForIme()) {
+            if (mImeGoingAway) {
+                insetBounds = mBounds;
+            } else {
+                insetBounds = mFullyAdjustedImeBounds;
+            }
+        }
+        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds);
         mDisplayContent.layoutNeeded = true;
-        return true;
     }
 
     private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
@@ -828,17 +834,18 @@
      * @param imeWin The IME window.
      */
     void setAdjustedForIme(WindowState imeWin) {
-        mAdjustedForIme = true;
         mImeWin = imeWin;
         mImeGoingAway = false;
+        if (!mAdjustedForIme) {
+            mAdjustedForIme = true;
+            mAdjustImeAmount = 0f;
+            updateAdjustForIme(0f, true /* force */);
+        }
     }
 
     boolean isAdjustedForIme() {
         return mAdjustedForIme || mImeGoingAway;
     }
-    void clearImeGoingAway() {
-        mImeGoingAway = false;
-    }
 
     boolean isAnimatingForIme() {
         return mImeWin != null && mImeWin.isAnimatingLw();
@@ -852,16 +859,14 @@
      *
      * @return true if a traversal should be performed after the adjustment.
      */
-    boolean updateAdjustForIme() {
-        boolean stopped = false;
-        if (mImeGoingAway && (!ANIMATE_IME_GOING_AWAY || !isAnimatingForIme())) {
-            mImeWin = null;
-            mAdjustedForIme = false;
-            stopped = true;
+    boolean updateAdjustForIme(float adjustAmount, boolean force) {
+        if (adjustAmount != mAdjustImeAmount || force) {
+            mAdjustImeAmount = adjustAmount;
+            updateAdjustedBounds();
+            return isVisibleForUserLocked();
+        } else {
+            return false;
         }
-        // Make sure to run a traversal when the animation stops so that the stack
-        // is moved to its final position.
-        return updateAdjustedBounds() || stopped;
     }
 
     /**
@@ -875,6 +880,7 @@
             mImeWin = null;
             mAdjustedForIme = false;
             mImeGoingAway = false;
+            mAdjustImeAmount = 0f;
             updateAdjustedBounds();
         } else {
             mImeGoingAway |= mAdjustedForIme;
@@ -901,10 +907,32 @@
         return mMinimizeAmount != 0f;
     }
 
+    /**
+     * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows
+     * to the list of to be drawn windows the service is waiting for.
+     */
+    void beginImeAdjustAnimation() {
+        for (int j = mTasks.size() - 1; j >= 0; j--) {
+            final Task task = mTasks.get(j);
+            if (task.isVisibleForUser()) {
+                task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+                task.addWindowsWaitingForDrawnIfResizingChanged();
+            }
+        }
+    }
+
+    /**
+     * Resets the resizing state of all windows.
+     */
+    void endImeAdjustAnimation() {
+        for (int j = mTasks.size() - 1; j >= 0; j--) {
+            mTasks.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+        }
+    }
+
     private boolean adjustForIME(final WindowState imeWin) {
         final int dockedSide = getDockSide();
         final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
-        final Rect adjustedBounds = mTmpAdjustedBounds;
         if (imeWin == null || !dockedTopOrBottom) {
             return false;
         }
@@ -917,41 +945,37 @@
         contentBounds.set(displayContentRect);
         int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
 
-        // if IME window is animating, get its actual vertical shown position (but no smaller than
-        // the final target vertical position)
-        if (imeWin.isAnimatingLw()) {
-            imeTop = Math.max(imeTop, imeWin.getShownPositionLw().y);
-        }
         imeTop += imeWin.getGivenContentInsetsLw().top;
         if (contentBounds.bottom > imeTop) {
             contentBounds.bottom = imeTop;
         }
 
-        // If content bounds not changing, nothing to do.
-        if (mLastContentBounds.equals(contentBounds)) {
-            return true;
-        }
-
-        // Content bounds changed, need to apply adjustments depending on dock sides.
         mLastContentBounds.set(contentBounds);
-        adjustedBounds.set(mBounds);
         final int yOffset = displayContentRect.bottom - contentBounds.bottom;
 
         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.
-            adjustedBounds.bottom = Math.max(
-                    adjustedBounds.bottom - yOffset, displayContentRect.top);
+            int bottom = Math.max(
+                    mBounds.bottom - yOffset, displayContentRect.top);
+            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();
-            adjustedBounds.top = Math.max(
-                    adjustedBounds.top - yOffset, displayContentRect.top + dividerWidth);
-            adjustedBounds.bottom = adjustedBounds.top + mBounds.height();
+            int top = Math.max(mBounds.top - yOffset, displayContentRect.top + dividerWidth);
+            mTmpAdjustedBounds.set(mBounds);
+            mTmpAdjustedBounds.top =
+                    (int) (mAdjustImeAmount * top + (1 - mAdjustImeAmount) * mBounds.top);
+            mFullyAdjustedImeBounds.set(mBounds);
+            mFullyAdjustedImeBounds.top = top;
+            mFullyAdjustedImeBounds.bottom = top + mBounds.height();
         }
         return true;
     }
@@ -1007,7 +1031,7 @@
     /**
      * Updates the adjustment depending on it's current state.
      */
-    boolean updateAdjustedBounds() {
+    void updateAdjustedBounds() {
         boolean adjust = false;
         if (mMinimizeAmount != 0f) {
             adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
@@ -1018,7 +1042,7 @@
             mTmpAdjustedBounds.setEmpty();
             mLastContentBounds.setEmpty();
         }
-        return setAdjustedBounds(mTmpAdjustedBounds, isAdjustedForMinimizedDockedStack());
+        setAdjustedBounds(mTmpAdjustedBounds);
     }
 
     boolean isAdjustedForMinimizedDockedStack() {
@@ -1030,6 +1054,9 @@
         pw.println(prefix + "mDeferDetach=" + mDeferDetach);
         pw.println(prefix + "mFullscreen=" + mFullscreen);
         pw.println(prefix + "mBounds=" + mBounds.toShortString());
+        if (!mAdjustedBounds.isEmpty()) {
+            pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
+        }
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) {
             mTasks.get(taskNdx).dump(prefix + "  ", pw);
         }
@@ -1158,7 +1185,7 @@
         return mDragResizing;
     }
 
-    private void setDragResizingLocked(boolean resizing) {
+    void setDragResizingLocked(boolean resizing) {
         if (mDragResizing == resizing) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 57dc97a..a4238c1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -646,17 +646,24 @@
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
+            if (uri == null) {
+                return;
+            }
+
             if (mDisplayInversionEnabledUri.equals(uri)) {
                 updateCircularDisplayMaskIfNeeded();
             } else {
                 @UpdateAnimationScaleMode
                 final int mode;
-                if (uri.equals(mWindowAnimationScaleUri)) {
+                if (mWindowAnimationScaleUri.equals(uri)) {
                     mode = WINDOW_ANIMATION_SCALE;
-                } else if (uri.equals(mTransitionAnimationScaleUri)) {
+                } else if (mTransitionAnimationScaleUri.equals(uri)) {
                     mode = TRANSITION_ANIMATION_SCALE;
-                } else { // uri.equals(mAnimationDurationScaleUri)
+                } else if (mAnimationDurationScaleUri.equals(uri)) {
                     mode = ANIMATION_DURATION_SCALE;
+                } else {
+                    // Ignoring unrecognized content changes
+                    return;
                 }
                 Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0);
                 mH.sendMessage(m);
@@ -2595,7 +2602,7 @@
 
     void repositionChild(Session session, IWindow client,
             int left, int top, int right, int bottom,
-            long deferTransactionUntilFrame, Rect outFrame) {
+            long frameNumber, Rect outFrame) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild");
         long origId = Binder.clearCallingIdentity();
 
@@ -2631,10 +2638,8 @@
 
                         win.mWinAnimator.setSurfaceBoundariesLocked(false);
 
-                        if (deferTransactionUntilFrame > 0) {
-                            win.mWinAnimator.mSurfaceController.deferTransactionUntil(
-                                    win.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
-                                    deferTransactionUntilFrame);
+                        if (frameNumber > 0) {
+                            win.mWinAnimator.deferTransactionUntilParentFrame(frameNumber);
                         }
 
                     } finally {
@@ -5998,7 +6003,7 @@
             @Override
             public void run() {
                 Bitmap bm = screenshotApplicationsInner(null, Display.DEFAULT_DISPLAY, -1, -1,
-                        true, 1f);
+                        true, 1f, Bitmap.Config.ARGB_8888);
                 try {
                     receiver.send(bm);
                 } catch (RemoteException e) {
@@ -6011,8 +6016,7 @@
 
     /**
      * Takes a snapshot of the screen.  In landscape mode this grabs the whole screen.
-     * In portrait mode, it grabs the upper region of the screen based on the vertical dimension
-     * of the target image.
+     * In portrait mode, it grabs the full screenshot.
      *
      * @param displayId the Display to take a screenshot of.
      * @param width the width of the target bitmap
@@ -6029,14 +6033,14 @@
         try {
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotApplications");
             return screenshotApplicationsInner(appToken, displayId, width, height, false,
-                    frameScale);
+                    frameScale, Bitmap.Config.RGB_565);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
     Bitmap screenshotApplicationsInner(IBinder appToken, int displayId, int width, int height,
-            boolean includeFullDisplay, float frameScale) {
+            boolean includeFullDisplay, float frameScale, Bitmap.Config config) {
         final DisplayContent displayContent;
         synchronized(mWindowMap) {
             displayContent = getDisplayContentLocked(displayId);
@@ -6275,7 +6279,7 @@
 
         // Create a copy of the screenshot that is immutable and backed in ashmem.
         // This greatly reduces the overhead of passing the bitmap between processes.
-        Bitmap ret = bm.createAshmemBitmap();
+        Bitmap ret = bm.createAshmemBitmap(config);
         bm.recycle();
         return ret;
     }
@@ -7439,7 +7443,7 @@
         }
     }
 
-    private void adjustForImeIfNeeded(final DisplayContent displayContent) {
+    void adjustForImeIfNeeded(final DisplayContent displayContent) {
         final WindowState imeWin = mInputMethodWindow;
         final TaskStack focusedStack =
                 mCurrentFocus != null ? mCurrentFocus.getStack() : null;
@@ -7455,14 +7459,14 @@
                     stack.setAdjustedForIme(imeWin);
                 }
             }
-            displayContent.mDividerControllerLocked.setAdjustedForIme(true, true);
+            displayContent.mDividerControllerLocked.setAdjustedForIme(true, true, imeWin);
         } else {
             final ArrayList<TaskStack> stacks = displayContent.getStacks();
             for (int i = stacks.size() - 1; i >= 0; --i) {
                 final TaskStack stack = stacks.get(i);
                 stack.resetAdjustedForIme(!dockVisible);
             }
-            displayContent.mDividerControllerLocked.setAdjustedForIme(false, dockVisible);
+            displayContent.mDividerControllerLocked.setAdjustedForIme(false, dockVisible, imeWin);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8371cfe..bf69717 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -82,6 +82,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -444,6 +445,11 @@
     // If not null, the window that will be used to replace the old one. This is being set when
     // the window is added and unset when this window reports its first draw.
     WindowState mReplacingWindow = null;
+    // For the new window in the replacement transition, if we have
+    // requested to replace without animation, then we should
+    // make sure we also don't apply an enter animation for
+    // the new window.
+    boolean mSkipEnterAnimationForSeamlessReplacement = false;
     // Whether this window is being moved via the resize API
     boolean mMovedByResize;
 
@@ -1552,7 +1558,7 @@
             // If app died visible, apply a dim over the window to indicate that it's inactive
             mDisplayContent.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
         } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
-                && mDisplayContent != null && !mAnimatingExit && isDisplayedLw()) {
+                && mDisplayContent != null && !mAnimatingExit && isVisibleUnchecked()) {
             mDisplayContent.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
         }
     }
@@ -1571,12 +1577,16 @@
         }
         for (int i = mAppToken.allAppWindows.size() - 1; i >= 0; i--) {
             final WindowState win = mAppToken.allAppWindows.get(i);
-            if (win.mWillReplaceWindow && win.mReplacingWindow == this) {
+            if (win.mWillReplaceWindow && win.mReplacingWindow == this && hasDrawnLw()) {
                 if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + win);
+                if (win.isDimming()) {
+                    win.transferDimToReplacement();
+                }
                 win.mWillReplaceWindow = false;
                 win.mAnimateReplacingWindow = false;
                 win.mReplacingRemoveRequested = false;
                 win.mReplacingWindow = null;
+                mSkipEnterAnimationForSeamlessReplacement = false;
                 if (win.mAnimatingExit) {
                     mService.removeWindowInnerLocked(win);
                 }
@@ -2710,4 +2720,25 @@
         }
         return winY;
     }
+
+    void transferDimToReplacement() {
+        final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
+        if (dimLayerUser != null && mDisplayContent != null) {
+            mDisplayContent.mDimLayerController.applyDim(dimLayerUser,
+                    mReplacingWindow.mWinAnimator,
+                    (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? true : false);
+        }
+    }
+
+    // During activity relaunch due to resize, we sometimes use window replacement
+    // for only child windows (as the main window is handled by window preservation)
+    // and the big surface.
+    //
+    // Though windows of TYPE_APPLICATION (as opposed to TYPE_BASE_APPLICATION)
+    // are not children in the sense of an attached window, we also want to replace
+    // them at such phases, as they won't be covered by window preservation,
+    // and in general we expect them to return following relaunch.
+    boolean shouldBeReplacedWithChildren() {
+        return isChildWindow() || mAttrs.type == TYPE_APPLICATION;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0245513..6fcc8f9 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -21,7 +21,8 @@
 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
 import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static com.android.server.wm.AppWindowAnimator.sDummyAnimation;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
@@ -182,6 +183,8 @@
      * window is first added or shown, cleared when the callback has been made. */
     boolean mEnteringAnimation;
 
+    private boolean mAnimationStartDelayed;
+
     boolean mKeyguardGoingAwayAnimation;
     boolean mKeyguardGoingAwayWithWallpaper;
 
@@ -220,6 +223,10 @@
 
     int mAttrType;
 
+    static final long PENDING_TRANSACTION_FINISH_WAIT_TIME = 100;
+    long mDeferTransactionUntilFrame = -1;
+    long mDeferTransactionTime = -1;
+
     private final Rect mTmpSize = new Rect();
 
     WindowStateAnimator(final WindowState win) {
@@ -294,7 +301,7 @@
     /** Is the window animating the DummyAnimation? */
     boolean isDummyAnimation() {
         return mAppAnimator != null
-                && mAppAnimator.animation == AppWindowAnimator.sDummyAnimation;
+                && mAppAnimator.animation == sDummyAnimation;
     }
 
     /** Is this window currently set to animate or currently animating? */
@@ -318,8 +325,12 @@
         if ((mAnimation == null) || !mLocalAnimating) {
             return false;
         }
+        currentTime = getAnimationFrameTime(mAnimation, currentTime);
         mTransformation.clear();
         final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
+        if (mAnimationStartDelayed && mAnimationIsEntrance) {
+            mTransformation.setAlpha(0f);
+        }
         if (false && DEBUG_ANIM) Slog.v(TAG, "Stepped animation in " + this + ": more=" + more
                 + ", xform=" + mTransformation);
         return more;
@@ -1673,6 +1684,12 @@
     }
 
     void applyEnterAnimationLocked() {
+        // If we are the new part of a window replacement transition and we have requested
+        // not to animate, we instead want to make it seamless, so we don't want to apply
+        // an enter transition.
+        if (mWin.mSkipEnterAnimationForSeamlessReplacement) {
+            return;
+        }
         final int transit;
         if (mEnterAnimationPending) {
             mEnterAnimationPending = false;
@@ -1755,7 +1772,9 @@
         } else {
             clearAnimation();
         }
-
+        if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+            mService.adjustForImeIfNeeded(mWin.mDisplayContent);
+        }
         return mAnimation != null;
     }
 
@@ -1834,6 +1853,9 @@
                     pw.print(" mDsDy="); pw.print(mDsDy);
                     pw.print(" mDtDy="); pw.println(mDtDy);
         }
+        if (mAnimationStartDelayed) {
+            pw.print(prefix); pw.print("mAnimationStartDelayed="); pw.print(mAnimationStartDelayed);
+        }
     }
 
     @Override
@@ -1880,4 +1902,57 @@
         mAnimDy = mWin.mLastFrame.top - top;
         mAnimateMove = true;
     }
+
+    void deferTransactionUntilParentFrame(long frameNumber) {
+        if (!mWin.isChildWindow()) {
+            return;
+        }
+        mDeferTransactionUntilFrame = frameNumber;
+        mDeferTransactionTime = System.currentTimeMillis();
+        mSurfaceController.deferTransactionUntil(
+                mWin.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
+                frameNumber);
+    }
+
+    // Defer the current transaction to the frame number of the last saved transaction.
+    // We do this to avoid shooting through an unsynchronized transaction while something is
+    // pending. This is generally fine, as either we will get in on the synchronization,
+    // or SurfaceFlinger will see that the frame has already occured. The only
+    // potential problem is in frame number resets so we reset things with a timeout
+    // every so often to be careful.
+    void deferToPendingTransaction() {
+        if (mDeferTransactionUntilFrame < 0) {
+            return;
+        }
+        long time = System.currentTimeMillis();
+        if (time > mDeferTransactionTime + PENDING_TRANSACTION_FINISH_WAIT_TIME) {
+            mDeferTransactionTime = -1;
+            mDeferTransactionUntilFrame = -1;
+        } else {
+            mSurfaceController.deferTransactionUntil(
+                    mWin.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
+                    mDeferTransactionUntilFrame);
+        }
+    }
+
+    /**
+     * Sometimes we need to synchronize the first frame of animation with some external event.
+     * To achieve this, we prolong the start of the animation and keep producing the first frame of
+     * the animation.
+     */
+    private long getAnimationFrameTime(Animation animation, long currentTime) {
+        if (mAnimationStartDelayed) {
+            animation.setStartTime(currentTime);
+            return currentTime + 1;
+        }
+        return currentTime;
+    }
+
+    void startDelayingAnimationStart() {
+        mAnimationStartDelayed = true;
+    }
+
+    void endDelayingAnimationStart() {
+        mAnimationStartDelayed = false;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 04aa735..1e6c585 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -721,6 +721,10 @@
 
                 // Moved from updateWindowsAndWallpaperLocked().
                 if (w.mHasSurface) {
+                    // If we have recently synchronized a previous transaction for this
+                    // window ensure we don't push through an unsynchronized one now.
+                    winAnimator.deferToPendingTransaction();
+
                     // Take care of the window being ready to display.
                     final boolean committed = winAnimator.commitFinishDrawingLocked();
                     if (isDefaultDisplay && committed) {
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 8aa0e34..cfc5305 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -33,8 +33,6 @@
     // From the top of ril.cpp
     int RIL_ERRNO_INVALID_RESPONSE = -1;
 
-    int MAX_INT = 0x7FFFFFFF;
-
     // from RIL_Errno
     int SUCCESS = 0;
     int RADIO_NOT_AVAILABLE = 1;              /* If radio did not start or is resetting */