Merge "Added useExternalScores field to WifiConfiguration." into nyc-dev
diff --git a/api/system-current.txt b/api/system-current.txt
index 104cf50..0ba3fd8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -25315,6 +25315,41 @@
     method public void onTetheringStarted();
   }
 
+  public final class ConnectivityMetricsEvent implements android.os.Parcelable {
+    ctor public ConnectivityMetricsEvent(long, int, int, android.os.Parcelable);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.ConnectivityMetricsEvent> CREATOR;
+    field public final int componentTag;
+    field public final android.os.Parcelable data;
+    field public final int eventTag;
+    field public final long timestamp;
+  }
+
+  public static final class ConnectivityMetricsEvent.Reference implements android.os.Parcelable {
+    ctor public ConnectivityMetricsEvent.Reference(long);
+    method public int describeContents();
+    method public long getValue();
+    method public void readFromParcel(android.os.Parcel);
+    method public void setValue(long);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.ConnectivityMetricsEvent.Reference> CREATOR;
+  }
+
+  public class ConnectivityMetricsLogger {
+    ctor public ConnectivityMetricsLogger();
+    method public void logEvent(long, int, int, android.os.Parcelable);
+    field public static final int COMPONENT_TAG_BLUETOOTH = 1; // 0x1
+    field public static final int COMPONENT_TAG_CONNECTIVITY = 0; // 0x0
+    field public static final int COMPONENT_TAG_TELECOM = 3; // 0x3
+    field public static final int COMPONENT_TAG_TELEPHONY = 4; // 0x4
+    field public static final int COMPONENT_TAG_WIFI = 2; // 0x2
+    field public static final java.lang.String CONNECTIVITY_METRICS_LOGGER_SERVICE = "connectivity_metrics_logger";
+    field public static final java.lang.String DATA_KEY_EVENTS_COUNT = "count";
+    field public static final int NUMBER_OF_COMPONENTS = 5; // 0x5
+    field public static final int TAG_SKIPPED_EVENTS = -1; // 0xffffffff
+  }
+
   public class Credentials {
     ctor public Credentials(int, int, int);
     method public int getGid();
@@ -26387,9 +26422,11 @@
     field public int bwSupported;
     field public boolean lciSupported;
     field public boolean lcrSupported;
+    field public int mcVersion;
     field public boolean oneSidedRttSupported;
     field public int preambleSupported;
     field public boolean responderSupported;
+    field public boolean secureRttSupported;
     field public deprecated boolean supportedPeerType;
     field public deprecated boolean supportedType;
     field public boolean twoSided11McRttSupported;
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/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/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index aa1e372..4108f6d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1210,7 +1210,7 @@
             // For those APKs we only care about extracting signer certificates, and don't care
             // about verifying integrity.
             boolean signatureSchemeRollbackProtectionsEnforced =
-                    (parseFlags & PARSE_IS_SYSTEM) == 0;
+                    (parseFlags & PARSE_IS_SYSTEM_DIR) == 0;
             jarFile = new StrictJarFile(
                     apkPath,
                     !verified, // whether to verify JAR signature
@@ -1239,7 +1239,7 @@
             toVerify.add(manifestEntry);
 
             // If we're parsing an untrusted package, verify all contents
-            if ((parseFlags & PARSE_IS_SYSTEM) == 0) {
+            if ((parseFlags & PARSE_IS_SYSTEM_DIR) == 0) {
                 final Iterator<ZipEntry> i = jarFile.iterator();
                 while (i.hasNext()) {
                     final ZipEntry entry = i.next();
@@ -1679,7 +1679,6 @@
     private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
             XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
             IOException {
-        final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0;
         mParseInstrumentationArgs = null;
         mParseActivityArgs = null;
         mParseServiceArgs = null;
@@ -1769,8 +1768,6 @@
                     return null;
                 }
             } else if (tagName.equals(TAG_OVERLAY)) {
-                pkg.mTrustedOverlay = trustedOverlay;
-
                 sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay);
                 pkg.mOverlayTarget = sa.getString(
@@ -2924,12 +2921,14 @@
             ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
         }
 
-        if (sa.getBoolean(R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage,
-                false) && (flags & PARSE_IS_SYSTEM) != 0) {
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage,
+                false)) {
             ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
         }
-        if (sa.getBoolean(R.styleable.AndroidManifestApplication_directBootAware, false)
-                && (flags & PARSE_IS_SYSTEM) != 0) {
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_directBootAware,
+                false)) {
             ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
         }
 
@@ -3554,7 +3553,7 @@
 
             a.info.encryptionAware = a.info.directBootAware = sa.getBoolean(
                     R.styleable.AndroidManifestActivity_directBootAware,
-                    owner.applicationInfo.isDirectBootAware());
+                    false);
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
@@ -3572,7 +3571,7 @@
 
             a.info.encryptionAware = a.info.directBootAware = sa.getBoolean(
                     R.styleable.AndroidManifestActivity_directBootAware,
-                    owner.applicationInfo.isDirectBootAware());
+                    false);
         }
 
         if (a.info.directBootAware) {
@@ -3985,7 +3984,7 @@
 
         p.info.encryptionAware = p.info.directBootAware = sa.getBoolean(
                 R.styleable.AndroidManifestProvider_directBootAware,
-                owner.applicationInfo.isDirectBootAware());
+                false);
         if (p.info.directBootAware) {
             owner.applicationInfo.privateFlags |=
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
@@ -4277,7 +4276,7 @@
 
         s.info.encryptionAware = s.info.directBootAware = sa.getBoolean(
                 R.styleable.AndroidManifestService_directBootAware,
-                owner.applicationInfo.isDirectBootAware());
+                false);
         if (s.info.directBootAware) {
             owner.applicationInfo.privateFlags |=
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
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/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index b5d67d3..5153ba9 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -16,10 +16,12 @@
 
 package android.net;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /** {@hide} */
+@SystemApi
 public final class ConnectivityMetricsEvent implements Parcelable {
 
     /**  The time when this event was collected, as returned by System.currentTimeMillis(). */
@@ -80,12 +82,13 @@
     }
 
     /** {@hide} */
-    public static class Reference implements Parcelable {
+    @SystemApi
+    public final static class Reference implements Parcelable {
 
-        public long value;
+        private long mValue;
 
         public Reference(long ref) {
-            this.value = ref;
+            this.mValue = ref;
         }
 
         /** Implement the Parcelable interface */
@@ -109,11 +112,19 @@
         /** Implement the Parcelable interface */
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeLong(value);
+            dest.writeLong(mValue);
         }
 
         public void readFromParcel(Parcel in) {
-            value = in.readLong();
+            mValue = in.readLong();
+        }
+
+        public long getValue() {
+            return mValue;
+        }
+
+        public void setValue(long val) {
+            mValue = val;
         }
     }
 }
diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java
index eafb8ac..b49cc2b 100644
--- a/core/java/android/net/ConnectivityMetricsLogger.java
+++ b/core/java/android/net/ConnectivityMetricsLogger.java
@@ -15,6 +15,7 @@
  */
 package android.net;
 
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.RemoteException;
@@ -22,6 +23,7 @@
 import android.util.Log;
 
 /** {@hide} */
+@SystemApi
 public class ConnectivityMetricsLogger {
     private static String TAG = "ConnectivityMetricsLogger";
     private static final boolean DBG = true;
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/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 080ed9a..2481e04 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -163,17 +163,33 @@
     ///////////////////////////////////////////////////////////////////////////
 
     /**
-     * Calls the function specified with the drawGLFunction function pointer. This is
-     * functionality used by webkit for calling into their renderer from our display lists.
-     * This function may return true if an invalidation is needed after the call.
+     * Records the functor specified with the drawGLFunction function pointer. This is
+     * functionality used by webview for calling into their renderer from our display lists.
      *
      * @param drawGLFunction A native function pointer
      */
     public void callDrawGLFunction2(long drawGLFunction) {
-        nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction);
+        nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction, null);
     }
 
-    private static native void nCallDrawGLFunction(long renderer, long drawGLFunction);
+    /**
+     * Records the functor specified with the drawGLFunction function pointer. This is
+     * functionality used by webview for calling into their renderer from our display lists.
+     *
+     * @param drawGLFunction A native function pointer
+     * @param releasedCallback Called when the display list is destroyed, and thus
+     * the functor is no longer referenced by this canvas's display list.
+     *
+     * NOTE: The callback does *not* necessarily mean that there are no longer
+     * any references to the functor, just that the reference from this specific
+     * canvas's display list has been released.
+     */
+    public void drawGLFunctor2(long drawGLFunctor, Runnable releasedCallback) {
+        nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
+    }
+
+    private static native void nCallDrawGLFunction(long renderer,
+            long drawGLFunction, Runnable releasedCallback);
 
     ///////////////////////////////////////////////////////////////////////////
     // Display list
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/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/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 6aac0e4..cadfd3d 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -22,12 +22,12 @@
 
 #include <android_runtime/AndroidRuntime.h>
 
+#include <utils/Looper.h>
 #include <cutils/properties.h>
 
 #include <SkBitmap.h>
 #include <SkRegion.h>
 
-
 #include <Rect.h>
 #include <RenderNode.h>
 #include <CanvasProperty.h>
@@ -41,6 +41,52 @@
 
 using namespace uirenderer;
 
+jmethodID gRunnableMethodId;
+
+static JNIEnv* jnienv(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
+    }
+    return env;
+}
+
+class InvokeRunnableMessage : public MessageHandler {
+public:
+    InvokeRunnableMessage(JNIEnv* env, jobject runnable) {
+        mRunnable = env->NewGlobalRef(runnable);
+        env->GetJavaVM(&mVm);
+    }
+
+    virtual ~InvokeRunnableMessage() {
+        jnienv(mVm)->DeleteGlobalRef(mRunnable);
+    }
+
+    virtual void handleMessage(const Message&) {
+        jnienv(mVm)->CallVoidMethod(mRunnable, gRunnableMethodId);
+    }
+
+private:
+    JavaVM* mVm;
+    jobject mRunnable;
+};
+
+class GlFunctorReleasedCallbackBridge : public GlFunctorLifecycleListener {
+public:
+    GlFunctorReleasedCallbackBridge(JNIEnv* env, jobject javaCallback) {
+        mLooper = Looper::getForThread();
+        mMessage = new InvokeRunnableMessage(env, javaCallback);
+    }
+
+    virtual void onGlFunctorReleased(Functor* functor) override {
+        mLooper->sendMessage(mMessage, 0);
+    }
+
+private:
+    sp<Looper> mLooper;
+    sp<InvokeRunnableMessage> mMessage;
+};
+
 // ----------------------------------------------------------------------------
 // Setup
 // ----------------------------------------------------------------------------
@@ -56,10 +102,12 @@
 // ----------------------------------------------------------------------------
 
 static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
-        jlong canvasPtr, jlong functorPtr) {
+        jlong canvasPtr, jlong functorPtr, jobject releasedCallback) {
     Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
     Functor* functor = reinterpret_cast<Functor*>(functorPtr);
-    canvas->callDrawGLFunction(functor);
+    sp<GlFunctorReleasedCallbackBridge> bridge(new GlFunctorReleasedCallbackBridge(
+            env, releasedCallback));
+    canvas->callDrawGLFunction(functor, bridge.get());
 }
 
 // ----------------------------------------------------------------------------
@@ -184,7 +232,8 @@
     { "nIsAvailable",       "!()Z",             (void*) android_view_DisplayListCanvas_isAvailable },
     { "nInsertReorderBarrier","!(JZ)V",         (void*) android_view_DisplayListCanvas_insertReorderBarrier },
 
-    { "nCallDrawGLFunction", "!(JJ)V",          (void*) android_view_DisplayListCanvas_callDrawGLFunction },
+    { "nCallDrawGLFunction", "!(JJLjava/lang/Runnable;)V",
+            (void*) android_view_DisplayListCanvas_callDrawGLFunction },
 
     { "nDrawRoundRect",     "!(JJJJJJJJ)V",     (void*) android_view_DisplayListCanvas_drawRoundRectProps },
     { "nDrawCircle",        "!(JJJJJ)V",        (void*) android_view_DisplayListCanvas_drawCircleProps },
@@ -207,6 +256,9 @@
 };
 
 int register_android_view_DisplayListCanvas(JNIEnv* env) {
+    jclass runnableClass = FindClassOrDie(env, "java/lang/Runnable");
+    gRunnableMethodId = GetMethodIDOrDie(env, runnableClass, "run", "()V");
+
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
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/floating_popup_overflow_button.xml b/core/res/res/layout/floating_popup_overflow_button.xml
index 7053f3e..12e2000 100644
--- a/core/res/res/layout/floating_popup_overflow_button.xml
+++ b/core/res/res/layout/floating_popup_overflow_button.xml
@@ -24,5 +24,5 @@
     android:paddingEnd="@dimen/floating_toolbar_menu_button_side_padding"
     android:paddingBottom="@dimen/floating_toolbar_menu_image_button_vertical_padding"
     android:scaleType="centerInside"
-    android:background="?attr/selectableItemBackgroundBorderless"
+    android:background="?attr/actionBarItemBackground"
     android:tint="?attr/floatingToolbarForegroundColor" />
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 717a1e6..6259466 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -7,7 +7,7 @@
 # Enables fine-grained GLES error checking
 # If set to true, every GLES call is wrapped & error checked
 # Has moderate overhead
-HWUI_ENABLE_OPENGL_VALIDATION := true
+HWUI_ENABLE_OPENGL_VALIDATION := false
 
 hwui_src_files := \
     font/CacheTexture.cpp \
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 181b343..b572bda 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -73,6 +73,12 @@
         delete path;
     }
 
+    for (auto& iter : functors) {
+        if (iter.listener) {
+            iter.listener->onGlFunctorReleased(iter.functor);
+        }
+    }
+
     patchResources.clear();
     pathResources.clear();
     paints.clear();
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index aba5d4b..5b3227b 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -35,6 +35,7 @@
 #include "Debug.h"
 #include "CanvasProperty.h"
 #include "DeferredDisplayList.h"
+#include "GlFunctorLifecycleListener.h"
 #include "Matrix.h"
 #include "RenderProperties.h"
 
@@ -119,6 +120,11 @@
     virtual void operator ()() {}
 };
 
+struct FunctorContainer {
+    Functor* functor;
+    GlFunctorLifecycleListener* listener;
+};
+
 /**
  * Data structure that holds the list of commands used in display list stream
  */
@@ -154,7 +160,7 @@
     const LsaVector<NodeOpType*>& getChildren() const { return children; }
 
     const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
-    const LsaVector<Functor*>& getFunctors() const { return functors; }
+    const LsaVector<FunctorContainer>& getFunctors() const { return functors; }
     const LsaVector<PushStagingFunctor*>& getPushStagingFunctors() { return pushStagingFunctors; }
 
     size_t addChild(NodeOpType* childOp);
@@ -195,7 +201,7 @@
     LsaVector< sp<VirtualLightRefBase> > referenceHolders;
 
     // List of functors
-    LsaVector<Functor*> functors;
+    LsaVector<FunctorContainer> functors;
 
     // List of functors that need to be notified of pushStaging. Note that this list gets nothing
     // but a callback during sync DisplayList, unlike the list of functors defined above, which
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index c6e92ab..ca968ce 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -81,9 +81,11 @@
     return displayList;
 }
 
-void DisplayListCanvas::callDrawGLFunction(Functor *functor) {
+void DisplayListCanvas::callDrawGLFunction(Functor* functor,
+        GlFunctorLifecycleListener* listener) {
     addDrawOp(new (alloc()) DrawFunctorOp(functor));
-    mDisplayList->functors.push_back(functor);
+    mDisplayList->functors.push_back({functor, listener});
+    mDisplayList->ref(listener);
 }
 
 SkCanvas* DisplayListCanvas::asSkCanvas() {
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index d6a5794..664f79e 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -93,7 +93,8 @@
 // ----------------------------------------------------------------------------
     virtual void drawLayer(DeferredLayerUpdater* layerHandle) override;
     virtual void drawRenderNode(RenderNode* renderNode) override;
-    virtual void callDrawGLFunction(Functor* functor) override;
+    virtual void callDrawGLFunction(Functor* functor,
+            GlFunctorLifecycleListener* listener) override;
 
 // ----------------------------------------------------------------------------
 // CanvasStateClient interface
diff --git a/libs/hwui/GlFunctorLifecycleListener.h b/libs/hwui/GlFunctorLifecycleListener.h
new file mode 100644
index 0000000..357090e
--- /dev/null
+++ b/libs/hwui/GlFunctorLifecycleListener.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <utils/Functor.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace uirenderer {
+
+class GlFunctorLifecycleListener : public VirtualLightRefBase {
+public:
+    virtual ~GlFunctorLifecycleListener() {}
+    virtual void onGlFunctorReleased(Functor* functor) = 0;
+};
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index ab733f1..b49f9b5 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -594,8 +594,10 @@
             layerHandle->backingLayer()));
 }
 
-void RecordingCanvas::callDrawGLFunction(Functor* functor) {
-    mDisplayList->functors.push_back(functor);
+void RecordingCanvas::callDrawGLFunction(Functor* functor,
+        GlFunctorLifecycleListener* listener) {
+    mDisplayList->functors.push_back({functor, listener});
+    mDisplayList->ref(listener);
     addOp(alloc().create_trivial<FunctorOp>(
             *(mState.currentSnapshot()->transform),
             getRecordedClip(),
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 219296c..372be24 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -59,7 +59,8 @@
 
     virtual void drawLayer(DeferredLayerUpdater* layerHandle) override;
     virtual void drawRenderNode(RenderNode* renderNode) override;
-    virtual void callDrawGLFunction(Functor* functor) override;
+    virtual void callDrawGLFunction(Functor* functor,
+            GlFunctorLifecycleListener* listener) override;
 
 // ----------------------------------------------------------------------------
 // CanvasStateClient interface
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index ea06fcd..6e848fd 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -474,8 +474,8 @@
     mDisplayList = mStagingDisplayList;
     mStagingDisplayList = nullptr;
     if (mDisplayList) {
-        for (size_t i = 0; i < mDisplayList->getFunctors().size(); i++) {
-            (*mDisplayList->getFunctors()[i])(DrawGlInfo::kModeSync, nullptr);
+        for (auto& iter : mDisplayList->getFunctors()) {
+            (*iter.functor)(DrawGlInfo::kModeSync, nullptr);
         }
         for (size_t i = 0; i < mDisplayList->getPushStagingFunctors().size(); i++) {
             (*mDisplayList->getPushStagingFunctors()[i])();
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 1b459c1..ce67554 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -160,7 +160,8 @@
 
     virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
     virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
-    virtual void callDrawGLFunction(Functor* functor) override;
+    virtual void callDrawGLFunction(Functor* functor,
+            uirenderer::GlFunctorLifecycleListener* listener) override;
 
 protected:
     virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
@@ -846,6 +847,7 @@
 
 void SkiaCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { }
 
-void SkiaCanvas::callDrawGLFunction(Functor* functor) { }
+void SkiaCanvas::callDrawGLFunction(Functor* functor,
+        uirenderer::GlFunctorLifecycleListener* listener) { }
 
 } // namespace android
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 691cfa0..1c6f48e 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -219,22 +219,24 @@
             int fillType = 0; /* non-zero or kWinding_FillType in Skia */
         };
         FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {}
+        ~FullPathProperties() {
+            SkSafeUnref(fillGradient);
+            SkSafeUnref(strokeGradient);
+        }
         void syncProperties(const FullPathProperties& prop) {
             mPrimitiveFields = prop.mPrimitiveFields;
             mTrimDirty = true;
-            fillGradient.reset(prop.fillGradient);
-            strokeGradient.reset(prop.strokeGradient);
+            UPDATE_SKPROP(fillGradient, prop.fillGradient);
+            UPDATE_SKPROP(strokeGradient, prop.strokeGradient);
             onPropertyChanged();
         }
         void setFillGradient(SkShader* gradient) {
-            if(fillGradient != gradient){
-                fillGradient.reset(gradient);
+            if(UPDATE_SKPROP(fillGradient, gradient)) {
                 onPropertyChanged();
             }
         }
         void setStrokeGradient(SkShader* gradient) {
-            if(strokeGradient != gradient){
-                strokeGradient.reset(gradient);
+            if(UPDATE_SKPROP(strokeGradient, gradient)) {
                 onPropertyChanged();
             }
         }
@@ -346,8 +348,8 @@
             count,
         };
         PrimitiveFields mPrimitiveFields;
-        SkAutoTUnref<SkShader> fillGradient;
-        SkAutoTUnref<SkShader> strokeGradient;
+        SkShader* fillGradient = nullptr;
+        SkShader* strokeGradient = nullptr;
     };
 
     // Called from UI thread
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 5dbda43..55af33e 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -20,6 +20,7 @@
 #include <cutils/compiler.h>
 #include <utils/Functor.h>
 
+#include "GlFunctorLifecycleListener.h"
 #include "utils/NinePatch.h"
 
 #include <SkBitmap.h>
@@ -124,7 +125,8 @@
 
     virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0;
     virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0;
-    virtual void callDrawGLFunction(Functor* functor) = 0;
+    virtual void callDrawGLFunction(Functor* functor,
+            uirenderer::GlFunctorLifecycleListener* listener) = 0;
 
 // ----------------------------------------------------------------------------
 // Canvas state operations
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 209a104..ebc1c80 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -581,7 +581,7 @@
     auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
             [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.translate(0, -800000);
-        canvas.callDrawGLFunction(&noopFunctor);
+        canvas.callDrawGLFunction(&noopFunctor, nullptr);
     });
 
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 7c57a50..b2997df 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -51,3 +51,41 @@
     EXPECT_FALSE(child->hasParents()) << "Child should be removed";
     EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents";
 }
+
+TEST(RenderNode, releasedCallback) {
+    class DecRefOnReleased : public GlFunctorLifecycleListener {
+    public:
+        DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {}
+        void onGlFunctorReleased(Functor* functor) override {
+            *mRefCnt -= 1;
+        }
+    private:
+        int* mRefCnt;
+    };
+
+    int refcnt = 0;
+    sp<DecRefOnReleased> listener(new DecRefOnReleased(&refcnt));
+    Functor noopFunctor;
+
+    auto node = TestUtils::createNode(0, 0, 200, 400,
+            [&](RenderProperties& props, TestCanvas& canvas) {
+        refcnt++;
+        canvas.callDrawGLFunction(&noopFunctor, listener.get());
+    });
+    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+    EXPECT_EQ(1, refcnt);
+
+    TestUtils::recordNode(*node, [&](TestCanvas& canvas) {
+        refcnt++;
+        canvas.callDrawGLFunction(&noopFunctor, listener.get());
+    });
+    EXPECT_EQ(2, refcnt);
+
+    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+    EXPECT_EQ(1, refcnt);
+
+    TestUtils::recordNode(*node, [](TestCanvas& canvas) {});
+    EXPECT_EQ(1, refcnt);
+    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+    EXPECT_EQ(0, refcnt);
+}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 7c6adad..2cd9872 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -27,6 +27,7 @@
 import android.media.MediaDescription;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -475,14 +476,8 @@
         // the service will be told when we connect.
         if (mState == CONNECT_STATE_CONNECTED) {
             try {
-                // NOTE: Do not call addSubscriptionWithOptions when options are null. Otherwise,
-                // it will break the action of support library which expects addSubscription will
-                // be called when options are null.
-                if (options == null) {
-                    mServiceBinder.addSubscription(parentId, mServiceCallbacks);
-                } else {
-                    mServiceBinder.addSubscriptionWithOptions(parentId, options, mServiceCallbacks);
-                }
+                mServiceBinder.addSubscription(parentId, callback.mToken, options,
+                        mServiceCallbacks);
             } catch (RemoteException ex) {
                 // Process is crashing. We will disconnect, and upon reconnect we will
                 // automatically reregister. So nothing to do here.
@@ -497,34 +492,37 @@
             throw new IllegalArgumentException("parentId is empty.");
         }
 
-        // Remove from our list.
         Subscription sub = mSubscriptions.get(parentId);
-
+        if (sub == null) {
+            return;
+        }
         // Tell the service if necessary.
-        if (mState == CONNECT_STATE_CONNECTED && sub != null) {
-            try {
-                if (callback == null) {
-                    mServiceBinder.removeSubscription(parentId, mServiceCallbacks);
-                } else {
-                    final List<SubscriptionCallback> callbacks = sub.getCallbacks();
-                    final List<Bundle> optionsList = sub.getOptionsList();
-                    for (int i = callbacks.size() - 1; i >= 0; --i) {
-                        if (callbacks.get(i) == callback) {
-                            mServiceBinder.removeSubscriptionWithOptions(
-                                    parentId, optionsList.get(i), mServiceCallbacks);
-                            callbacks.remove(i);
-                            optionsList.remove(i);
+        try {
+            if (callback == null) {
+                if (mState == CONNECT_STATE_CONNECTED) {
+                    mServiceBinder.removeSubscription(parentId, null, mServiceCallbacks);
+                }
+            } else {
+                final List<SubscriptionCallback> callbacks = sub.getCallbacks();
+                final List<Bundle> optionsList = sub.getOptionsList();
+                for (int i = callbacks.size() - 1; i >= 0; --i) {
+                    if (callbacks.get(i) == callback) {
+                        if (mState == CONNECT_STATE_CONNECTED) {
+                            mServiceBinder.removeSubscription(
+                                    parentId, callback.mToken, mServiceCallbacks);
                         }
+                        callbacks.remove(i);
+                        optionsList.remove(i);
                     }
                 }
-            } catch (RemoteException ex) {
-                // Process is crashing. We will disconnect, and upon reconnect we will
-                // automatically reregister. So nothing to do here.
-                Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId);
             }
+        } catch (RemoteException ex) {
+            // Process is crashing. We will disconnect, and upon reconnect we will
+            // automatically reregister. So nothing to do here.
+            Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId);
         }
 
-        if (sub != null && (sub.isEmpty() || callback == null)) {
+        if (sub.isEmpty() || callback == null) {
             mSubscriptions.remove(parentId);
         }
     }
@@ -579,17 +577,12 @@
                 for (Entry<String, Subscription> subscriptionEntry : mSubscriptions.entrySet()) {
                     String id = subscriptionEntry.getKey();
                     Subscription sub = subscriptionEntry.getValue();
-                    for (Bundle options : sub.getOptionsList()) {
+                    List<SubscriptionCallback> callbackList = sub.getCallbacks();
+                    List<Bundle> optionsList = sub.getOptionsList();
+                    for (int i = 0; i < callbackList.size(); ++i) {
                         try {
-                            // NOTE: Do not call addSubscriptionWithOptions when options are null.
-                            // Otherwise, it will break the action of support library which expects
-                            // addSubscription will be called when options are null.
-                            if (options == null) {
-                                mServiceBinder.addSubscription(id, mServiceCallbacks);
-                            } else {
-                                mServiceBinder.addSubscriptionWithOptions(
-                                        id, options, mServiceCallbacks);
-                            }
+                            mServiceBinder.addSubscription(id, callbackList.get(i).mToken,
+                                    optionsList.get(i), mServiceCallbacks);
                         } catch (RemoteException ex) {
                             // Process is crashing. We will disconnect, and upon reconnect we will
                             // automatically reregister. So nothing to do here.
@@ -859,6 +852,12 @@
      * Callbacks for subscription related events.
      */
     public static abstract class SubscriptionCallback {
+        Binder mToken;
+
+        public SubscriptionCallback() {
+            mToken = new Binder();
+        }
+
         /**
          * Called when the list of children is loaded or updated.
          *
@@ -1071,12 +1070,7 @@
         }
 
         @Override
-        public void onLoadChildren(String parentId, ParceledListSlice list) {
-            onLoadChildrenWithOptions(parentId, list, null);
-        }
-
-        @Override
-        public void onLoadChildrenWithOptions(String parentId, ParceledListSlice list,
+        public void onLoadChildren(String parentId, ParceledListSlice list,
                 final Bundle options) {
             MediaBrowser mediaBrowser = mMediaBrowser.get();
             if (mediaBrowser != null) {
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index e134635..21211d7 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -839,9 +839,11 @@
         public abstract boolean onTune(Uri channelUri);
 
         /**
-         * Calls {@link #onTune(Uri)}. Override this method in order to handle domain-specific
+         * Tunes to a given channel. Override this method in order to handle domain-specific
          * features that are only known between certain TV inputs and their clients.
          *
+         * <p>The default implementation calls {@link #onTune(Uri)}.
+         *
          * @param channelUri The URI of the channel.
          * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
          *            name, i.e. prefixed with a package name you own, so that different developers
@@ -1693,11 +1695,12 @@
         public abstract void onTune(Uri channelUri);
 
         /**
-         * Calls {@link #onTune(Uri)}. Override this method in order to handle domain-specific
-         * features that are only known between certain TV inputs and their clients.
+         * Called when the application requests to tune to a given channel for TV program recording.
+         * Override this method in order to handle domain-specific features that are only known
+         * between certain TV inputs and their clients.
          *
          * <p>The application may call this method before starting or after stopping recording, but
-         * not during recording.
+         * not during recording. The default implementation calls {@link #onTune(Uri)}.
          *
          * <p>The session must call {@link #notifyTuned(Uri)} if the tune request was fulfilled, or
          * {@link #notifyError(int)} otherwise.
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
index 6ca5ac5..eef5a7c 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -14,19 +14,11 @@
  * @hide
  */
 oneway interface IMediaBrowserService {
-
-    // Warning: DO NOT CHANGE the methods signature and order of methods.
-    // A change of the order or the method signatures could break the support library.
-
     void connect(String pkg, in Bundle rootHints, IMediaBrowserServiceCallbacks callbacks);
     void disconnect(IMediaBrowserServiceCallbacks callbacks);
 
-    void addSubscription(String uri, IMediaBrowserServiceCallbacks callbacks);
-    void removeSubscription(String uri, IMediaBrowserServiceCallbacks callbacks);
+    void addSubscription(String uri, in IBinder token, in Bundle options,
+            IMediaBrowserServiceCallbacks callbacks);
+    void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks);
     void getMediaItem(String uri, in ResultReceiver cb);
-
-    void addSubscriptionWithOptions(String uri, in Bundle options,
-            IMediaBrowserServiceCallbacks callbacks);
-    void removeSubscriptionWithOptions(String uri, in Bundle options,
-            IMediaBrowserServiceCallbacks callbacks);
 }
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index e6b0e8c..dadb025 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -13,10 +13,6 @@
  * @hide
  */
 oneway interface IMediaBrowserServiceCallbacks {
-
-    // Warning: DO NOT CHANGE the methods signature and order of methods.
-    // A change of the order or the method signatures could break the support library.
-
     /**
      * Invoked when the connected has been established.
      * @param root The root media id for browsing.
@@ -26,6 +22,5 @@
      */
     void onConnect(String root, in MediaSession.Token session, in Bundle extras);
     void onConnectFailed();
-    void onLoadChildren(String mediaId, in ParceledListSlice list);
-    void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list, in Bundle options);
+    void onLoadChildren(String mediaId, in ParceledListSlice list, in Bundle options);
 }
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 6954045..ddc0e88 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -39,6 +39,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -108,7 +109,7 @@
         Bundle rootHints;
         IMediaBrowserServiceCallbacks callbacks;
         BrowserRoot root;
-        HashMap<String, List<Bundle>> subscriptions = new HashMap<>();
+        HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
     }
 
     /**
@@ -247,13 +248,7 @@
         }
 
         @Override
-        public void addSubscription(final String id,
-                final IMediaBrowserServiceCallbacks callbacks) {
-            addSubscriptionWithOptions(id, null, callbacks);
-        }
-
-        @Override
-        public void addSubscriptionWithOptions(final String id, final Bundle options,
+        public void addSubscription(final String id, final IBinder token, final Bundle options,
                 final IMediaBrowserServiceCallbacks callbacks) {
             mHandler.post(new Runnable() {
                     @Override
@@ -268,19 +263,13 @@
                             return;
                         }
 
-                        MediaBrowserService.this.addSubscription(id, connection, options);
+                        MediaBrowserService.this.addSubscription(id, connection, token, options);
                     }
                 });
         }
 
         @Override
-        public void removeSubscription(final String id,
-                final IMediaBrowserServiceCallbacks callbacks) {
-            removeSubscriptionWithOptions(id, null, callbacks);
-        }
-
-        @Override
-        public void removeSubscriptionWithOptions(final String id, final Bundle options,
+        public void removeSubscription(final String id, final IBinder token,
                 final IMediaBrowserServiceCallbacks callbacks) {
             mHandler.post(new Runnable() {
                 @Override
@@ -293,7 +282,7 @@
                                 + id);
                         return;
                     }
-                    if (!MediaBrowserService.this.removeSubscription(id, connection, options)) {
+                    if (!MediaBrowserService.this.removeSubscription(id, connection, token)) {
                         Log.w(TAG, "removeSubscription called for " + id
                                 + " which is not subscribed");
                     }
@@ -519,11 +508,12 @@
             public void run() {
                 for (IBinder binder : mConnections.keySet()) {
                     ConnectionRecord connection = mConnections.get(binder);
-                    List<Bundle> optionsList = connection.subscriptions.get(parentId);
-                    if (optionsList != null) {
-                        for (Bundle bundle : optionsList) {
-                            if (MediaBrowserUtils.hasDuplicatedItems(options, bundle)) {
-                                performLoadChildren(parentId, connection, bundle);
+                    List<Pair<IBinder, Bundle>> callbackList =
+                            connection.subscriptions.get(parentId);
+                    if (callbackList != null) {
+                        for (Pair<IBinder, Bundle> callback : callbackList) {
+                            if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) {
+                                performLoadChildren(parentId, connection, callback.second);
                             }
                         }
                     }
@@ -553,19 +543,21 @@
     /**
      * Save the subscription and if it is a new subscription send the results.
      */
-    private void addSubscription(String id, ConnectionRecord connection, Bundle options) {
+    private void addSubscription(String id, ConnectionRecord connection, IBinder token,
+            Bundle options) {
         // Save the subscription
-        List<Bundle> optionsList = connection.subscriptions.get(id);
-        if (optionsList == null) {
-            optionsList = new ArrayList<>();
+        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
+        if (callbackList == null) {
+            callbackList = new ArrayList<>();
         }
-        for (Bundle bundle : optionsList) {
-            if (MediaBrowserUtils.areSameOptions(options, bundle)) {
+        for (Pair<IBinder, Bundle> callback : callbackList) {
+            if (token == callback.first
+                    && MediaBrowserUtils.areSameOptions(options, callback.second)) {
                 return;
             }
         }
-        optionsList.add(options);
-        connection.subscriptions.put(id, optionsList);
+        callbackList.add(new Pair<>(token, options));
+        connection.subscriptions.put(id, callbackList);
         // send the results
         performLoadChildren(id, connection, options);
     }
@@ -573,21 +565,20 @@
     /**
      * Remove the subscription.
      */
-    private boolean removeSubscription(String id, ConnectionRecord connection, Bundle options) {
-        if (options == null) {
+    private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) {
+        if (token == null) {
             return connection.subscriptions.remove(id) != null;
         }
         boolean removed = false;
-        List<Bundle> optionsList = connection.subscriptions.get(id);
-        if (optionsList != null) {
-            for (Bundle bundle : optionsList) {
-                if (MediaBrowserUtils.areSameOptions(options, bundle)) {
+        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
+        if (callbackList != null) {
+            for (Pair<IBinder, Bundle> callback : callbackList) {
+                if (token == callback.first) {
                     removed = true;
-                    optionsList.remove(bundle);
-                    break;
+                    callbackList.remove(callback);
                 }
             }
-            if (optionsList.size() == 0) {
+            if (callbackList.size() == 0) {
                 connection.subscriptions.remove(id);
             }
         }
@@ -619,14 +610,7 @@
                 final ParceledListSlice<MediaBrowser.MediaItem> pls =
                         filteredList == null ? null : new ParceledListSlice<>(filteredList);
                 try {
-                    // NOTE: Do not call onLoadChildrenWithOptions when options are null. Otherwise,
-                    // it will break the action of support library which expects onLoadChildren will
-                    // be called when options are null.
-                    if (options == null) {
-                        connection.callbacks.onLoadChildren(parentId, pls);
-                    } else {
-                        connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options);
-                    }
+                    connection.callbacks.onLoadChildren(parentId, pls, options);
                 } catch (RemoteException ex) {
                     // The other side is in the process of crashing.
                     Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
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/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/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/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 b6fc66b..82806fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -4323,7 +4323,7 @@
         try {
             IActivityManager activityManager = ActivityManagerNative.getDefault();
             if (activityManager.isInLockTaskMode()) {
-                activityManager.stopLockTaskModeOnCurrent();
+                activityManager.stopSystemLockTaskMode();
 
                 // When exiting refresh disabled flags.
                 mNavigationBarView.setDisabledFlags(mDisabled1, true);
@@ -4346,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..7b3fd66 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2152,6 +2152,23 @@
     // 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;
+
+    // Action: user enable / disabled data saver using Settings. Arguments:
+    // 0: Data Saver mode is disabled.
+    // 1: Data Saver mode is enabled.
+    ACTION_DATA_SAVER_MODE = 394;
+
+    // User whitelisted an app for Data Saver mode; action pass package name of app.
+    ACTION_DATA_SAVER_WHITELIST = 395;
+
+    // User blacklisted an app for Data Saver mode; action pass package name of app.
+    ACTION_DATA_SAVER_BLACKLIST = 396;
+
     // 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..40430b4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1471,6 +1471,7 @@
     static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 66;
     static final int NOTIFY_FORCED_RESIZABLE_MSG = 67;
     static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 68;
+    static final int VR_MODE_APPLY_IF_NEEDED_MSG = 69;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2241,6 +2242,17 @@
                 }
                 vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
             } break;
+            case VR_MODE_APPLY_IF_NEEDED_MSG: {
+                final ActivityRecord r = (ActivityRecord) msg.obj;
+                final boolean needsVrMode = r != null && r.requestedVrComponent != null;
+                if (needsVrMode) {
+                    VrManagerInternal vrService =
+                            LocalServices.getService(VrManagerInternal.class);
+                    boolean enable = msg.arg1 == 1;
+                    vrService.setVrMode(enable, r.requestedVrComponent, r.userId,
+                            r.info.getComponentName());
+                }
+            } break;
             }
         }
     };
@@ -3021,6 +3033,11 @@
                 mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
     }
 
+    private void applyVrModeIfNeededLocked(ActivityRecord r, boolean enable) {
+        mHandler.sendMessage(
+                mHandler.obtainMessage(VR_MODE_APPLY_IF_NEEDED_MSG, enable ? 1 : 0, 0, r));
+    }
+
     final void showAskCompatModeDialogLocked(ActivityRecord r) {
         Message msg = Message.obtain();
         msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -6537,6 +6554,7 @@
 
                     // Some stack visibility might change (e.g. docked stack)
                     mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                    applyVrModeIfNeededLocked(mFocusedActivity, true);
                 }
             }
         } finally {
@@ -9873,7 +9891,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 +9899,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 +9945,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 +9995,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();
@@ -20926,6 +20943,7 @@
                 SleepTokenImpl token = new SleepTokenImpl(tag);
                 mSleepTokens.add(token);
                 updateSleepIfNeededLocked();
+                applyVrModeIfNeededLocked(mFocusedActivity, false);
                 return token;
             }
         }
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/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index ab3a0b3..cd465d6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2270,7 +2270,7 @@
         final ActivityRecord r = task.getTopActivity();
         final ActivityStack prevStack = task.stack;
         final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);
-        final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
+        final boolean wasResumed = prevStack.mResumedActivity == r;
         // In some cases the focused stack isn't the front stack. E.g. pinned stack.
         // Whenever we are moving the top activity from the front stack we want to make sure to move
         // the stack to the front.
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..ac5c4ae 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) {
@@ -291,14 +300,14 @@
          */
         public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
             enforceDumpPermission();
-            long ref = reference.value;
+            long ref = reference.getValue();
             if (VDBG) Log.v(TAG, "getEvents(" + ref + ")");
 
             ConnectivityMetricsEvent[] result;
             synchronized (mEvents) {
                 if (ref > mLastEventReference) {
                     Log.e(TAG, "Invalid reference");
-                    reference.value = mLastEventReference;
+                    reference.setValue(mLastEventReference);
                     return null;
                 }
                 if (ref < mLastEventReference - mEvents.size()) {
@@ -320,7 +329,7 @@
                 }
             }
 
-            reference.value = mLastEventReference;
+            reference.setValue(mLastEventReference);
 
             return result;
         }
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/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index fed7e4b..e07d72f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1189,7 +1189,9 @@
         @Override
         public void binderDied() {
             synchronized (mLock) {
-                mRecordListenerRecord = null;
+                if (mRecordListenerRecord == this) {
+                    mRecordListenerRecord = null;
+                }
             }
         }
     }
@@ -1813,7 +1815,9 @@
         @Override
         public void binderDied() {
             synchronized (mLock) {
-                mInputChangeListenerRecord = null;
+                if (mInputChangeListenerRecord == this) {
+                    mInputChangeListenerRecord = null;
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c248608..3fb786b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -260,6 +260,8 @@
     private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7;
     private static final int MSG_SCREEN_ON_CHANGED = 8;
     private static final int MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED = 9;
+    private static final int MSG_UPDATE_INTERFACE_QUOTA = 10;
+    private static final int MSG_REMOVE_INTERFACE_QUOTA = 11;
 
     private final Context mContext;
     private final IActivityManager mActivityManager;
@@ -1275,8 +1277,10 @@
                 }
 
                 for (String iface : ifaces) {
-                    removeInterfaceQuota(iface);
-                    setInterfaceQuota(iface, quotaBytes);
+                    // long quotaBytes split up into two ints to fit in message
+                    mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA,
+                            (int) (quotaBytes >> 32), (int) (quotaBytes & 0xFFFFFFFF), iface)
+                            .sendToTarget();
                     newMeteredIfaces.add(iface);
                 }
             }
@@ -1292,8 +1296,10 @@
 
         for (int i = connIfaces.size()-1; i >= 0; i--) {
             String iface = connIfaces.valueAt(i);
-            removeInterfaceQuota(iface);
-            setInterfaceQuota(iface, Long.MAX_VALUE);
+            // long quotaBytes split up into two ints to fit in message
+            mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA,
+                    (int) (Long.MAX_VALUE >> 32), (int) (Long.MAX_VALUE & 0xFFFFFFFF), iface)
+                    .sendToTarget();
             newMeteredIfaces.add(iface);
         }
 
@@ -1303,7 +1309,8 @@
         for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
             final String iface = mMeteredIfaces.valueAt(i);
             if (!newMeteredIfaces.contains(iface)) {
-                removeInterfaceQuota(iface);
+                mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTA, iface)
+                        .sendToTarget();
             }
         }
         mMeteredIfaces = newMeteredIfaces;
@@ -2950,6 +2957,17 @@
                     updateScreenOn();
                     return true;
                 }
+                case MSG_UPDATE_INTERFACE_QUOTA: {
+                    removeInterfaceQuota((String) msg.obj);
+                    // int params need to be stitched back into a long
+                    setInterfaceQuota((String) msg.obj,
+                            ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL));
+                    return true;
+                }
+                case MSG_REMOVE_INTERFACE_QUOTA: {
+                    removeInterfaceQuota((String) msg.obj);
+                    return true;
+                }
                 default: {
                     return false;
                 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1fb260d..dc81c65 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2315,24 +2315,29 @@
             // For security and version matching reason, only consider
             // overlay packages if they reside in VENDOR_OVERLAY_DIR.
             File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
-            scanDirTracedLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
-                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
+            scanDirTracedLI(vendorOverlayDir, mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM
+                    | PackageParser.PARSE_IS_SYSTEM_DIR
+                    | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
 
             // Find base frameworks (resource packages without code).
-            scanDirTracedLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
+            scanDirTracedLI(frameworkDir, mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR
                     | PackageParser.PARSE_IS_PRIVILEGED,
                     scanFlags | SCAN_NO_DEX, 0);
 
             // Collected privileged system packages.
             final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
-            scanDirTracedLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
+            scanDirTracedLI(privilegedAppDir, mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR
                     | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
 
             // Collect ordinary system packages.
             final File systemAppDir = new File(Environment.getRootDirectory(), "app");
-            scanDirTracedLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
+            scanDirTracedLI(systemAppDir, mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
 
             // Collect all vendor packages.
@@ -2342,12 +2347,14 @@
             } catch (IOException e) {
                 // failed to look up canonical path, continue with original one
             }
-            scanDirTracedLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
+            scanDirTracedLI(vendorAppDir, mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
 
             // Collect all OEM packages.
             final File oemAppDir = new File(Environment.getOemDirectory(), "app");
-            scanDirTracedLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
+            scanDirTracedLI(oemAppDir, mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
 
             // Prune any system packages that no longer exist.
@@ -2428,10 +2435,12 @@
                         SystemClock.uptimeMillis());
                 scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
 
-                scanDirTracedLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
+                scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
+                        | PackageParser.PARSE_FORWARD_LOCK,
                         scanFlags | SCAN_REQUIRE_KNOWN, 0);
 
-                scanDirLI(mEphemeralInstallDir, PackageParser.PARSE_IS_EPHEMERAL,
+                scanDirLI(mEphemeralInstallDir, mDefParseFlags
+                        | PackageParser.PARSE_IS_EPHEMERAL,
                         scanFlags | SCAN_REQUIRE_KNOWN, 0);
 
                 /**
@@ -2476,7 +2485,7 @@
                         logCriticalInfo(Log.WARN, "Expected better " + packageName
                                 + " but never showed up; reverting to system");
 
-                        final int reparseFlags;
+                        int reparseFlags = mDefParseFlags;
                         if (FileUtils.contains(privilegedAppDir, scanFile)) {
                             reparseFlags = PackageParser.PARSE_IS_SYSTEM
                                     | PackageParser.PARSE_IS_SYSTEM_DIR
@@ -6501,7 +6510,7 @@
         return true;
     }
 
-    private void scanDirTracedLI(File dir, int parseFlags, int scanFlags, long currentTime) {
+    private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir");
         try {
             scanDirLI(dir, parseFlags, scanFlags, currentTime);
@@ -6510,7 +6519,7 @@
         }
     }
 
-    private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
+    private void scanDirLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
         final File[] files = dir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
             Log.d(TAG, "No files in app dir " + dir);
@@ -6576,7 +6585,7 @@
     }
 
     private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, File srcFile,
-            int parseFlags) throws PackageManagerException {
+            final int policyFlags) throws PackageManagerException {
         if (ps != null
                 && ps.codePath.equals(srcFile)
                 && ps.timeStamp == srcFile.lastModified()
@@ -6605,7 +6614,7 @@
         }
 
         try {
-            PackageParser.collectCertificates(pkg, parseFlags);
+            PackageParser.collectCertificates(pkg, policyFlags);
         } catch (PackageParserException e) {
             throw PackageManagerException.from(e);
         }
@@ -6615,8 +6624,8 @@
      *  Traces a package scan.
      *  @see #scanPackageLI(File, int, int, long, UserHandle)
      */
-    private PackageParser.Package scanPackageTracedLI(File scanFile, int parseFlags, int scanFlags,
-            long currentTime, UserHandle user) throws PackageManagerException {
+    private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
+            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
         try {
             return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
@@ -6632,7 +6641,6 @@
     private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
             long currentTime, UserHandle user) throws PackageManagerException {
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
-        parseFlags |= mDefParseFlags;
         PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
         pp.setOnlyCoreApps(mOnlyCore);
@@ -6642,11 +6650,14 @@
             parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
         }
 
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
         final PackageParser.Package pkg;
         try {
             pkg = pp.parsePackage(scanFile, parseFlags);
         } catch (PackageParserException e) {
             throw PackageManagerException.from(e);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
 
         return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
@@ -6657,7 +6668,7 @@
      *  @throws PackageManagerException on a parse error.
      */
     private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
-            int parseFlags, int scanFlags, long currentTime, UserHandle user)
+            final int policyFlags, int scanFlags, long currentTime, UserHandle user)
             throws PackageManagerException {
         // If the package has children and this is the first dive in the function
         // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all
@@ -6673,20 +6684,20 @@
         }
 
         // Scan the parent
-        PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, parseFlags,
+        PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, policyFlags,
                 scanFlags, currentTime, user);
 
         // Scan the children
         final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
         for (int i = 0; i < childCount; i++) {
             PackageParser.Package childPackage = pkg.childPackages.get(i);
-            scanPackageInternalLI(childPackage, scanFile, parseFlags, scanFlags,
+            scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags,
                     currentTime, user);
         }
 
 
         if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
-            return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
+            return scanPackageLI(pkg, scanFile, policyFlags, scanFlags, currentTime, user);
         }
 
         return scannedPkg;
@@ -6697,7 +6708,7 @@
      *  @throws PackageManagerException on a parse error.
      */
     private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,
-            int parseFlags, int scanFlags, long currentTime, UserHandle user)
+            int policyFlags, int scanFlags, long currentTime, UserHandle user)
             throws PackageManagerException {
         PackageSetting ps = null;
         PackageSetting updatedPkg;
@@ -6724,7 +6735,7 @@
             // may need to remove disabled child packages on the system partition
             // or may need to not add child packages if the parent apk is updated
             // on the data partition and no longer defines this child package.
-            if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
+            if ((policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
                 // If this is a parent package for an updated system app and this system
                 // app got an OTA update which no longer defines some of the child packages
                 // we have to prune them from the disabled system packages.
@@ -6754,7 +6765,7 @@
 
         boolean updatedPkgBetter = false;
         // First check if this is a system package that may involve an update
-        if (updatedPkg != null && (parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
+        if (updatedPkg != null && (policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
             // If new package is not located in "/system/priv-app" (e.g. due to an OTA),
             // it needs to drop FLAG_PRIVILEGED.
             if (locationIsPrivileged(scanFile)) {
@@ -6836,17 +6847,17 @@
         if (updatedPkg != null) {
             // An updated system app will not have the PARSE_IS_SYSTEM flag set
             // initially
-            parseFlags |= PackageParser.PARSE_IS_SYSTEM;
+            policyFlags |= PackageParser.PARSE_IS_SYSTEM;
 
             // An updated privileged app will not have the PARSE_IS_PRIVILEGED
             // flag set initially
             if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
-                parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
+                policyFlags |= PackageParser.PARSE_IS_PRIVILEGED;
             }
         }
 
         // Verify certificates against what was last scanned
-        collectCertificatesLI(ps, pkg, scanFile, parseFlags);
+        collectCertificatesLI(ps, pkg, scanFile, policyFlags);
 
         /*
          * A new system app appeared, but we already had a non-system one of the
@@ -6854,7 +6865,7 @@
          */
         boolean shouldHideSystemApp = false;
         if (updatedPkg == null && ps != null
-                && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {
+                && (policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {
             /*
              * Check to make sure the signatures match first. If they don't,
              * wipe the installed application and its data.
@@ -6902,16 +6913,16 @@
         // are kept in different files. (except for app in either system or
         // vendor path).
         // TODO grab this value from PackageSettings
-        if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+        if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
             if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
-                parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
+                policyFlags |= PackageParser.PARSE_FORWARD_LOCK;
             }
         }
 
         // TODO: extend to support forward-locked splits
         String resourcePath = null;
         String baseResourcePath = null;
-        if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
+        if ((policyFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
             if (ps != null && ps.resourcePathString != null) {
                 resourcePath = ps.resourcePathString;
                 baseResourcePath = ps.resourcePathString;
@@ -6934,7 +6945,7 @@
         pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
 
         // Note that we invoke the following method only if we are about to unpack an application
-        PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
+        PackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags
                 | SCAN_UPDATE_SIGNATURE, currentTime, user);
 
         /*
@@ -7576,8 +7587,9 @@
         return cpuAbiOverride;
     }
 
-    private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg, int parseFlags,
-            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
+    private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg,
+            final int policyFlags, int scanFlags, long currentTime, UserHandle user)
+                    throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
         // If the package has children and this is the first dive in the function
         // we recursively scan the package with the SCAN_CHECK_ONLY flag set to see
@@ -7595,12 +7607,12 @@
         final PackageParser.Package scannedPkg;
         try {
             // Scan the parent
-            scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags, currentTime, user);
+            scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags, currentTime, user);
             // Scan the children
             final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
             for (int i = 0; i < childCount; i++) {
                 PackageParser.Package childPkg = pkg.childPackages.get(i);
-                scanPackageLI(childPkg, parseFlags,
+                scanPackageLI(childPkg, policyFlags,
                         scanFlags, currentTime, user);
             }
         } finally {
@@ -7608,17 +7620,17 @@
         }
 
         if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
-            return scanPackageTracedLI(pkg, parseFlags, scanFlags, currentTime, user);
+            return scanPackageTracedLI(pkg, policyFlags, scanFlags, currentTime, user);
         }
 
         return scannedPkg;
     }
 
-    private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
+    private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,
             int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
         boolean success = false;
         try {
-            final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
+            final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
                     currentTime, user);
             success = true;
             return res;
@@ -7632,8 +7644,8 @@
         }
     }
 
-    private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
-            int scanFlags, long currentTime, UserHandle user)
+    private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
+            final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
             throws PackageManagerException {
         final File scanFile = new File(pkg.codePath);
         if (pkg.applicationInfo.getCodePath() == null ||
@@ -7643,14 +7655,36 @@
                     "Code and resource paths haven't been set correctly");
         }
 
-        if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
+        // Apply policy
+        if ((policyFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+            if (pkg.applicationInfo.isDirectBootAware()) {
+                // we're direct boot aware; set for all components
+                for (PackageParser.Service s : pkg.services) {
+                    s.info.encryptionAware = s.info.directBootAware = true;
+                }
+                for (PackageParser.Provider p : pkg.providers) {
+                    p.info.encryptionAware = p.info.directBootAware = true;
+                }
+                for (PackageParser.Activity a : pkg.activities) {
+                    a.info.encryptionAware = a.info.directBootAware = true;
+                }
+                for (PackageParser.Activity r : pkg.receivers) {
+                    r.info.encryptionAware = r.info.directBootAware = true;
+                }
+            }
         } else {
             // Only allow system apps to be flagged as core apps.
             pkg.coreApp = false;
+            // clear flags not applicable to regular apps
+            pkg.applicationInfo.privateFlags &=
+                    ~ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
+            pkg.applicationInfo.privateFlags &=
+                    ~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
         }
+        pkg.mTrustedOverlay = (policyFlags&PackageParser.PARSE_TRUSTED_OVERLAY) != 0;
 
-        if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
+        if ((policyFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
             pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
         }
 
@@ -7699,7 +7733,7 @@
         }
 
         if (DEBUG_PACKAGE_SCANNING) {
-            if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+            if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)
                 Log.d(TAG, "Scanning package " + pkg.packageName);
         }
 
@@ -7774,7 +7808,7 @@
                             + " for shared user failed");
                 }
                 if (DEBUG_PACKAGE_SCANNING) {
-                    if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+                    if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)
                         Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId
                                 + "): packages=" + suid.packages);
                 }
@@ -7820,6 +7854,7 @@
                                     origPackage = null;
                                     continue;
                                 }
+                                // TODO: Add case when shared user id is added [b/28144775]
                             } else {
                                 if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
                                         + pkg.packageName + " to old name " + origPackage.name);
@@ -7886,7 +7921,7 @@
                 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
 
-            if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+            if ((policyFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                 // Check all shared libraries and map to their actual file path.
                 // We only do this here for apps not on a system dir, because those
                 // are the only ones that can fail an install due to this.  We
@@ -7907,7 +7942,7 @@
                     // over the latest parsed certs.
                     pkgSetting.signatures.mSignatures = pkg.mSignatures;
                 } else {
-                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                    if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                         throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                                 "Package " + pkg.packageName + " upgrade keys do not match the "
                                 + "previously installed version");
@@ -7925,7 +7960,7 @@
                     // over the latest parsed certs.
                     pkgSetting.signatures.mSignatures = pkg.mSignatures;
                 } catch (PackageManagerException e) {
-                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                    if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                         throw e;
                     }
                     // The signature has changed, but this package is in the system
@@ -8114,7 +8149,7 @@
 
         // Only privileged apps and updated privileged apps can add child packages.
         if (pkg.childPackages != null && !pkg.childPackages.isEmpty()) {
-            if ((parseFlags & PARSE_IS_PRIVILEGED) == 0) {
+            if ((policyFlags & PARSE_IS_PRIVILEGED) == 0) {
                 throw new PackageManagerException("Only privileged apps and updated "
                         + "privileged apps can add child packages. Ignoring package "
                         + pkg.packageName);
@@ -8240,7 +8275,7 @@
             } else if (pkgSetting.firstInstallTime == 0) {
                 // We need *something*.  Take time time stamp of the file.
                 pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
-            } else if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+            } else if ((policyFlags&PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
                 if (scanFileTime != pkgSetting.timeStamp) {
                     // A package on the system image has changed; consider this
                     // to be an update.
@@ -8283,7 +8318,7 @@
                                 p.info.authority = p.info.authority + ";" + names[j];
                             }
                             if (DEBUG_PACKAGE_SCANNING) {
-                                if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+                                if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)
                                     Log.d(TAG, "Registered content provider: " + names[j]
                                             + ", className = " + p.info.name + ", isSyncable = "
                                             + p.info.isSyncable);
@@ -8298,7 +8333,7 @@
                         }
                     }
                 }
-                if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+                if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
                     if (r == null) {
                         r = new StringBuilder(256);
                     } else {
@@ -8318,7 +8353,7 @@
                 s.info.processName = fixProcessName(pkg.applicationInfo.processName,
                         s.info.processName, pkg.applicationInfo.uid);
                 mServices.addService(s);
-                if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+                if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
                     if (r == null) {
                         r = new StringBuilder(256);
                     } else {
@@ -8338,7 +8373,7 @@
                 a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                         a.info.processName, pkg.applicationInfo.uid);
                 mReceivers.addActivity(a, "receiver");
-                if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+                if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
                     if (r == null) {
                         r = new StringBuilder(256);
                     } else {
@@ -8358,7 +8393,7 @@
                 a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                         a.info.processName, pkg.applicationInfo.uid);
                 mActivities.addActivity(a, "activity");
-                if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+                if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
                     if (r == null) {
                         r = new StringBuilder(256);
                     } else {
@@ -8378,7 +8413,7 @@
                 PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);
                 if (cur == null) {
                     mPermissionGroups.put(pg.info.name, pg);
-                    if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+                    if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
                         if (r == null) {
                             r = new StringBuilder(256);
                         } else {
@@ -8390,7 +8425,7 @@
                     Slog.w(TAG, "Permission group " + pg.info.name + " from package "
                             + pg.info.packageName + " ignored: original from "
                             + cur.info.packageName);
-                    if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+                    if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
                         if (r == null) {
                             r = new StringBuilder(256);
                         } else {
@@ -8469,7 +8504,7 @@
                             bp.uid = pkg.applicationInfo.uid;
                             bp.sourcePackage = p.info.packageName;
                             p.info.flags |= PermissionInfo.FLAG_INSTALLED;
-                            if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+                            if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
                                 if (r == null) {
                                     r = new StringBuilder(256);
                                 } else {
@@ -8488,7 +8523,7 @@
                                 + p.info.packageName + " ignored: original from "
                                 + bp.sourcePackage);
                     }
-                } else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+                } else if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
                     if (r == null) {
                         r = new StringBuilder(256);
                     } else {
@@ -8523,7 +8558,7 @@
                 // need other information about the application, like the ABI and what not ?
                 a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
                 mInstrumentation.put(a.getComponentName(), a);
-                if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
+                if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
                     if (r == null) {
                         r = new StringBuilder(256);
                     } else {
@@ -13538,8 +13573,8 @@
     /*
      * Install a non-existing package.
      */
-    private void installNewPackageLIF(PackageParser.Package pkg, int parseFlags, int scanFlags,
-            UserHandle user, String installerPackageName, String volumeUuid,
+    private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
+            int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
             PackageInstalledInfo res) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");
 
@@ -13568,7 +13603,7 @@
         }
 
         try {
-            PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
+            PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
                     System.currentTimeMillis(), user);
 
             updateSettingsLI(newPackage, installerPackageName, null, res, user);
@@ -13625,9 +13660,9 @@
         return false;
     }
 
-    private void replacePackageLIF(PackageParser.Package pkg, int parseFlags, int scanFlags,
+    private void replacePackageLIF(PackageParser.Package pkg, final int policyFlags, int scanFlags,
             UserHandle user, String installerPackageName, PackageInstalledInfo res) {
-        final boolean isEphemeral = (parseFlags & PackageParser.PARSE_IS_EPHEMERAL) != 0;
+        final boolean isEphemeral = (policyFlags & PackageParser.PARSE_IS_EPHEMERAL) != 0;
 
         final PackageParser.Package oldPackage;
         final String pkgName = pkg.packageName;
@@ -13715,10 +13750,18 @@
 
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
-            replaceSystemPackageLIF(oldPackage, pkg, parseFlags, scanFlags,
+            // Set the system/privileged flags as needed
+            final boolean privileged =
+                    (oldPackage.applicationInfo.privateFlags
+                            & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+            final int systemPolicyFlags = policyFlags
+                    | PackageParser.PARSE_IS_SYSTEM
+                    | (privileged ? PackageParser.PARSE_IS_PRIVILEGED : 0);
+
+            replaceSystemPackageLIF(oldPackage, pkg, systemPolicyFlags, scanFlags,
                     user, allUsers, installerPackageName, res);
         } else {
-            replaceNonSystemPackageLIF(oldPackage, pkg, parseFlags, scanFlags,
+            replaceNonSystemPackageLIF(oldPackage, pkg, policyFlags, scanFlags,
                     user, allUsers, installerPackageName, res);
         }
     }
@@ -13733,7 +13776,7 @@
     }
 
     private void replaceNonSystemPackageLIF(PackageParser.Package deletedPackage,
-            PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
+            PackageParser.Package pkg, final int policyFlags, int scanFlags, UserHandle user,
             int[] allUsers, String installerPackageName, PackageInstalledInfo res) {
         if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
                 + deletedPackage);
@@ -13775,7 +13818,7 @@
             clearAppProfilesLIF(pkg);
 
             try {
-                final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
+                final PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags,
                         scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
                 updateSettingsLI(newPackage, installerPackageName, allUsers, res, user);
 
@@ -13872,20 +13915,13 @@
     }
 
     private void replaceSystemPackageLIF(PackageParser.Package deletedPackage,
-            PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
+            PackageParser.Package pkg, final int policyFlags, int scanFlags, UserHandle user,
             int[] allUsers, String installerPackageName, PackageInstalledInfo res) {
         if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
                 + ", old=" + deletedPackage);
 
         final boolean disabledSystem;
 
-        // Set the system/privileged flags as needed
-        parseFlags |= PackageParser.PARSE_IS_SYSTEM;
-        if ((deletedPackage.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
-                != 0) {
-            parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
-        }
-
         // Remove existing system package
         removePackageLI(deletedPackage, true);
 
@@ -13914,7 +13950,7 @@
         PackageParser.Package newPackage = null;
         try {
             // Add the package to the internal data structures
-            newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
+            newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags, 0, user);
 
             // Set the update and install times
             PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;
@@ -13967,7 +14003,7 @@
             }
             // Add back the old system package
             try {
-                scanPackageTracedLI(deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
+                scanPackageTracedLI(deletedPackage, policyFlags, SCAN_UPDATE_SIGNATURE, 0, user);
             } catch (PackageManagerException e) {
                 Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
             }
@@ -15296,7 +15332,10 @@
 
         // Install the system package
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
-        int parseFlags = PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM;
+        int parseFlags = mDefParseFlags
+                | PackageParser.PARSE_MUST_BE_APK
+                | PackageParser.PARSE_IS_SYSTEM
+                | PackageParser.PARSE_IS_SYSTEM_DIR;
         if (locationIsPrivileged(disabledPs.codePath)) {
             parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
         }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index ac6510a..0ac5c1f 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -907,6 +907,16 @@
         }
     }
 
+    public void cleanupBitmapsForPackage(@UserIdInt int userId, String packageName) {
+        final File packagePath = new File(getUserBitmapFilePath(userId), packageName);
+        if (!packagePath.isDirectory()) {
+            return;
+        }
+        if (!(FileUtils.deleteContents(packagePath) && packagePath.delete())) {
+            Slog.w(TAG, "Unable to remove directory " + packagePath);
+        }
+    }
+
     @VisibleForTesting
     static class FileOutputStreamWithPath extends FileOutputStream {
         private final File mFile;
@@ -1572,7 +1582,7 @@
 
         // First, remove the package from the package list (if the package is a publisher).
         if (packageUserId == owningUserId) {
-            if (mUser.removePackage(packageName) != null) {
+            if (mUser.removePackage(this, packageName) != null) {
                 doNotify = true;
             }
         }
@@ -2084,11 +2094,11 @@
             pw.println(mIconPersistFormat);
             pw.print("    Icon quality: ");
             pw.println(mIconPersistQuality);
-            pw.print("    saveDelayMillis:");
+            pw.print("    saveDelayMillis: ");
             pw.println(mSaveDelayMillis);
-            pw.print("    resetInterval:");
+            pw.print("    resetInterval: ");
             pw.println(mResetInterval);
-            pw.print("    maxUpdatesPerInterval:");
+            pw.print("    maxUpdatesPerInterval: ");
             pw.println(mMaxUpdatesPerInterval);
             pw.print("    maxDynamicShortcuts:");
             pw.println(mMaxDynamicShortcuts);
@@ -2416,7 +2426,6 @@
         return mPackageManagerInternal;
     }
 
-    @VisibleForTesting
     File getUserBitmapFilePath(@UserIdInt int userId) {
         return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
     }
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 593f607..0b8c3a2 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
+import android.text.format.Formatter;
 import android.util.ArrayMap;
 import android.util.Slog;
 
@@ -29,6 +30,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.function.Consumer;
@@ -103,8 +105,12 @@
         return mPackages;
     }
 
-    public ShortcutPackage removePackage(@NonNull String packageName) {
-        return mPackages.remove(packageName);
+    public ShortcutPackage removePackage(@NonNull ShortcutService s, @NonNull String packageName) {
+        final ShortcutPackage removed = mPackages.remove(packageName);
+
+        s.cleanupBitmapsForPackage(mUserId, packageName);
+
+        return removed;
     }
 
     public ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchers() {
@@ -279,18 +285,51 @@
         pw.print(mUserId);
         pw.println();
 
+        prefix += prefix + "  ";
+
         pw.print(prefix);
-        pw.print("  ");
         pw.print("Default launcher: ");
         pw.print(mLauncherComponent);
         pw.println();
 
         for (int i = 0; i < mLaunchers.size(); i++) {
-            mLaunchers.valueAt(i).dump(s, pw, prefix + "  ");
+            mLaunchers.valueAt(i).dump(s, pw, prefix);
         }
 
         for (int i = 0; i < mPackages.size(); i++) {
-            mPackages.valueAt(i).dump(s, pw, prefix + "  ");
+            mPackages.valueAt(i).dump(s, pw, prefix);
         }
+
+        pw.println();
+        pw.print(prefix);
+        pw.println("Bitmap directories: ");
+        dumpDirectorySize(s, pw, prefix + "  ", s.getUserBitmapFilePath(mUserId));
+    }
+
+    private void dumpDirectorySize(@NonNull ShortcutService s, @NonNull PrintWriter pw,
+            @NonNull String prefix, File path) {
+        int numFiles = 0;
+        long size = 0;
+        final File[] children = path.listFiles();
+        if (children != null) {
+            for (File child : path.listFiles()) {
+                if (child.isFile()) {
+                    numFiles++;
+                    size += child.length();
+                } else if (child.isDirectory()) {
+                    dumpDirectorySize(s, pw, prefix + "  ", child);
+                }
+            }
+        }
+        pw.print(prefix);
+        pw.print("Path: ");
+        pw.print(path.getName());
+        pw.print("/ has ");
+        pw.print(numFiles);
+        pw.print(" files, size=");
+        pw.print(size);
+        pw.print(" (");
+        pw.print(Formatter.formatFileSize(s.mContext, size));
+        pw.println(")");
     }
 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 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/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/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 6bdcd42..0217c09 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -218,7 +218,7 @@
     }
 
     private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
-        if (win != null && layer > win.mLayer) {
+        if (win != null) {
             assignAnimLayer(win, layer);
             // Make sure we leave space inbetween normal windows for dims and such.
             layer += WINDOW_LAYER_MULTIPLIER;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 79abf25..a4238c1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2602,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();
 
@@ -2638,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 {
@@ -7445,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;
@@ -7461,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..1f76f31 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,13 @@
         } else {
             clearAnimation();
         }
-
+        if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+            mService.adjustForImeIfNeeded(mWin.mDisplayContent);
+            if (isEntrance) {
+                mWin.setDisplayLayoutNeeded();
+                mService.mWindowPlacerLocked.requestTraversal();
+            }
+        }
         return mAnimation != null;
     }
 
@@ -1834,6 +1857,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 +1906,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/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index ce02a79..d20d5fa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -15,9 +15,36 @@
  */
 package com.android.server.pm;
 
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamic;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamicOrPinned;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIcon;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIconFile;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIconResId;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIntents;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveTitle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllKeyFieldsOnly;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotHaveIntents;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotHaveTitle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotKeyFieldsOnly;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllPinned;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllUnique;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBitmapSize;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundleEmpty;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCallbackNotReceived;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCallbackReceived;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicAndPinned;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicOnly;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertShortcutIds;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.hashSet;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makeBundle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.pfdToBitmap;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resetAll;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyList;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -27,7 +54,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.*;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -57,13 +83,11 @@
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
-import android.os.BaseBundle;
 import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserHandle;
@@ -71,7 +95,6 @@
 import android.test.InstrumentationTestCase;
 import android.test.mock.MockContext;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -84,9 +107,6 @@
 import com.android.server.pm.ShortcutService.ConfigConstants;
 import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
 import com.android.server.pm.ShortcutUser.PackageWithUser;
-import com.android.server.testutis.TestUtils;
-
-import libcore.io.IoUtils;
 
 import org.junit.Assert;
 import org.mockito.ArgumentCaptor;
@@ -98,8 +118,6 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -4018,24 +4036,33 @@
         checkCanRestoreTo(false, spi2, 11, "x", "sig2x", "sig1", "y");
     }
 
+    private boolean bitmapDirectoryExists(String packageName, int userId) {
+        final File path = new File(mService.getUserBitmapFilePath(userId), packageName);
+        return path.isDirectory();
+    }
+
     public void testHandlePackageDelete() {
+        final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                getTestContext().getResources(), R.drawable.black_32x32));
         setCaller(CALLING_PACKAGE_1, USER_0);
-        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
+        assertTrue(mManager.addDynamicShortcuts(list(
+                makeShortcutWithIcon("s1", bmp32x32), makeShortcutWithIcon("s2", bmp32x32)
+        )));
 
         setCaller(CALLING_PACKAGE_2, USER_0);
-        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
 
         setCaller(CALLING_PACKAGE_3, USER_0);
-        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
 
         setCaller(CALLING_PACKAGE_1, USER_10);
-        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
 
         setCaller(CALLING_PACKAGE_2, USER_10);
-        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
 
         setCaller(CALLING_PACKAGE_3, USER_10);
-        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
 
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
@@ -4044,6 +4071,13 @@
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
 
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
         uninstallPackage(USER_0, CALLING_PACKAGE_1);
         mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDeleteIntent(CALLING_PACKAGE_1, USER_0));
@@ -4055,6 +4089,13 @@
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
 
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
         uninstallPackage(USER_10, CALLING_PACKAGE_2);
         mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDeleteIntent(CALLING_PACKAGE_2, USER_10));
@@ -4066,6 +4107,13 @@
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
 
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
         mInjectedPackages.remove(CALLING_PACKAGE_1);
         mInjectedPackages.remove(CALLING_PACKAGE_3);
 
@@ -4078,6 +4126,13 @@
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
 
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
         mService.handleUnlockUser(USER_10);
 
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
@@ -4086,6 +4141,13 @@
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
     }
 
     private void backupAndRestore() {
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 87fc7fa..2579d9f 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -199,6 +199,12 @@
         // Whether STA responder role is supported.
         public boolean responderSupported;
 
+        /** Whether the secure RTT protocol is supported. */
+        public boolean secureRttSupported;
+
+        /** Draft 11mc version supported, including major and minor version. e.g, draft 4.3 is 43 */
+        public int mcVersion;
+
         @Override
         public String toString() {
             StringBuffer sb = new StringBuffer();
@@ -223,7 +229,7 @@
                 sb.append("VHT ");
             }
 
-            sb.append("is supported. \n");
+            sb.append("is supported. ");
 
             if ((bwSupported & RTT_BW_5_SUPPORT) != 0) {
                 sb.append("5 MHz ");
@@ -252,7 +258,10 @@
             sb.append("is supported.");
 
             sb.append(" STA responder role is ")
-                .append(responderSupported ? "supported" : "not supported.");
+                    .append(responderSupported ? "supported" : "not supported");
+            sb.append(" Secure RTT protocol is ")
+                    .append(secureRttSupported ? "supported" : "not supported");
+            sb.append(" 11mc version is " + mcVersion);
 
             return sb.toString();
         }
@@ -272,6 +281,8 @@
             dest.writeInt(preambleSupported);
             dest.writeInt(bwSupported);
             dest.writeInt(responderSupported ? 1 : 0);
+            dest.writeInt(secureRttSupported ? 1 : 0);
+            dest.writeInt(mcVersion);
         }
 
         /** Implement the Parcelable interface {@hide} */
@@ -279,16 +290,18 @@
             new Creator<RttCapabilities>() {
             @Override
             public RttCapabilities createFromParcel(Parcel in) {
-                    RttCapabilities capabilities = new RttCapabilities();
-                    capabilities.oneSidedRttSupported = (in.readInt() == 1);
-                        capabilities.twoSided11McRttSupported = (in.readInt() == 1);
-                        capabilities.lciSupported = (in.readInt() == 1);
-                        capabilities.lcrSupported = (in.readInt() == 1);
-                        capabilities.preambleSupported = in.readInt();
-                        capabilities.bwSupported = in.readInt();
-                        capabilities.responderSupported = (in.readInt() == 1);
-                        return capabilities;
-                    }
+                RttCapabilities capabilities = new RttCapabilities();
+                capabilities.oneSidedRttSupported = (in.readInt() == 1);
+                capabilities.twoSided11McRttSupported = (in.readInt() == 1);
+                capabilities.lciSupported = (in.readInt() == 1);
+                capabilities.lcrSupported = (in.readInt() == 1);
+                capabilities.preambleSupported = in.readInt();
+                capabilities.bwSupported = in.readInt();
+                capabilities.responderSupported = (in.readInt() == 1);
+                capabilities.secureRttSupported = (in.readInt() == 1);
+                capabilities.mcVersion = in.readInt();
+                return capabilities;
+            }
                 /** Implement the Parcelable interface {@hide} */
                 @Override
                 public RttCapabilities[] newArray(int size) {
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 1a8197c..2ee1aef 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
@@ -31,13 +30,11 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.Protocol;
 
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
 
 
 /**
@@ -1143,7 +1140,6 @@
     private final Object mListenerMapLock = new Object();
 
     private AsyncChannel mAsyncChannel;
-    private final CountDownLatch mConnected;
     private final Handler mInternalHandler;
 
     /**
@@ -1157,22 +1153,6 @@
      * @hide
      */
     public WifiScanner(Context context, IWifiScanner service, Looper looper) {
-        this(context, service, looper, true);
-    }
-
-    /**
-     * Create a new WifiScanner instance.
-     *
-     * @param context The application context.
-     * @param service The IWifiScanner Binder interface
-     * @param looper the Looper used to deliver callbacks
-     * @param waitForConnection If true, this will not return until a connection to Wifi Scanner
-     *          service is established.
-     * @hide
-     */
-    @VisibleForTesting
-    public WifiScanner(Context context, IWifiScanner service, Looper looper,
-            boolean waitForConnection) {
         mContext = context;
         mService = service;
 
@@ -1188,17 +1168,12 @@
         }
 
         mAsyncChannel = new AsyncChannel();
-        mConnected = new CountDownLatch(1);
 
         mInternalHandler = new ServiceHandler(looper);
-        mAsyncChannel.connect(mContext, mInternalHandler, messenger);
-        if (waitForConnection) {
-            try {
-                mConnected.await();
-            } catch (InterruptedException e) {
-                Log.e(TAG, "interrupted wait at init");
-            }
-        }
+        mAsyncChannel.connectSync(mContext, mInternalHandler, messenger);
+        // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
+        // synchronously, which causes WifiScanningService to receive the wrong replyTo value.
+        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
     }
 
     private void validateChannel() {
@@ -1326,17 +1301,6 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
-                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
-                    } else {
-                        Log.e(TAG, "Failed to set up channel connection");
-                        // This will cause all further async API calls on the WifiManager
-                        // to fail and throw an exception
-                        mAsyncChannel = null;
-                    }
-                    mConnected.countDown();
-                    return;
                 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
                     return;
                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: