Merge "Fix multiple configuration changes in multiwindow" into nyc-dev
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 96757bb..3f9629c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2216,9 +2216,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 +2353,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 +2387,7 @@
      */
     public ComponentName setGlobalProxy(@NonNull ComponentName admin, Proxy proxySpec,
             List<String> exclusionList ) {
+        throwIfParentInstance("setGlobalProxy");
         if (proxySpec == null) {
             throw new NullPointerException();
         }
@@ -2453,6 +2453,7 @@
      */
     public void setRecommendedGlobalProxy(@NonNull ComponentName admin, @Nullable ProxyInfo
             proxyInfo) {
+        throwIfParentInstance("setRecommendedGlobalProxy");
         if (mService != null) {
             try {
                 mService.setRecommendedGlobalProxy(admin, proxyInfo);
@@ -2603,6 +2604,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 +2625,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());
@@ -2718,6 +2721,7 @@
      *         owner.
      */
     public boolean installCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
+        throwIfParentInstance("installCaCert");
         if (mService != null) {
             try {
                 return mService.installCaCert(admin, certBuffer);
@@ -2738,6 +2742,7 @@
      *         owner.
      */
     public void uninstallCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
+        throwIfParentInstance("uninstallCaCert");
         if (mService != null) {
             try {
                 final String alias = getCaCertAlias(certBuffer);
@@ -2763,6 +2768,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 +2797,7 @@
      *         owner.
      */
     public void uninstallAllUserCaCerts(@Nullable ComponentName admin) {
+        throwIfParentInstance("uninstallAllUserCaCerts");
         if (mService != null) {
             try {
                 mService.uninstallCaCerts(admin, new TrustedCertificateStore().userAliases()
@@ -2811,6 +2818,7 @@
      *         owner.
      */
     public boolean hasCaCertInstalled(@Nullable ComponentName admin, byte[] certBuffer) {
+        throwIfParentInstance("hasCaCertInstalled");
         if (mService != null) {
             try {
                 mService.enforceCanManageCaCerts(admin);
@@ -2879,6 +2887,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 +2920,7 @@
      *         owner.
      */
     public boolean removeKeyPair(@Nullable ComponentName admin, @NonNull String alias) {
+        throwIfParentInstance("removeKeyPair");
         try {
             return mService.removeKeyPair(admin, alias);
         } catch (RemoteException e) {
@@ -2951,6 +2961,7 @@
      */
     public void setCertInstallerPackage(@NonNull ComponentName admin, @Nullable String
             installerPackage) throws SecurityException {
+        throwIfParentInstance("setCertInstallerPackage");
         if (mService != null) {
             try {
                 mService.setCertInstallerPackage(admin, installerPackage);
@@ -2970,6 +2981,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 +3012,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 +3034,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 +3062,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 +3079,7 @@
      * have disabled the camera
      */
     public boolean getCameraDisabled(@Nullable ComponentName admin) {
+        throwIfParentInstance("getCameraDisabled");
         return getCameraDisabled(admin, myUserId());
     }
 
@@ -3093,6 +3109,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 +3148,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 +3165,7 @@
      * have disabled screen capture.
      */
     public boolean getScreenCaptureDisabled(@Nullable ComponentName admin) {
+        throwIfParentInstance("getScreenCaptureDisabled");
         return getScreenCaptureDisabled(admin, myUserId());
     }
 
@@ -3176,6 +3195,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 +3209,7 @@
      * @return true if auto time is required.
      */
     public boolean getAutoTimeRequired() {
+        throwIfParentInstance("getAutoTimeRequired");
         if (mService != null) {
             try {
                 return mService.getAutoTimeRequired();
@@ -3215,6 +3236,7 @@
      */
     public void setForceEphemeralUsers(
             @NonNull ComponentName admin, boolean forceEphemeralUsers) {
+        throwIfParentInstance("setForceEphemeralUsers");
         if (mService != null) {
             try {
                 mService.setForceEphemeralUsers(admin, forceEphemeralUsers);
@@ -3230,6 +3252,7 @@
      * @hide
      */
     public boolean getForceEphemeralUsers(@NonNull ComponentName admin) {
+        throwIfParentInstance("getForceEphemeralUsers");
         if (mService != null) {
             try {
                 return mService.getForceEphemeralUsers(admin);
@@ -3517,6 +3540,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 +3638,7 @@
      *             does not own the current device owner component.
      */
     public void clearDeviceOwnerApp(String packageName) {
+        throwIfParentInstance("clearDeviceOwnerApp");
         if (mService != null) {
             try {
                 mService.clearDeviceOwner(packageName);
@@ -3731,6 +3756,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 +3830,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 +3844,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 +3876,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 +3899,7 @@
      */
     public boolean isPackageSuspended(@NonNull ComponentName admin, String packageName)
             throws NameNotFoundException {
+        throwIfParentInstance("isPackageSuspended");
         if (mService != null) {
             try {
                 return mService.isPackageSuspended(admin, packageName);
@@ -3891,6 +3921,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 +3943,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 +3962,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());
@@ -4024,6 +4057,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 +4080,7 @@
      */
     public void clearPackagePersistentPreferredActivities(@NonNull ComponentName admin,
             String packageName) {
+        throwIfParentInstance("clearPackagePersistentPreferredActivities");
         if (mService != null) {
             try {
                 mService.clearPackagePersistentPreferredActivities(admin, packageName);
@@ -4074,6 +4109,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 +4131,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 +4151,7 @@
      * that method.
      */
     public boolean isCallerApplicationRestrictionsManagingPackage() {
+        throwIfParentInstance("isCallerApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
                 return mService.isCallerApplicationRestrictionsManagingPackage();
@@ -4159,6 +4197,7 @@
      */
     public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
             Bundle settings) {
+        throwIfParentInstance("setApplicationRestrictions");
         if (mService != null) {
             try {
                 mService.setApplicationRestrictions(admin, packageName, settings);
@@ -4257,6 +4296,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 +4317,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 +4358,7 @@
      */
     public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin,
             boolean disabled) {
+        throwIfParentInstance("setCrossProfileContactsSearchDisabled");
         if (mService != null) {
             try {
                 mService.setCrossProfileContactsSearchDisabled(admin, disabled);
@@ -4337,6 +4379,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 +4450,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 +4473,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 +4517,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 +4536,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 +4566,7 @@
      */
     public boolean setPermittedAccessibilityServices(@NonNull ComponentName admin,
             List<String> packageNames) {
+        throwIfParentInstance("setPermittedAccessibilityServices");
         if (mService != null) {
             try {
                 return mService.setPermittedAccessibilityServices(admin, packageNames);
@@ -4540,6 +4588,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);
@@ -4617,6 +4666,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 +4689,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);
@@ -4834,6 +4885,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 +4903,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 +4921,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 +4947,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 +4970,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 +4992,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 +5014,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 +5059,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 +5079,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 +5099,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 +5120,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 +5153,7 @@
      */
     public void setAccountManagementDisabled(@NonNull ComponentName admin, String accountType,
             boolean disabled) {
+        throwIfParentInstance("setAccountManagementDisabled");
         if (mService != null) {
             try {
                 mService.setAccountManagementDisabled(admin, accountType, disabled);
@@ -5111,6 +5174,7 @@
      * @see #setAccountManagementDisabled
      */
     public String[] getAccountTypesWithManagementDisabled() {
+        throwIfParentInstance("getAccountTypesWithManagementDisabled");
         return getAccountTypesWithManagementDisabledAsUser(myUserId());
     }
 
@@ -5148,6 +5212,7 @@
      */
     public void setLockTaskPackages(@NonNull ComponentName admin, String[] packages)
             throws SecurityException {
+        throwIfParentInstance("setLockTaskPackages");
         if (mService != null) {
             try {
                 mService.setLockTaskPackages(admin, packages);
@@ -5164,6 +5229,7 @@
      * @hide
      */
     public String[] getLockTaskPackages(@NonNull ComponentName admin) {
+        throwIfParentInstance("getLockTaskPackages");
         if (mService != null) {
             try {
                 return mService.getLockTaskPackages(admin);
@@ -5180,6 +5246,7 @@
      * @param pkg The package to check
      */
     public boolean isLockTaskPermitted(String pkg) {
+        throwIfParentInstance("isLockTaskPermitted");
         if (mService != null) {
             try {
                 return mService.isLockTaskPermitted(pkg);
@@ -5228,6 +5295,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 +5328,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 +5352,7 @@
      */
     public void setRestrictionsProvider(@NonNull ComponentName admin,
             @Nullable ComponentName provider) {
+        throwIfParentInstance("setRestrictionsProvider");
         if (mService != null) {
             try {
                 mService.setRestrictionsProvider(admin, provider);
@@ -5300,6 +5370,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 +5388,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 +5409,7 @@
      */
     public void setUninstallBlocked(@NonNull ComponentName admin, String packageName,
             boolean uninstallBlocked) {
+        throwIfParentInstance("setUninstallBlocked");
         if (mService != null) {
             try {
                 mService.setUninstallBlocked(admin, packageName, uninstallBlocked);
@@ -5362,6 +5435,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 +5463,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 +5491,7 @@
      */
     public boolean removeCrossProfileWidgetProvider(
             @NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("removeCrossProfileWidgetProvider");
         if (mService != null) {
             try {
                 return mService.removeCrossProfileWidgetProvider(admin, packageName);
@@ -5437,6 +5513,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 +5535,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 +5555,7 @@
      * @see SystemUpdatePolicy
      */
     public void setSystemUpdatePolicy(@NonNull ComponentName admin, SystemUpdatePolicy policy) {
+        throwIfParentInstance("setSystemUpdatePolicy");
         if (mService != null) {
             try {
                 mService.setSystemUpdatePolicy(admin, policy);
@@ -5492,6 +5571,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 +5597,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 +5616,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) {
@@ -5580,6 +5662,7 @@
      * @see #setPermissionGrantState
      */
     public void setPermissionPolicy(@NonNull ComponentName admin, int policy) {
+        throwIfParentInstance("setPermissionPolicy");
         try {
             mService.setPermissionPolicy(admin, policy);
         } catch (RemoteException re) {
@@ -5594,6 +5677,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 +5714,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 +5743,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 +5759,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 +5775,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 +5809,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 +5826,7 @@
      * @see TelephonyManager#CALL_STATE_IDLE
      */
     public void reboot(@NonNull ComponentName admin) {
+        throwIfParentInstance("reboot");
         try {
             mService.reboot(admin);
         } catch (RemoteException re) {
@@ -5763,6 +5853,7 @@
      */
     public void setShortSupportMessage(@NonNull ComponentName admin,
             @Nullable String message) {
+        throwIfParentInstance("setShortSupportMessage");
         if (mService != null) {
             try {
                 mService.setShortSupportMessage(admin, message);
@@ -5781,6 +5872,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public String getShortSupportMessage(@NonNull ComponentName admin) {
+        throwIfParentInstance("getShortSupportMessage");
         if (mService != null) {
             try {
                 return mService.getShortSupportMessage(admin);
@@ -5807,6 +5899,7 @@
      */
     public void setLongSupportMessage(@NonNull ComponentName admin,
             @Nullable String message) {
+        throwIfParentInstance("setLongSupportMessage");
         if (mService != null) {
             try {
                 mService.setLongSupportMessage(admin, message);
@@ -5825,6 +5918,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public String getLongSupportMessage(@NonNull ComponentName admin) {
+        throwIfParentInstance("getLongSupportMessage");
         if (mService != null) {
             try {
                 return mService.getLongSupportMessage(admin);
@@ -5921,6 +6015,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.");
@@ -5947,6 +6042,7 @@
      * @see #retrieveSecurityLogs
      */
     public void setSecurityLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
+        throwIfParentInstance("setSecurityLoggingEnabled");
         try {
             mService.setSecurityLoggingEnabled(admin, enabled);
         } catch (RemoteException re) {
@@ -5965,6 +6061,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) {
@@ -5988,6 +6085,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) {
@@ -6033,6 +6131,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();
@@ -6054,6 +6153,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setOrganizationColor(@NonNull ComponentName admin, int color) {
+        throwIfParentInstance("setOrganizationColor");
         try {
             mService.setOrganizationColor(admin, color);
         } catch (RemoteException re) {
@@ -6089,6 +6189,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public int getOrganizationColor(@NonNull ComponentName admin) {
+        throwIfParentInstance("getOrganizationColor");
         try {
             return mService.getOrganizationColor(admin);
         } catch (RemoteException re) {
@@ -6124,6 +6225,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setOrganizationName(@NonNull ComponentName admin, @Nullable String title) {
+        throwIfParentInstance("setOrganizationName");
         try {
             mService.setOrganizationName(admin, title);
         } catch (RemoteException re) {
@@ -6140,6 +6242,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public String getOrganizationName(@NonNull ComponentName admin) {
+        throwIfParentInstance("getOrganizationName");
         try {
             return mService.getOrganizationName(admin);
         } catch (RemoteException re) {
@@ -6265,4 +6368,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/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index e40ebf7..d39968a 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -115,6 +115,13 @@
      */
     public static final String EXTRA_MAX_CHARGING_VOLTAGE = "max_charging_voltage";
 
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer containing the charge counter present in the battery.
+     * {@hide}
+     */
+     public static final String EXTRA_CHARGE_COUNTER = "charge_counter";
+
     // values for "status" field in the ACTION_BATTERY_CHANGED Intent
     public static final int BATTERY_STATUS_UNKNOWN = 1;
     public static final int BATTERY_STATUS_CHARGING = 2;
diff --git a/core/java/android/os/BatteryProperties.java b/core/java/android/os/BatteryProperties.java
index c3e0f24..b509d76 100644
--- a/core/java/android/os/BatteryProperties.java
+++ b/core/java/android/os/BatteryProperties.java
@@ -30,6 +30,7 @@
     public int batteryLevel;
     public int batteryVoltage;
     public int batteryTemperature;
+    public int batteryChargeCounter;
     public String batteryTechnology;
 
     public BatteryProperties() {
@@ -47,6 +48,7 @@
         batteryLevel = other.batteryLevel;
         batteryVoltage = other.batteryVoltage;
         batteryTemperature = other.batteryTemperature;
+        batteryChargeCounter = other.batteryChargeCounter;
         batteryTechnology = other.batteryTechnology;
     }
 
@@ -67,6 +69,7 @@
         batteryLevel = p.readInt();
         batteryVoltage = p.readInt();
         batteryTemperature = p.readInt();
+        batteryChargeCounter = p.readInt();
         batteryTechnology = p.readString();
     }
 
@@ -82,6 +85,7 @@
         p.writeInt(batteryLevel);
         p.writeInt(batteryVoltage);
         p.writeInt(batteryTemperature);
+        p.writeInt(batteryChargeCounter);
         p.writeString(batteryTechnology);
     }
 
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 29a72fd..f1c8c7d 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -31,12 +31,7 @@
             throw new IllegalArgumentException("Path string can not be null.");
         }
         Path path = new Path();
-        boolean hasValidPathData = nParseStringForPath(path.mNativePath, pathString,
-                pathString.length());
-        if (!hasValidPathData) {
-            throw new IllegalArgumentException("Path string: " + pathString +
-                    " does not contain valid path data");
-        }
+        nParseStringForPath(path.mNativePath, pathString, pathString.length());
         return path;
     }
 
@@ -104,7 +99,6 @@
             }
             super.finalize();
         }
-
     }
 
     /**
@@ -123,7 +117,7 @@
     }
 
     // Native functions are defined below.
-    private static native boolean nParseStringForPath(long pathPtr, String pathString,
+    private static native void nParseStringForPath(long pathPtr, String pathString,
             int stringLength);
     private static native void nCreatePathFromPathData(long outPathPtr, long pathData);
     private static native long nCreateEmptyPathData();
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index c972476..f44d4c1a 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -485,15 +485,25 @@
     }
 
     /**
-     * Stops any rendering into the surface. Use this if it is unclear whether
+     * Halts any current rendering into the surface. Use this if it is unclear whether
      * or not the surface used by the HardwareRenderer will be changing. It
-     * Suspends any rendering into the surface, but will not do any destruction
+     * Suspends any rendering into the surface, but will not do any destruction.
+     *
+     * Any subsequent draws will override the pause, resuming normal operation.
      */
     boolean pauseSurface(Surface surface) {
         return nPauseSurface(mNativeProxy, surface);
     }
 
     /**
+     * Hard stops or resumes rendering into the surface. This flag is used to
+     * determine whether or not it is safe to use the given surface *at all*
+     */
+    void setStopped(boolean stopped) {
+        nSetStopped(mNativeProxy, stopped);
+    }
+
+    /**
      * Destroys all hardware rendering resources associated with the specified
      * view hierarchy.
      *
@@ -988,6 +998,7 @@
     private static native void nInitialize(long nativeProxy, Surface window);
     private static native void nUpdateSurface(long nativeProxy, Surface window);
     private static native boolean nPauseSurface(long nativeProxy, Surface window);
+    private static native void nSetStopped(long nativeProxy, boolean stopped);
     private static native void nSetup(long nativeProxy, int width, int height,
             float lightRadius, int ambientShadowAlpha, int spotShadowAlpha);
     private static native void nSetLightCenter(long nativeProxy,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5b2877f..5626f01 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1079,13 +1079,16 @@
     void setWindowStopped(boolean stopped) {
         if (mStopped != stopped) {
             mStopped = stopped;
+            final ThreadedRenderer renderer = mAttachInfo.mHardwareRenderer;
+            if (renderer != null) {
+                if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
+                renderer.setStopped(mStopped);
+            }
             if (!mStopped) {
                 scheduleTraversals();
             } else {
-                if (mAttachInfo.mHardwareRenderer != null) {
-                    if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle());
-                    mAttachInfo.mHardwareRenderer.updateSurface(null);
-                    mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
+                if (renderer != null) {
+                    renderer.destroyHardwareResources(mView);
                 }
             }
         }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c372c30..ddad23a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1701,6 +1701,14 @@
          */
         public int accessibilityIdOfAnchor = -1;
 
+        /**
+         * The window title isn't kept in sync with what is displayed in the title bar, so we
+         * separately track the currently shown title to provide to accessibility.
+         *
+         * @hide
+         */
+        public CharSequence accessibilityTitle;
+
         public LayoutParams() {
             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
             type = TYPE_APPLICATION;
@@ -1749,6 +1757,7 @@
                 title = "";
 
             mTitle = TextUtils.stringOrSpannedString(title);
+            accessibilityTitle = mTitle;
         }
 
         public final CharSequence getTitle() {
@@ -1808,6 +1817,7 @@
             out.writeInt(hasManualSurfaceInsets ? 1 : 0);
             out.writeInt(needsMenuKey);
             out.writeInt(accessibilityIdOfAnchor);
+            TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
         }
 
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1859,6 +1869,7 @@
             hasManualSurfaceInsets = in.readInt() != 0;
             needsMenuKey = in.readInt();
             accessibilityIdOfAnchor = in.readInt();
+            accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1900,6 +1911,8 @@
         /** {@hide} */
         public static final int ACCESSIBILITY_ANCHOR_CHANGED = 1 << 24;
         /** {@hide} */
+        public static final int ACCESSIBILITY_TITLE_CHANGED = 1 << 25;
+        /** {@hide} */
         public static final int EVERYTHING_CHANGED = 0xffffffff;
 
         // internal buffer to backup/restore parameters under compatibility mode.
@@ -2065,6 +2078,13 @@
                 changes |= ACCESSIBILITY_ANCHOR_CHANGED;
             }
 
+            if (!Objects.equals(accessibilityTitle, o.accessibilityTitle)
+                    && o.accessibilityTitle != null) {
+                // NOTE: accessibilityTitle only copied if the originator set one.
+                accessibilityTitle = o.accessibilityTitle;
+                changes |= ACCESSIBILITY_TITLE_CHANGED;
+            }
+
             return changes;
         }
 
diff --git a/core/java/android/webkit/WebViewProviderResponse.java b/core/java/android/webkit/WebViewProviderResponse.java
index f5e09e2..c0aeb59 100644
--- a/core/java/android/webkit/WebViewProviderResponse.java
+++ b/core/java/android/webkit/WebViewProviderResponse.java
@@ -21,7 +21,7 @@
 import android.os.Parcelable;
 
 /** @hide */
-public class WebViewProviderResponse implements Parcelable {
+public final class WebViewProviderResponse implements Parcelable {
 
     public WebViewProviderResponse(PackageInfo packageInfo, int status) {
         this.packageInfo = packageInfo;
@@ -56,6 +56,6 @@
         out.writeInt(status);
     }
 
-    PackageInfo packageInfo;
-    int status;
+    public final PackageInfo packageInfo;
+    public final int status;
 }
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index f6fbaab..27588e9 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -53,6 +54,7 @@
 
     private int mUserId;
     private int mReason;
+    private IntentSender mTarget;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -60,6 +62,7 @@
         Intent intent = getIntent();
         mReason = intent.getIntExtra(EXTRA_UNLAUNCHABLE_REASON, -1);
         mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+        mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT);
 
         if (mUserId == UserHandle.USER_NULL) {
             Log.wtf(TAG, "Invalid user id: " + mUserId + ". Stopping.");
@@ -105,6 +108,14 @@
     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) {
+                try {
+                    startIntentSenderForResult(mTarget, -1, null, 0, 0, 0);
+                } catch (IntentSender.SendIntentException e) {
+                    /* ignore */
+                }
+            }
         }
     }
 
@@ -121,4 +132,10 @@
         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         return intent;
     }
+
+    public static Intent createInQuietModeDialogIntent(int userId, IntentSender target) {
+        Intent intent = createInQuietModeDialogIntent(userId);
+        intent.putExtra(Intent.EXTRA_INTENT, target);
+        return intent;
+    }
 }
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 0f257d7..c64328b 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -516,8 +516,8 @@
         }
         mTitle = title;
         WindowManager.LayoutParams params = getAttributes();
-        if (!TextUtils.equals(title, params.getTitle())) {
-            params.setTitle(title);
+        if (!TextUtils.equals(title, params.accessibilityTitle)) {
+            params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
             dispatchWindowAttributesChanged(getAttributes());
         }
     }
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 6dc251c..1c2d13d 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -53,36 +53,14 @@
     fontFamily->Unref();
 }
 
-static jboolean addSkTypeface(FontFamily* family, SkTypeface* face) {
-    MinikinFont* minikinFont = new MinikinFontSkia(face);
+static jboolean addSkTypeface(FontFamily* family, SkTypeface* face, const void* fontData,
+        size_t fontSize, int ttcIndex) {
+    MinikinFont* minikinFont = new MinikinFontSkia(face, fontData, fontSize, ttcIndex);
     bool result = family->addFont(minikinFont);
     minikinFont->Unref();
     return result;
 }
 
-static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path,
-        jint ttcIndex) {
-    NPE_CHECK_RETURN_ZERO(env, path);
-    ScopedUtfChars str(env, path);
-    SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
-    if (face == NULL) {
-        ALOGE("addFont failed to create font %s", str.c_str());
-        return false;
-    }
-    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    return addSkTypeface(fontFamily, face);
-}
-
-static struct {
-    jmethodID mGet;
-    jmethodID mSize;
-} gListClassInfo;
-
-static struct {
-    jfieldID mTag;
-    jfieldID mStyleValue;
-} gAxisClassInfo;
-
 static void release_global_ref(const void* /*data*/, void* context) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     bool needToAttach = (env == NULL);
@@ -106,6 +84,47 @@
     }
 }
 
+static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jobject bytebuf,
+        jint ttcIndex) {
+    NPE_CHECK_RETURN_ZERO(env, bytebuf);
+    const void* fontPtr = env->GetDirectBufferAddress(bytebuf);
+    if (fontPtr == NULL) {
+        ALOGE("addFont failed to create font, buffer invalid");
+        return false;
+    }
+    jlong fontSize = env->GetDirectBufferCapacity(bytebuf);
+    if (fontSize < 0) {
+        ALOGE("addFont failed to create font, buffer size invalid");
+        return false;
+    }
+    jobject fontRef = MakeGlobalRefOrDie(env, bytebuf);
+    SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize,
+            release_global_ref, reinterpret_cast<void*>(fontRef)));
+    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data));
+
+    SkFontMgr::FontParameters params;
+    params.setCollectionIndex(ttcIndex);
+
+    SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+    SkTypeface* face = fm->createFromStream(fontData.release(), params);
+    if (face == NULL) {
+        ALOGE("addFont failed to create font");
+        return false;
+    }
+    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
+    return addSkTypeface(fontFamily, face, fontPtr, (size_t)fontSize, ttcIndex);
+}
+
+static struct {
+    jmethodID mGet;
+    jmethodID mSize;
+} gListClassInfo;
+
+static struct {
+    jfieldID mTag;
+    jfieldID mStyleValue;
+} gAxisClassInfo;
+
 static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
         jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) {
     NPE_CHECK_RETURN_ZERO(env, font);
@@ -133,7 +152,7 @@
         }
     }
 
-    void* fontPtr = env->GetDirectBufferAddress(font);
+    const void* fontPtr = env->GetDirectBufferAddress(font);
     if (fontPtr == NULL) {
         ALOGE("addFont failed to create font, buffer invalid");
         return false;
@@ -159,7 +178,7 @@
         return false;
     }
     FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    MinikinFont* minikinFont = new MinikinFontSkia(face);
+    MinikinFont* minikinFont = new MinikinFontSkia(face, fontPtr, (size_t)fontSize, ttcIndex);
     fontFamily->addFont(minikinFont, FontStyle(weight / 100, isItalic));
     minikinFont->Unref();
     return true;
@@ -191,6 +210,7 @@
         return false;
     }
 
+    size_t bufSize = asset->getLength();
     SkAutoTUnref<SkData> data(SkData::NewWithProc(buf, asset->getLength(), releaseAsset, asset));
     SkMemoryStream* stream = new SkMemoryStream(data);
     // CreateFromStream takes ownership of stream.
@@ -200,7 +220,7 @@
         return false;
     }
     FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    return addSkTypeface(fontFamily, face);
+    return addSkTypeface(fontFamily, face, buf, bufSize, /* ttcIndex */ 0);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -208,7 +228,7 @@
 static const JNINativeMethod gFontFamilyMethods[] = {
     { "nCreateFamily",         "(Ljava/lang/String;I)J", (void*)FontFamily_create },
     { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
-    { "nAddFont",              "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont },
+    { "nAddFont",              "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont },
     { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
             (void*)FontFamily_addFontWeightStyle },
     { "nAddFontFromAsset",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index b04293e..e5c4a2d 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -176,6 +176,9 @@
     PathParser::ParseResult result;
     PathData data;
     PathParser::getPathDataFromString(&data, &result, pathString, stringLength);
+    if (result.failureOccurred) {
+        doThrowIAE(env, result.failureMessage.c_str());
+    }
     path->mutateStagingProperties()->setData(data);
     env->ReleaseStringUTFChars(inputStr, pathString);
 }
diff --git a/core/jni/android_util_PathParser.cpp b/core/jni/android_util_PathParser.cpp
index 0927120..0c867f1 100644
--- a/core/jni/android_util_PathParser.cpp
+++ b/core/jni/android_util_PathParser.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "jni.h"
+#include "GraphicsJNI.h"
 
 #include <PathParser.h>
 #include <SkPath.h>
@@ -27,7 +28,7 @@
 
 using namespace uirenderer;
 
-static bool parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
+static void parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
         jint strLength) {
     const char* pathString = env->GetStringUTFChars(inputPathStr, NULL);
     SkPath* skPath = reinterpret_cast<SkPath*>(skPathHandle);
@@ -36,9 +37,8 @@
     PathParser::parseStringForSkPath(skPath, &result, pathString, strLength);
     env->ReleaseStringUTFChars(inputPathStr, pathString);
     if (result.failureOccurred) {
-        ALOGE(result.failureMessage.c_str());
+        doThrowIAE(env, result.failureMessage.c_str());
     }
-    return !result.failureOccurred;
 }
 
 static long createEmptyPathData(JNIEnv*, jobject) {
@@ -62,7 +62,7 @@
         return reinterpret_cast<jlong>(pathData);
     } else {
         delete pathData;
-        ALOGE(result.failureMessage.c_str());
+        doThrowIAE(env, result.failureMessage.c_str());
         return NULL;
     }
 }
@@ -100,7 +100,7 @@
 }
 
 static const JNINativeMethod gMethods[] = {
-    {"nParseStringForPath", "(JLjava/lang/String;I)Z", (void*)parseStringForPath},
+    {"nParseStringForPath", "(JLjava/lang/String;I)V", (void*)parseStringForPath},
     {"nCreateEmptyPathData", "!()J", (void*)createEmptyPathData},
     {"nCreatePathData", "!(J)J", (void*)createPathData},
     {"nCreatePathDataFromString", "(Ljava/lang/String;I)J", (void*)createPathDataFromStringPath},
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 3d65209..ef45c87 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -479,6 +479,12 @@
     return proxy->pauseSurface(surface);
 }
 
+static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean stopped) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setStopped(stopped);
+}
+
 static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
         jint width, jint height, jfloat lightRadius, jint ambientShadowAlpha, jint spotShadowAlpha) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -732,6 +738,7 @@
     { "nInitialize", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_initialize },
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
     { "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface },
+    { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped },
     { "nSetup", "(JIIFII)V", (void*) android_view_ThreadedRenderer_setup },
     { "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter },
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
diff --git a/core/res/res/drawable/ic_corp_user_badge.xml b/core/res/res/drawable/ic_corp_user_badge.xml
new file mode 100644
index 0000000..23809d5
--- /dev/null
+++ b/core/res/res/drawable/ic_corp_user_badge.xml
@@ -0,0 +1,24 @@
+<!--
+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="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M18,0C8.06,-0 0,8.06 0,18C0,27.94 8.06,36 18,36C27.94,36 36,27.94 36,18C36,8.06 27.94,0 18,0zM15.5,10.5L20.5,10.5L21.75,11.75L21.75,13L24.66,13C25.57,13 26.34,13.74 26.34,14.66L26.34,18C26.34,18.92 25.57,19.66 24.66,19.66L19.66,19.66L19.66,18.41L16.34,18.41L16.34,19.66L11.34,19.66C10.43,19.66 9.66,18.92 9.66,18L9.66,14.66C9.66,13.74 10.43,13 11.34,13L14.25,13L14.25,11.78L15.5,10.5zM15.5,11.75L15.5,13L20.5,13L20.5,11.75L15.5,11.75zM10.5,20.5L16.34,20.5L16.34,21.75L19.66,21.75L19.66,20.5L25.5,20.5L25.5,23.84C25.5,24.76 24.76,25.5 23.84,25.5L12.16,25.5C11.24,25.5 10.5,24.76 10.5,23.84L10.5,20.5z"/>
+</vector>
diff --git a/core/res/res/values-w320dp/dimens.xml b/core/res/res/values-w320dp/dimens.xml
new file mode 100644
index 0000000..ad6d2ec
--- /dev/null
+++ b/core/res/res/values-w320dp/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- The platform's desired fixed width for a dialog along the major axis
+         (the screen is in landscape). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_width_major">320dp</item>
+    <!-- The platform's desired fixed width for a dialog along the minor axis
+         (the screen is in portrait). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_width_minor">320dp</item>
+</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a21f276..9aec1bb 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -112,10 +112,10 @@
 
     <!-- The platform's desired fixed width for a dialog along the major axis
          (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_width_major">320dp</item>
+    <item type="dimen" name="dialog_fixed_width_major">100%</item>
     <!-- The platform's desired fixed width for a dialog along the minor axis
          (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_width_minor">320dp</item>
+    <item type="dimen" name="dialog_fixed_width_minor">100%</item>
     <!-- The platform's desired fixed height for a dialog along the major axis
          (the screen is in portrait). This may be either a fraction or a dimension.-->
     <item type="dimen" name="dialog_fixed_height_major">80%</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d0414c3..592904e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1265,6 +1265,7 @@
   <java-symbol type="drawable" name="ic_corp_badge" />
   <java-symbol type="drawable" name="ic_corp_badge_off" />
   <java-symbol type="drawable" name="ic_corp_icon_badge" />
+  <java-symbol type="drawable" name="ic_corp_user_badge" />
   <java-symbol type="drawable" name="ic_corp_badge_no_background" />
   <java-symbol type="drawable" name="ic_corp_icon" />
   <java-symbol type="drawable" name="ic_corp_statusbar_icon" />
diff --git a/core/tests/notificationtests/Android.mk b/core/tests/notificationtests/Android.mk
index be2e6bf..702218c 100644
--- a/core/tests/notificationtests/Android.mk
+++ b/core/tests/notificationtests/Android.mk
@@ -11,6 +11,9 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := NotificationStressTests
 
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    ub-uiautomator
+
 include $(BUILD_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/notificationtests/src/android/app/NotificationStressTest.java b/core/tests/notificationtests/src/android/app/NotificationStressTest.java
index 4cb617e..6e86c37 100644
--- a/core/tests/notificationtests/src/android/app/NotificationStressTest.java
+++ b/core/tests/notificationtests/src/android/app/NotificationStressTest.java
@@ -16,15 +16,19 @@
 
 package android.app;
 
-
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
+import android.os.RemoteException;
 import android.os.SystemClock;
+import android.support.test.uiautomator.UiDevice;
 import android.test.InstrumentationTestCase;
 import android.test.RepetitiveTest;
-import android.test.TimedTest;
+import android.util.Log;
 
+import java.lang.InterruptedException;
+import java.lang.reflect.Method;
 import java.util.Random;
 
 /**
@@ -34,51 +38,78 @@
 public class NotificationStressTest extends InstrumentationTestCase {
 
     private static final int NUM_ITERATIONS = 200;
+    private static final int NUM_ITERATIONS_2 = 30;
+    private static final int LONG_TIMEOUT = 2000;
+    // 50 notifications per app: defined as Variable MAX_PACKAGE_NOTIFICATIONS in
+    // NotificationManagerService.java
+    private static final int MAX_NOTIFCATIONS = 50;
     private static final int[] ICONS = new int[] {
-        android.R.drawable.stat_notify_call_mute,
-        android.R.drawable.stat_notify_chat,
-        android.R.drawable.stat_notify_error,
-        android.R.drawable.stat_notify_missed_call,
-        android.R.drawable.stat_notify_more,
-        android.R.drawable.stat_notify_sdcard,
-        android.R.drawable.stat_notify_sdcard_prepare,
-        android.R.drawable.stat_notify_sdcard_usb,
-        android.R.drawable.stat_notify_sync,
-        android.R.drawable.stat_notify_sync_noanim,
-        android.R.drawable.stat_notify_voicemail,
+            android.R.drawable.stat_notify_call_mute,
+            android.R.drawable.stat_notify_chat,
+            android.R.drawable.stat_notify_error,
+            android.R.drawable.stat_notify_missed_call,
+            android.R.drawable.stat_notify_more,
+            android.R.drawable.stat_notify_sdcard,
+            android.R.drawable.stat_notify_sdcard_prepare,
+            android.R.drawable.stat_notify_sdcard_usb,
+            android.R.drawable.stat_notify_sync,
+            android.R.drawable.stat_notify_sync_noanim,
+            android.R.drawable.stat_notify_voicemail,
     };
 
     private final Random mRandom = new Random();
     private Context mContext;
     private NotificationManager mNotificationManager;
-    private int notifyId = 0;
+    private UiDevice mDevice = null;
+    private int mNotifyId = 0;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        mDevice = UiDevice.getInstance(getInstrumentation());
         mContext = getInstrumentation().getContext();
         mNotificationManager = (NotificationManager) mContext.getSystemService(
                 Context.NOTIFICATION_SERVICE);
+        mDevice.setOrientationNatural();
+        mNotificationManager.cancelAll();
     }
 
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
+        mDevice.unfreezeRotation();
         mNotificationManager.cancelAll();
     }
 
-    @RepetitiveTest(numIterations=NUM_ITERATIONS)
+    @RepetitiveTest(numIterations = NUM_ITERATIONS)
     public void testNotificationStress() {
         // Cancel one of every five notifications to vary load on notification manager
-        if (notifyId % 5 == 4) {
-            mNotificationManager.cancel(notifyId - 4);
+        if (mNotifyId % 5 == 4) {
+            mNotificationManager.cancel(mNotifyId - 4);
         }
-        sendNotification(notifyId++, "testNotificationStressNotify");
+        sendNotification(mNotifyId++, "testNotificationStressNotify");
+    }
+
+    @RepetitiveTest(numIterations = NUM_ITERATIONS_2)
+    public void testNotificationsWithShadeStress() throws Exception {
+        mDevice.openNotification();
+        Thread.sleep(LONG_TIMEOUT);
+        for (int j = 0; j < MAX_NOTIFCATIONS; j++) {
+            sendNotification(mNotifyId++, "testNotificationStressNotify");
+        }
+        Thread.sleep(500);
+        assertTrue(mNotificationManager.getActiveNotifications().length == MAX_NOTIFCATIONS);
+        for (int j = 0; j < MAX_NOTIFCATIONS; j++) {
+            mNotificationManager.cancel(--mNotifyId);
+        }
+        if (isLockScreen()) {
+            fail("Notification stress test failed, back to lockscreen");
+        }
     }
 
     private void sendNotification(int id, CharSequence text) {
         // Fill in arbitrary content
-        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
+        Intent intent = new Intent(Intent.ACTION_VIEW);
         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
         CharSequence title = text + " " + id;
         CharSequence subtitle = String.valueOf(System.currentTimeMillis());
@@ -90,8 +121,19 @@
                 .setContentTitle(title)
                 .setContentText(subtitle)
                 .setContentIntent(pendingIntent)
+                .setPriority(Notification.PRIORITY_HIGH)
                 .build();
         mNotificationManager.notify(id, notification);
         SystemClock.sleep(10);
     }
+
+    private boolean isLockScreen() {
+        KeyguardManager myKM = (KeyguardManager) mContext
+                .getSystemService(Context.KEYGUARD_SERVICE);
+        if (myKM.inKeyguardRestrictedInputMode()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
 }
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index f741e3c..e48bf79 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,8 +17,12 @@
 package android.graphics;
 
 import android.content.res.AssetManager;
+import android.util.Log;
 
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
 import java.util.List;
 
 /**
@@ -27,6 +31,9 @@
  * @hide
  */
 public class FontFamily {
+
+    private static String TAG = "FontFamily";
+
     /**
      * @hide
      */
@@ -62,7 +69,15 @@
     }
 
     public boolean addFont(String path, int ttcIndex) {
-        return nAddFont(mNativePtr, path, ttcIndex);
+        try (FileInputStream file = new FileInputStream(path)) {
+            FileChannel fileChannel = file.getChannel();
+            long fontSize = fileChannel.size();
+            ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
+            return nAddFont(mNativePtr, fontBuffer, ttcIndex);
+        } catch (IOException e) {
+            Log.e(TAG, "Error mapping font file " + path);
+            return false;
+        }
     }
 
     public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontListParser.Axis> axes,
@@ -76,7 +91,7 @@
 
     private static native long nCreateFamily(String lang, int variant);
     private static native void nUnrefFamily(long nativePtr);
-    private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex);
+    private static native boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex);
     private static native boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
             int ttcIndex, List<FontListParser.Axis> listOfAxis,
             int weight, boolean isItalic);
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index 4e9ac9c..7e85333 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -156,6 +156,12 @@
     return;
 }
 
+bool PathParser::isVerbValid(char verb) {
+    verb = tolower(verb);
+    return verb == 'a' || verb == 'c' || verb == 'h' || verb == 'l' || verb == 'm' || verb == 'q'
+            || verb == 's' || verb == 't' || verb == 'v' || verb == 'z';
+}
+
 void PathParser::getPathDataFromString(PathData* data, ParseResult* result,
         const char* pathStr, size_t strLen) {
     if (pathStr == NULL) {
@@ -171,6 +177,12 @@
         end = nextStart(pathStr, strLen, end);
         std::vector<float> points;
         getFloats(&points, result, pathStr, start, end);
+        if (!isVerbValid(pathStr[start])) {
+            result->failureOccurred = true;
+            result->failureMessage = "Invalid pathData. Failure occurred at position "
+                    + std::to_string(start) + " of path: " + pathStr;
+        }
+        // If either verb or points is not valid, return immediately.
         if (result->failureOccurred) {
             return;
         }
@@ -182,10 +194,15 @@
     }
 
     if ((end - start) == 1 && start < strLen) {
+        if (!isVerbValid(pathStr[start])) {
+            result->failureOccurred = true;
+            result->failureMessage = "Invalid pathData. Failure occurred at position "
+                    + std::to_string(start) + " of path: " + pathStr;
+            return;
+        }
         data->verbs.push_back(pathStr[start]);
         data->verbSizes.push_back(0);
     }
-    return;
 }
 
 void PathParser::dump(const PathData& data) {
@@ -218,7 +235,8 @@
     // Check if there is valid data coming out of parsing the string.
     if (pathData.verbs.size() == 0) {
         result->failureOccurred = true;
-        result->failureMessage = "No verbs found in the string for pathData";
+        result->failureMessage = "No verbs found in the string for pathData: ";
+        result->failureMessage += pathStr;
         return;
     }
     VectorDrawableUtils::verbsToPath(skPath, pathData);
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index c4bbb74..180a7a3 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -44,6 +44,7 @@
     ANDROID_API static void getPathDataFromString(PathData* outData, ParseResult* result,
             const char* pathStr, size_t strLength);
     static void dump(const PathData& data);
+    static bool isVerbValid(char verb);
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index b9e3358..b39de26 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -22,8 +22,9 @@
 
 namespace android {
 
-MinikinFontSkia::MinikinFontSkia(SkTypeface *typeface) :
-    mTypeface(typeface) {
+MinikinFontSkia::MinikinFontSkia(SkTypeface* typeface, const void* fontData, size_t fontSize,
+        int ttcIndex) :
+    mTypeface(typeface), mFontData(fontData), mFontSize(fontSize), mTtcIndex(ttcIndex) {
 }
 
 MinikinFontSkia::~MinikinFontSkia() {
@@ -66,22 +67,38 @@
     bounds->mBottom = skBounds.fBottom;
 }
 
-bool MinikinFontSkia::GetTable(uint32_t tag, uint8_t *buf, size_t *size) {
-    if (buf == NULL) {
-        const size_t tableSize = mTypeface->getTableSize(tag);
-        *size = tableSize;
-        return tableSize != 0;
-    } else {
-        const size_t actualSize = mTypeface->getTableData(tag, 0, *size, buf);
-        *size = actualSize;
-        return actualSize != 0;
+const void* MinikinFontSkia::GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) {
+    // we don't have a buffer to the font data, copy to own buffer
+    const size_t tableSize = mTypeface->getTableSize(tag);
+    *size = tableSize;
+    if (tableSize == 0) {
+        return nullptr;
     }
+    void* buf = malloc(tableSize);
+    if (buf == nullptr) {
+        return nullptr;
+    }
+    mTypeface->getTableData(tag, 0, tableSize, buf);
+    *destroy = free;
+    return buf;
 }
 
 SkTypeface *MinikinFontSkia::GetSkTypeface() const {
     return mTypeface;
 }
 
+const void* MinikinFontSkia::GetFontData() const {
+    return mFontData;
+}
+
+size_t MinikinFontSkia::GetFontSize() const {
+    return mFontSize;
+}
+
+int MinikinFontSkia::GetFontIndex() const {
+    return mTtcIndex;
+}
+
 int32_t MinikinFontSkia::GetUniqueId() const {
     return mTypeface->uniqueID();
 }
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index 1d50168..dbc65f9 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -28,7 +28,8 @@
 class ANDROID_API MinikinFontSkia : public MinikinFont {
 public:
     // Note: this takes ownership of the reference (will unref on dtor)
-    explicit MinikinFontSkia(SkTypeface *typeface);
+    explicit MinikinFontSkia(SkTypeface *typeface, const void* fontData, size_t fontSize,
+        int ttcIndex);
 
     ~MinikinFontSkia();
 
@@ -38,20 +39,30 @@
     void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
         const MinikinPaint &paint) const;
 
-    // If buf is NULL, just update size
-    bool GetTable(uint32_t tag, uint8_t *buf, size_t *size);
+    const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy);
 
     int32_t GetUniqueId() const;
 
     SkTypeface* GetSkTypeface() const;
 
+    // Access to underlying raw font bytes
+    const void* GetFontData() const;
+    size_t GetFontSize() const;
+    int GetFontIndex() const;
+
     static uint32_t packPaintFlags(const SkPaint* paint);
     static void unpackPaintFlags(SkPaint* paint, uint32_t paintFlags);
 
     // set typeface and fake bold/italic parameters
     static void populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery);
 private:
-    SkTypeface *mTypeface;
+    SkTypeface* mTypeface;
+
+    // A raw pointer to the font data - it should be owned by some other object with
+    // lifetime at least as long as this object.
+    const void* mFontData;
+    size_t mFontSize;
+    int mTtcIndex;
 };
 
 }  // namespace android
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index fa8ad5d..c583988 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -66,7 +66,10 @@
         ALOGD("makeFontCollection adding %s", fn);
         SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
         if (skFace != NULL) {
-            MinikinFont *font = new MinikinFontSkia(skFace);
+            // TODO: might be a nice optimization to get access to the underlying font
+            // data, but would require us opening the file ourselves and passing that
+            // to the appropriate Create method of SkTypeface.
+            MinikinFont *font = new MinikinFontSkia(skFace, NULL, 0, 0);
             family->addFont(font);
             font->Unref();
         } else {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ab66b2a..890d4a1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -113,18 +113,11 @@
         mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
         mHaveNewSurface = true;
         mSwapHistory.clear();
-        makeCurrent();
     } else {
         mRenderThread.removeFrameCallback(this);
     }
 }
 
-void CanvasContext::requireSurface() {
-    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
-            "requireSurface() called but no surface set!");
-    makeCurrent();
-}
-
 void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
     mSwapBehavior = swapBehavior;
 }
@@ -146,6 +139,18 @@
     return mRenderThread.removeFrameCallback(this);
 }
 
+void CanvasContext::setStopped(bool stopped) {
+    if (mStopped != stopped) {
+        mStopped = stopped;
+        if (mStopped) {
+            mRenderThread.removeFrameCallback(this);
+            if (mEglManager.isCurrent(mEglSurface)) {
+                mEglManager.makeCurrent(EGL_NO_SURFACE);
+            }
+        }
+    }
+}
+
 // TODO: don't pass viewport size, it's automatic via EGL
 void CanvasContext::setup(int width, int height, float lightRadius,
         uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
@@ -172,7 +177,9 @@
     mOpaque = opaque;
 }
 
-void CanvasContext::makeCurrent() {
+bool CanvasContext::makeCurrent() {
+    if (mStopped) return false;
+
     // TODO: Figure out why this workaround is needed, see b/13913604
     // In the meantime this matches the behavior of GLRenderer, so it is not a regression
     EGLint error = 0;
@@ -180,6 +187,7 @@
     if (error) {
         setSurface(nullptr);
     }
+    return !error;
 }
 
 static bool wasSkipped(FrameInfo* info) {
@@ -671,7 +679,7 @@
 }
 
 Layer* CanvasContext::createTextureLayer() {
-    requireSurface();
+    mEglManager.initialize();
     return LayerRenderer::createTextureLayer(mRenderThread.renderState());
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 9350114..52df3abe 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -82,13 +82,14 @@
     void initialize(Surface* surface);
     void updateSurface(Surface* surface);
     bool pauseSurface(Surface* surface);
+    void setStopped(bool stopped);
     bool hasSurface() { return mNativeSurface.get(); }
 
     void setup(int width, int height, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     void setLightCenter(const Vector3& lightCenter);
     void setOpaque(bool opaque);
-    void makeCurrent();
+    bool makeCurrent();
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
             int64_t syncQueued, RenderNode* target);
     void draw();
@@ -172,7 +173,6 @@
     friend class android::uirenderer::RenderState;
 
     void setSurface(Surface* window);
-    void requireSurface();
 
     void freePrefetchedLayers(TreeObserver* observer);
 
@@ -185,6 +185,7 @@
     EglManager& mEglManager;
     sp<Surface> mNativeSurface;
     EGLSurface mEglSurface = EGL_NO_SURFACE;
+    bool mStopped = false;
     bool mBufferPreserved = false;
     SwapBehavior mSwapBehavior = kSwap_default;
     struct SwapHistory {
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 651aaa2..ed472ac 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -115,7 +115,7 @@
     ATRACE_CALL();
     int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
     mRenderThread->timeLord().vsyncReceived(vsync);
-    mContext->makeCurrent();
+    bool canDraw = mContext->makeCurrent();
     Caches::getInstance().textureCache.resetMarkInUse(mContext);
 
     for (size_t i = 0; i < mLayers.size(); i++) {
@@ -126,8 +126,9 @@
 
     // This is after the prepareTree so that any pending operations
     // (RenderNode tree state, prefetched layers, etc...) will be flushed.
-    if (CC_UNLIKELY(!mContext->hasSurface())) {
+    if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
         mSyncResult |= kSync_LostSurfaceRewardIfFound;
+        info.out.canDrawThisFrame = false;
     }
 
     if (info.out.hasAnimations) {
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 8def7ad..ac6a28f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -270,12 +270,6 @@
         // Ensure we always have a valid surface & context
         surface = mPBufferSurface;
     }
-    // TODO: Temporary to help diagnose b/27286867
-    if (mCurrentSurface == mPBufferSurface || surface == mPBufferSurface) {
-        ALOGD("Switching from surface %p%s to %p%s", mCurrentSurface,
-                mCurrentSurface == mPBufferSurface ? " (pbuffer)" : "",
-                        surface, surface == mPBufferSurface ? " (pbuffer)" : "");
-    }
     if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
         if (errOut) {
             *errOut = eglGetError();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 2e99d0b..c5a2dc7 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -52,14 +52,6 @@
     MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
     ARGS(method) *args = (ARGS(method) *) task->payload()
 
-namespace DumpFlags {
-    enum {
-        FrameStats = 1 << 0,
-        Reset      = 1 << 1,
-        JankStats  = 1 << 2,
-    };
-};
-
 CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
         RenderNode* rootRenderNode, IContextFactory* contextFactory) {
     return new CanvasContext(*args->thread, args->translucent,
@@ -175,6 +167,18 @@
     return (bool) postAndWait(task);
 }
 
+CREATE_BRIDGE2(setStopped, CanvasContext* context, bool stopped) {
+    args->context->setStopped(args->stopped);
+    return nullptr;
+}
+
+void RenderProxy::setStopped(bool stopped) {
+    SETUP_TASK(setStopped);
+    args->context = mContext;
+    args->stopped = stopped;
+    postAndWait(task);
+}
+
 CREATE_BRIDGE6(setup, CanvasContext* context, int width, int height,
         float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
     args->context->setup(args->width, args->height, args->lightRadius,
@@ -425,6 +429,9 @@
     if (args->dumpFlags & DumpFlags::Reset) {
         args->context->resetFrameStats();
     }
+    if (args->dumpFlags & DumpFlags::JankStats) {
+        args->thread->jankTracker().dump(args->fd);
+    }
     return nullptr;
 }
 
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 97194fe..32d3283 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -50,6 +50,14 @@
 class RenderThread;
 class RenderProxyBridge;
 
+namespace DumpFlags {
+    enum {
+        FrameStats = 1 << 0,
+        Reset      = 1 << 1,
+        JankStats  = 1 << 2,
+    };
+};
+
 /*
  * RenderProxy is strictly single threaded. All methods must be invoked on the owning
  * thread. It is important to note that RenderProxy may be deleted while it has
@@ -71,6 +79,7 @@
     ANDROID_API void initialize(const sp<Surface>& surface);
     ANDROID_API void updateSurface(const sp<Surface>& surface);
     ANDROID_API bool pauseSurface(const sp<Surface>& surface);
+    ANDROID_API void setStopped(bool stopped);
     ANDROID_API void setup(int width, int height, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     ANDROID_API void setLightCenter(const Vector3& lightCenter);
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index cc0fdd5..c5af061 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -122,5 +122,5 @@
         }
     }
 
-    proxy->dumpProfileInfo(STDOUT_FILENO, 0);
+    proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats);
 }
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 7208547..796169e 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -231,11 +231,12 @@
 };
 
 const StringPath sStringPaths[] = {
-    {"3e...3", false},
-    {"L.M.F.A.O", false},
-    {"m 1 1", true},
-    {"z", true},
-    {"1-2e34567", false}
+    {"3e...3", false}, // Not starting with a verb and ill-formatted float
+    {"L.M.F.A.O", false}, // No floats following verbs
+    {"m 1 1", true}, // Valid path data
+    {"z", true}, // Valid path data
+    {"1-2e34567", false}, // Not starting with a verb and ill-formatted float
+    {"f 4 5", false} // Invalid verb
 };
 
 
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index eb96015..7c8806e 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
 #define LOG_TAG "AppFuseJNI"
 #include "utils/Log.h"
 
@@ -451,7 +450,7 @@
     ScopedFd fd(static_cast<int>(jfd));
     AppFuse appfuse(env, self);
 
-    ALOGD("Start fuse loop.");
+    ALOGV("Start fuse loop.");
     while (true) {
         FuseRequest request;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 586f269..ca0b86a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -15,7 +15,7 @@
 import android.os.BatteryManager;
 import android.os.UserManager;
 import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.drawable.UserIconDrawable;
 
 import java.text.NumberFormat;
 
@@ -73,21 +73,22 @@
     /**
      * Returns a circular icon for a user.
      */
-    public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
+    public static UserIconDrawable getUserIcon(Context context, UserManager um, UserInfo user) {
+        final int iconSize = UserIconDrawable.getSizeForList(context);
         if (user.isManagedProfile()) {
             // We use predefined values for managed profiles
             Bitmap b = BitmapFactory.decodeResource(context.getResources(),
                     com.android.internal.R.drawable.ic_corp_icon);
-            return CircleFramedDrawable.getInstance(context, b);
+            return new UserIconDrawable(iconSize).setIcon(b).bake();
         }
         if (user.iconPath != null) {
             Bitmap icon = um.getUserIcon(user.id);
             if (icon != null) {
-                return CircleFramedDrawable.getInstance(context, icon);
+                return new UserIconDrawable(iconSize).setIcon(icon).bake();
             }
         }
-        return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
-                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
+        return new UserIconDrawable(iconSize).setIconDrawable(
+                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)).bake();
     }
 
     /** Formats the ratio of amount/total as a percentage. */
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
new file mode 100644
index 0000000..32478a7
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -0,0 +1,428 @@
+/*
+ * 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.settingslib.drawable;
+
+import android.annotation.NonNull;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.R;
+
+/**
+ * Converts the user avatar icon to a circularly clipped one with an optional badge and frame
+ */
+public class UserIconDrawable extends Drawable implements Drawable.Callback {
+
+    private Drawable mUserDrawable;
+    private Bitmap mUserIcon;
+    private Bitmap mBitmap; // baked representation. Required for transparent border around badge
+    private final Paint mIconPaint = new Paint();
+    private final Paint mPaint = new Paint();
+    private final Matrix mIconMatrix = new Matrix();
+    private float mIntrinsicRadius;
+    private float mDisplayRadius;
+    private float mPadding = 0;
+    private int mSize = 0; // custom "intrinsic" size for this drawable if non-zero
+    private boolean mInvalidated = true;
+    private ColorStateList mTintColor = null;
+    private PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_ATOP;
+
+    private float mFrameWidth;
+    private float mFramePadding;
+    private ColorStateList mFrameColor = null;
+    private Paint mFramePaint;
+
+    private Drawable mBadge;
+    private Paint mClearPaint;
+    private float mBadgeRadius;
+    private float mBadgeMargin;
+
+    /**
+     * Gets the system default managed-user badge as a drawable
+     * @param context
+     * @return drawable containing just the badge
+     */
+    public static Drawable getManagedUserBadgeDrawable(Context context) {
+        int displayDensity = context.getResources().getDisplayMetrics().densityDpi;
+        return context.getResources().getDrawableForDensity(
+                com.android.internal.R.drawable.ic_corp_user_badge,
+                displayDensity, context.getTheme());
+    }
+
+    /**
+     * Gets the preferred list-item size of this drawable.
+     * @param context
+     * @return size in pixels
+     */
+    public static int getSizeForList(Context context) {
+        return (int) context.getResources().getDimension(R.dimen.circle_avatar_size);
+    }
+
+    public UserIconDrawable() {
+        this(0);
+    }
+
+    /**
+     * Use this constructor if the drawable is intended to be placed in listviews
+     * @param intrinsicSize if 0, the intrinsic size will come from the icon itself
+     */
+    public UserIconDrawable(int intrinsicSize) {
+        super();
+        mIconPaint.setAntiAlias(true);
+        mIconPaint.setFilterBitmap(true);
+        mPaint.setFilterBitmap(true);
+        mPaint.setAntiAlias(true);
+        if (intrinsicSize > 0) {
+            setBounds(0, 0, intrinsicSize, intrinsicSize);
+            setIntrinsicSize(intrinsicSize);
+        }
+        setIcon(null);
+    }
+
+    public UserIconDrawable setIcon(Bitmap icon) {
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+            mUserDrawable = null;
+        }
+        mUserIcon = icon;
+        if (mUserIcon == null) {
+            mIconPaint.setShader(null);
+            mBitmap = null;
+        } else {
+            mIconPaint.setShader(new BitmapShader(icon, Shader.TileMode.CLAMP,
+                    Shader.TileMode.CLAMP));
+        }
+        onBoundsChange(getBounds());
+        return this;
+    }
+
+    public UserIconDrawable setIconDrawable(Drawable icon) {
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+        }
+        mUserIcon = null;
+        mUserDrawable = icon;
+        if (mUserDrawable == null) {
+            mBitmap = null;
+        } else {
+            mUserDrawable.setCallback(this);
+        }
+        onBoundsChange(getBounds());
+        return this;
+    }
+
+    public UserIconDrawable setBadge(Drawable badge) {
+        mBadge = badge;
+        if (mBadge != null) {
+            if (mClearPaint == null) {
+                mClearPaint = new Paint();
+                mClearPaint.setAntiAlias(true);
+                mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+                mClearPaint.setStyle(Paint.Style.FILL);
+            }
+            // update metrics
+            onBoundsChange(getBounds());
+        } else {
+            invalidateSelf();
+        }
+        return this;
+    }
+
+    public UserIconDrawable setBadgeIfManagedUser(Context context, int userId) {
+        Drawable badge = null;
+        boolean isManaged = context.getSystemService(DevicePolicyManager.class)
+                .getProfileOwnerAsUser(userId) != null;
+        if (isManaged) {
+            badge = getManagedUserBadgeDrawable(context);
+        }
+        return setBadge(badge);
+    }
+
+    public void setBadgeRadius(float radius) {
+        mBadgeRadius = radius;
+        onBoundsChange(getBounds());
+    }
+
+    public void setBadgeMargin(float margin) {
+        mBadgeMargin = margin;
+        onBoundsChange(getBounds());
+    }
+
+    /**
+     * Sets global padding of icon/frame. Doesn't effect the badge.
+     * @param padding
+     */
+    public void setPadding(float padding) {
+        mPadding = padding;
+        onBoundsChange(getBounds());
+    }
+
+    private void initFramePaint() {
+        if (mFramePaint == null) {
+            mFramePaint = new Paint();
+            mFramePaint.setStyle(Paint.Style.STROKE);
+            mFramePaint.setAntiAlias(true);
+        }
+    }
+
+    public void setFrameWidth(float width) {
+        initFramePaint();
+        mFrameWidth = width;
+        mFramePaint.setStrokeWidth(width);
+        onBoundsChange(getBounds());
+    }
+
+    public void setFramePadding(float padding) {
+        initFramePaint();
+        mFramePadding = padding;
+        onBoundsChange(getBounds());
+    }
+
+    public void setFrameColor(int color) {
+        initFramePaint();
+        mFramePaint.setColor(color);
+        invalidateSelf();
+    }
+
+    public void setFrameColor(ColorStateList colorList) {
+        initFramePaint();
+        mFrameColor = colorList;
+        invalidateSelf();
+    }
+
+    /**
+     * This sets the "intrinsic" size of this drawable. Useful for views which use the drawable's
+     * intrinsic size for layout. It is independent of the bounds.
+     * @param size if 0, the intrinsic size will be set to the displayed icon's size
+     */
+    public void setIntrinsicSize(int size) {
+        mSize = size;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mInvalidated) {
+            rebake();
+        }
+        if (mBitmap != null) {
+            if (mTintColor == null) {
+                mPaint.setColorFilter(null);
+            } else {
+                int color = mTintColor.getColorForState(getState(), mTintColor.getDefaultColor());
+                if (mPaint.getColorFilter() == null) {
+                    mPaint.setColorFilter(new PorterDuffColorFilter(color, mTintMode));
+                } else {
+                    ((PorterDuffColorFilter) mPaint.getColorFilter()).setMode(mTintMode);
+                    ((PorterDuffColorFilter) mPaint.getColorFilter()).setColor(color);
+                }
+            }
+
+            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+        super.invalidateSelf();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+    }
+
+    @Override
+    public void setTintList(ColorStateList tintList) {
+        mTintColor = tintList;
+        super.invalidateSelf();
+    }
+
+    @Override
+    public void setTintMode(@NonNull PorterDuff.Mode mode) {
+        mTintMode = mode;
+        super.invalidateSelf();
+    }
+
+    /**
+     * This 'bakes' the current state of this icon into a bitmap and removes/recycles the source
+     * bitmap/drawable. Use this when no more changes will be made and an intrinsic size is set.
+     * This effectively turns this into a static drawable.
+     */
+    public UserIconDrawable bake() {
+        if (mSize <= 0) {
+            throw new IllegalStateException("Baking requires an explicit intrinsic size");
+        }
+        onBoundsChange(new Rect(0, 0, mSize, mSize));
+        rebake();
+        mFrameColor = null;
+        mFramePaint = null;
+        mClearPaint = null;
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+            mUserDrawable = null;
+        } else if (mUserIcon != null) {
+            mUserIcon.recycle();
+            mUserIcon = null;
+        }
+        return this;
+    }
+
+    private void rebake() {
+        mInvalidated = false;
+
+        if (mBitmap == null || (mUserDrawable == null && mUserIcon == null)) {
+            return;
+        }
+
+        final Canvas canvas = new Canvas(mBitmap);
+        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+        if(mUserDrawable != null) {
+            mUserDrawable.draw(canvas);
+        } else if (mUserIcon != null) {
+            int saveId = canvas.save();
+            canvas.concat(mIconMatrix);
+            canvas.drawCircle(mUserIcon.getWidth() * 0.5f, mUserIcon.getHeight() * 0.5f,
+                    mIntrinsicRadius, mIconPaint);
+            canvas.restoreToCount(saveId);
+        }
+
+        if (mFrameColor != null) {
+            mFramePaint.setColor(mFrameColor.getColorForState(getState(), Color.TRANSPARENT));
+        }
+        if ((mFrameWidth + mFramePadding) > 0.001f) {
+            float radius = mDisplayRadius - mPadding - mFrameWidth * 0.5f;
+            canvas.drawCircle(getBounds().exactCenterX(), getBounds().exactCenterY(),
+                    radius, mFramePaint);
+        }
+
+        if ((mBadge != null) && (mBadgeRadius > 0.001f)) {
+            final float badgeDiameter = mBadgeRadius * 2f;
+            final float badgeTop = mBitmap.getHeight() - badgeDiameter;
+            float badgeLeft = mBitmap.getWidth() - badgeDiameter;
+
+            mBadge.setBounds((int) badgeLeft, (int) badgeTop,
+                    (int) (badgeLeft + badgeDiameter), (int) (badgeTop + badgeDiameter));
+
+            final float borderRadius = mBadge.getBounds().width() * 0.5f + mBadgeMargin;
+            canvas.drawCircle(badgeLeft + mBadgeRadius, badgeTop + mBadgeRadius,
+                    borderRadius, mClearPaint);
+
+            mBadge.draw(canvas);
+        }
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        if (bounds.isEmpty() || (mUserIcon == null && mUserDrawable == null)) {
+            return;
+        }
+
+        // re-create bitmap if applicable
+        float newDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f;
+        int size = (int) (newDisplayRadius * 2);
+        if (mBitmap == null || size != ((int) (mDisplayRadius * 2))) {
+            mDisplayRadius = newDisplayRadius;
+            if (mBitmap != null) {
+                mBitmap.recycle();
+            }
+            mBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        }
+
+        // update metrics
+        mDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f;
+        final float iconRadius = mDisplayRadius - mFrameWidth - mFramePadding - mPadding;
+        RectF dstRect = new RectF(bounds.exactCenterX() - iconRadius,
+                                  bounds.exactCenterY() - iconRadius,
+                                  bounds.exactCenterX() + iconRadius,
+                                  bounds.exactCenterY() + iconRadius);
+        if (mUserDrawable != null) {
+            Rect rounded = new Rect();
+            dstRect.round(rounded);
+            mIntrinsicRadius = Math.min(mUserDrawable.getIntrinsicWidth(),
+                                        mUserDrawable.getIntrinsicHeight()) * 0.5f;
+            mUserDrawable.setBounds(rounded);
+        } else if (mUserIcon != null) {
+            // Build square-to-square transformation matrix
+            final float iconCX = mUserIcon.getWidth() * 0.5f;
+            final float iconCY = mUserIcon.getHeight() * 0.5f;
+            mIntrinsicRadius = Math.min(iconCX, iconCY);
+            RectF srcRect = new RectF(iconCX - mIntrinsicRadius, iconCY - mIntrinsicRadius,
+                                      iconCX + mIntrinsicRadius, iconCY + mIntrinsicRadius);
+            mIconMatrix.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.FILL);
+        }
+
+        invalidateSelf();
+    }
+
+    @Override
+    public void invalidateSelf() {
+        super.invalidateSelf();
+        mInvalidated = true;
+    }
+
+    @Override
+    public boolean isStateful() {
+        return mFrameColor != null && mFrameColor.isStateful();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return (mSize <= 0 ? (int) mIntrinsicRadius * 2 : mSize);
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return getIntrinsicWidth();
+    }
+
+    @Override
+    public void invalidateDrawable(@NonNull Drawable who) {
+        invalidateSelf();
+    }
+
+    @Override
+    public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+        scheduleSelf(what, when);
+    }
+
+    @Override
+    public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+        unscheduleSelf(what);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
index f9fa805f..750601d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
@@ -33,7 +33,7 @@
 import android.widget.SpinnerAdapter;
 import android.widget.TextView;
 import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.drawable.UserIconDrawable;
 
 import com.android.settingslib.R;
 
@@ -71,7 +71,8 @@
         }
 
         private static Drawable encircle(Context context, Drawable icon) {
-            return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(icon));
+            return new UserIconDrawable(UserIconDrawable.getSizeForList(context))
+                    .setIconDrawable(icon).bake();
         }
     }
     private ArrayList<UserDetails> data;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 61c9f2b..5621642 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1942,7 +1942,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 126;
+            private static final int SETTINGS_VERSION = 127;
 
             private final int mUserId;
 
@@ -2167,6 +2167,36 @@
                     currentVersion = 126;
                 }
 
+                if (currentVersion == 126) {
+                    // Version 126: copy the primary values of LOCK_SCREEN_SHOW_NOTIFICATIONS and
+                    // LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS into managed profile.
+                    if (mUserManager.isManagedProfile(userId)) {
+                        final SettingsState systemSecureSettings =
+                                getSecureSettingsLocked(UserHandle.USER_SYSTEM);
+
+                        final Setting showNotifications = systemSecureSettings.getSettingLocked(
+                                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+                        if (showNotifications != null) {
+                            final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                            secureSettings.insertSettingLocked(
+                                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                                    showNotifications.getValue(),
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+
+                        final Setting allowPrivate = systemSecureSettings.getSettingLocked(
+                                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+                        if (allowPrivate != null) {
+                            final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                            secureSettings.insertSettingLocked(
+                                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                                    allowPrivate.getValue(),
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+                    currentVersion = 127;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 // Return the current version.
diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
new file mode 100644
index 0000000..20251c2
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
@@ -0,0 +1,22 @@
+<?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
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true" android:color="@color/current_user_border_color" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
new file mode 100644
index 0000000..2b75c36
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
@@ -0,0 +1,22 @@
+<?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
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index 9c2c0ab..3865020 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -22,8 +22,17 @@
         android:paddingStart="24dp"
         android:paddingEnd="24dp"
         android:paddingBottom="8dp">
+    <ImageView
+            android:id="@+id/keyboard_shortcuts_icon"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_marginEnd="32dp"
+            android:layout_gravity="center_vertical"
+            android:visibility="gone"
+            android:layout_alignParentStart="true"/>
     <TextView
             android:id="@+id/keyboard_shortcuts_keyword"
+            android:layout_toEndOf="@+id/keyboard_shortcuts_icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:paddingEnd="12dp"
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index c6e453a..d685528 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -33,14 +33,18 @@
     <TextView android:id="@+id/user_name"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="16dp"
+            android:layout_marginEnd="13dp"
             android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName"
             />
     <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
-            android:layout_width="@dimen/max_avatar_size"
-            android:layout_height="@dimen/max_avatar_size"
+            android:layout_width="@dimen/framed_avatar_size"
+            android:layout_height="@dimen/framed_avatar_size"
             android:contentDescription="@null"
+            android:backgroundTint="@color/qs_user_detail_avatar_tint"
+            android:backgroundTintMode="src_atop"
             sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
-            sysui:framePadding="6dp"
-            sysui:activeFrameColor="@color/current_user_border_color" />
+            sysui:framePadding="2.5dp"
+            sysui:badgeDiameter="18dp"
+            sysui:badgeMargin="1dp"
+            sysui:frameColor="@color/qs_user_detail_avatar_frame" />
 </com.android.systemui.qs.tiles.UserDetailItemView>
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 661d74a..58fc069 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -33,12 +33,16 @@
 
     <com.android.systemui.statusbar.phone.UserAvatarView
             android:id="@+id/user_picture"
-            android:layout_width="@dimen/max_avatar_size"
-            android:layout_height="@dimen/max_avatar_size"
-            android:layout_marginBottom="10dp"
+            android:layout_width="@dimen/framed_avatar_size"
+            android:layout_height="@dimen/framed_avatar_size"
+            android:layout_marginBottom="7dp"
+            android:backgroundTint="@color/qs_user_detail_avatar_tint"
+            android:backgroundTintMode="src_atop"
             systemui:frameWidth="2dp"
-            systemui:framePadding="6dp"
-            systemui:activeFrameColor="@color/current_user_border_color"/>
+            systemui:framePadding="2.5dp"
+            systemui:badgeDiameter="18dp"
+            systemui:badgeMargin="1dp"
+            systemui:frameColor="@color/qs_user_detail_avatar_frame"/>
 
     <LinearLayout
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 6dd8c52..1543360 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -53,10 +53,14 @@
         <enum name="vertical" value="1" />
     </attr>
     <declare-styleable name="UserAvatarView">
+        <attr name="avatarPadding" format="dimension" />
         <attr name="frameWidth" format="dimension" />
         <attr name="framePadding" format="dimension" />
+        <!-- {@deprecated Use a statelist in frameColor instead.} -->
         <attr name="activeFrameColor" format="color" />
-        <attr name="frameColor" />
+        <attr name="frameColor" format="color" />
+        <attr name="badgeDiameter" format="dimension" />
+        <attr name="badgeMargin" format="dimension" />
     </declare-styleable>
     <declare-styleable name="UserDetailItemView">
         <attr name="regularFontFamily" format="string" />
diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml
index 22b7578..40e3b12 100644
--- a/packages/SystemUI/res/values/config_tv.xml
+++ b/packages/SystemUI/res/values/config_tv.xml
@@ -16,6 +16,10 @@
 
 <resources>
     <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+         when the PIP menu is shown with settings. -->
+    <string translatable="false" name="pip_settings_bounds">"662 54 1142 324"</string>
+
+    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
          when the PIP menu is shown in center. -->
     <string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a4eadbf..cf2e338 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -388,6 +388,9 @@
          quick settings header -->
     <dimen name="max_avatar_size">48dp</dimen>
 
+    <!-- Size of user icon + frame in the qs/keyguard user picker (incl. frame) -->
+    <dimen name="framed_avatar_size">54dp</dimen>
+
     <!-- Margin on the left side of the carrier text on Keyguard -->
     <dimen name="keyguard_carrier_text_margin">16dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java b/packages/SystemUI/src/com/android/systemui/BitmapHelper.java
deleted file mode 100644
index 1933bbc..0000000
--- a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.graphics.Shader;
-
-public class BitmapHelper {
-    /**
-     * Generate a new bitmap (width x height pixels, ARGB_8888) with the input bitmap scaled
-     * to fit and clipped to an inscribed circle.
-     * @param input Bitmap to resize and clip
-     * @param width Width of output bitmap (and diameter of circle)
-     * @param height Height of output bitmap
-     * @return A shiny new bitmap for you to use
-     */
-    public static Bitmap createCircularClip(Bitmap input, int width, int height) {
-        if (input == null) return null;
-
-        final int inWidth = input.getWidth();
-        final int inHeight = input.getHeight();
-        final Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        final Canvas canvas = new Canvas(output);
-        final Paint paint = new Paint();
-        paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
-        paint.setAntiAlias(true);
-        final RectF srcRect = new RectF(0, 0, inWidth, inHeight);
-        final RectF dstRect = new RectF(0, 0, width, height);
-        final Matrix m = new Matrix();
-        m.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.CENTER);
-        canvas.setMatrix(m);
-        canvas.drawCircle(inWidth / 2, inHeight / 2, inWidth / 2, paint);
-        return output;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index fcf758b..99eae02 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -30,6 +30,7 @@
 import android.widget.TextView;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.UserAvatarView;
@@ -87,25 +88,29 @@
         return (UserDetailItemView) convertView;
     }
 
-    public void bind(String name, Bitmap picture) {
+    public void bind(String name, Bitmap picture, int userId) {
         mName.setText(name);
-        mAvatar.setBitmap(picture);
+        mAvatar.setAvatarWithBadge(picture, userId);
     }
 
-    public void bind(String name, Drawable picture) {
+    public void bind(String name, Drawable picture, int userId) {
         mName.setText(name);
-        mAvatar.setDrawable(picture);
+        mAvatar.setDrawableWithBadge(picture, userId);
+    }
+
+    public void setAvatarEnabled(boolean enabled) {
+        mAvatar.setEnabled(enabled);
     }
 
     public void setDisabledByAdmin(boolean disabled) {
         mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE);
         mName.setEnabled(!disabled);
-        mAvatar.setDisabled(disabled);
+        mAvatar.setEnabled(!disabled);
     }
 
     public void setEnabled(boolean enabled) {
         mName.setEnabled(enabled);
-        mAvatar.setDisabled(!enabled);
+        mAvatar.setEnabled(enabled);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index da98762..d4fa765 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -76,9 +76,9 @@
             }
             String name = getName(mContext, item);
             if (item.picture == null) {
-                v.bind(name, getDrawable(mContext, item));
+                v.bind(name, getDrawable(mContext, item), item.resolveId());
             } else {
-                v.bind(name, item.picture);
+                v.bind(name, item.picture, item.info.id);
             }
             v.setActivated(item.isCurrent);
             v.setDisabledByAdmin(item.isDisabledByAdmin);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7ca7d12..e25f9de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -618,6 +618,19 @@
         return mOnKeyguard;
     }
 
+    public void removeAllChildren() {
+        List<ExpandableNotificationRow> notificationChildren
+                = mChildrenContainer.getNotificationChildren();
+        ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
+        for (int i = 0; i < clonedList.size(); i++) {
+            ExpandableNotificationRow row = clonedList.get(i);
+            mChildrenContainer.removeNotification(row);
+            mHeaderUtil.restoreNotificationHeader(row);
+            row.setIsChildInGroup(false, null);
+        }
+        onChildrenCountChanged();
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 977a77d..fff1491 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -17,16 +17,24 @@
 package com.android.systemui.statusbar;
 
 import android.app.AlertDialog;
+import android.app.AppGlobals;
 import android.app.Dialog;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Icon;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.hardware.input.InputManager;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.ContextThemeWrapper;
@@ -42,8 +50,10 @@
 import android.view.WindowManager.KeyboardShortcutsReceiver;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import com.android.internal.app.AssistUtils;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 
@@ -58,7 +68,6 @@
  */
 public final class KeyboardShortcuts {
     private static final String TAG = KeyboardShortcuts.class.getSimpleName();
-
     private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
     private final SparseArray<String> mModifierNames = new SparseArray<>();
     private final SparseArray<Drawable> mSpecialCharacterDrawables = new SparseArray<>();
@@ -66,6 +75,7 @@
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final Context mContext;
+    private final IPackageManager mPackageManager;
     private final OnClickListener dialogCloseListener =  new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int id) {
             dismissKeyboardShortcutsDialog();
@@ -77,6 +87,7 @@
 
     public KeyboardShortcuts(Context context) {
         this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_Material_Light);
+        this.mPackageManager = AppGlobals.getPackageManager();
         loadResources(context);
     }
 
@@ -254,68 +265,11 @@
                     @Override
                     public void onKeyboardShortcutsReceived(
                             final List<KeyboardShortcutGroup> result) {
-                        KeyboardShortcutGroup systemGroup = new KeyboardShortcutGroup(
-                                mContext.getString(R.string.keyboard_shortcut_group_system), true);
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(R.string.keyboard_shortcut_group_system_home),
-                                KeyEvent.KEYCODE_ENTER, KeyEvent.META_META_ON));
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(R.string.keyboard_shortcut_group_system_back),
-                                KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON));
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(R.string.keyboard_shortcut_group_system_recents),
-                                KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON));
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_system_notifications),
-                                KeyEvent.KEYCODE_N, KeyEvent.META_META_ON));
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_system_shortcuts_helper),
-                                KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON));
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_system_switch_input),
-                                KeyEvent.KEYCODE_SPACE, KeyEvent.META_META_ON));
-                        result.add(systemGroup);
-
-                        KeyboardShortcutGroup applicationGroup = new KeyboardShortcutGroup(
-                                mContext.getString(R.string.keyboard_shortcut_group_applications),
-                                true);
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_assist),
-                                KeyEvent.KEYCODE_UNKNOWN, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_browser),
-                                KeyEvent.KEYCODE_B, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_contacts),
-                                KeyEvent.KEYCODE_C, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_email),
-                                KeyEvent.KEYCODE_E, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_im),
-                                KeyEvent.KEYCODE_T, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_music),
-                                KeyEvent.KEYCODE_P, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_youtube),
-                                KeyEvent.KEYCODE_Y, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_calendar),
-                                KeyEvent.KEYCODE_L, KeyEvent.META_META_ON));
-                        result.add(applicationGroup);
-
+                        result.add(getSystemShortcuts());
+                        final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts();
+                        if (appShortcuts != null) {
+                            result.add(appShortcuts);
+                        }
                         showKeyboardShortcutsDialog(result);
                     }
                 }, deviceId);
@@ -331,6 +285,160 @@
         }
     }
 
+    private KeyboardShortcutGroup getSystemShortcuts() {
+        final KeyboardShortcutGroup systemGroup = new KeyboardShortcutGroup(
+                mContext.getString(R.string.keyboard_shortcut_group_system), true);
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(R.string.keyboard_shortcut_group_system_home),
+                KeyEvent.KEYCODE_ENTER,
+                KeyEvent.META_META_ON));
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(R.string.keyboard_shortcut_group_system_back),
+                KeyEvent.KEYCODE_DEL,
+                KeyEvent.META_META_ON));
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(R.string.keyboard_shortcut_group_system_recents),
+                KeyEvent.KEYCODE_TAB,
+                KeyEvent.META_ALT_ON));
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(
+                        R.string.keyboard_shortcut_group_system_notifications),
+                KeyEvent.KEYCODE_N,
+                KeyEvent.META_META_ON));
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(
+                        R.string.keyboard_shortcut_group_system_shortcuts_helper),
+                KeyEvent.KEYCODE_SLASH,
+                KeyEvent.META_META_ON));
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(
+                        R.string.keyboard_shortcut_group_system_switch_input),
+                KeyEvent.KEYCODE_SPACE,
+                KeyEvent.META_META_ON));
+        return systemGroup;
+    }
+
+    private KeyboardShortcutGroup getDefaultApplicationShortcuts() {
+        final int userId = mContext.getUserId();
+        final KeyboardShortcutGroup applicationGroup = new KeyboardShortcutGroup(
+                mContext.getString(R.string.keyboard_shortcut_group_applications),
+                true);
+
+        // Assist.
+        final AssistUtils assistUtils = new AssistUtils(mContext);
+        final ComponentName assistComponent = assistUtils.getAssistComponentForUser(userId);
+        PackageInfo assistPackageInfo = null;
+        try {
+            assistPackageInfo = mPackageManager.getPackageInfo(
+                    assistComponent.getPackageName(), 0, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "PackageManagerService is dead");
+        }
+
+        if (assistPackageInfo != null) {
+            final Icon assistIcon = Icon.createWithResource(
+                    assistPackageInfo.applicationInfo.packageName,
+                    assistPackageInfo.applicationInfo.icon);
+
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_assist),
+                    assistIcon,
+                    KeyEvent.KEYCODE_UNKNOWN,
+                    KeyEvent.META_META_ON));
+        }
+
+        // Browser.
+        final Icon browserIcon = getIconForIntentCategory(Intent.CATEGORY_APP_BROWSER, userId);
+        if (browserIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_browser),
+                    browserIcon,
+                    KeyEvent.KEYCODE_B,
+                    KeyEvent.META_META_ON));
+        }
+
+
+        // Contacts.
+        final Icon contactsIcon = getIconForIntentCategory(Intent.CATEGORY_APP_CONTACTS, userId);
+        if (contactsIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_contacts),
+                    contactsIcon,
+                    KeyEvent.KEYCODE_C,
+                    KeyEvent.META_META_ON));
+        }
+
+        // Email.
+        final Icon emailIcon = getIconForIntentCategory(Intent.CATEGORY_APP_EMAIL, userId);
+        if (emailIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_email),
+                    emailIcon,
+                    KeyEvent.KEYCODE_E,
+                    KeyEvent.META_META_ON));
+        }
+
+        // Messaging.
+        final Icon messagingIcon = getIconForIntentCategory(Intent.CATEGORY_APP_MESSAGING, userId);
+        if (messagingIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_im),
+                    messagingIcon,
+                    KeyEvent.KEYCODE_T,
+                    KeyEvent.META_META_ON));
+        }
+
+        // Music.
+        final Icon musicIcon = getIconForIntentCategory(Intent.CATEGORY_APP_MUSIC, userId);
+        if (musicIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_music),
+                    musicIcon,
+                    KeyEvent.KEYCODE_P,
+                    KeyEvent.META_META_ON));
+        }
+
+        // Calendar.
+        final Icon calendarIcon = getIconForIntentCategory(Intent.CATEGORY_APP_CALENDAR, userId);
+        if (calendarIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
+                    calendarIcon,
+                    KeyEvent.KEYCODE_L,
+                    KeyEvent.META_META_ON));
+        }
+
+        return applicationGroup.getItems().size() == 0 ? null : applicationGroup;
+    }
+
+    private Icon getIconForIntentCategory(String intentCategory, int userId) {
+        final Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(intentCategory);
+
+        final PackageInfo packageInfo = getPackageInfoForIntent(intent, userId);
+        if (packageInfo != null && packageInfo.applicationInfo.icon != 0) {
+            return Icon.createWithResource(
+                    packageInfo.applicationInfo.packageName,
+                    packageInfo.applicationInfo.icon);
+        }
+        return null;
+    }
+
+    private PackageInfo getPackageInfoForIntent(Intent intent, int userId) {
+        try {
+            ResolveInfo handler;
+            handler = mPackageManager.resolveIntent(
+                    intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), 0, userId);
+            if (handler == null || handler.activityInfo == null) {
+                return null;
+            }
+            return mPackageManager.getPackageInfo(handler.activityInfo.packageName, 0, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "PackageManagerService is dead", e);
+            return null;
+        }
+    }
+
     private void showKeyboardShortcutsDialog(
             final List<KeyboardShortcutGroup> keyboardShortcutGroups) {
         // Need to post on the main thread.
@@ -394,9 +502,23 @@
                 }
                 View shortcutView = inflater.inflate(R.layout.keyboard_shortcut_app_item,
                         shortcutContainer, false);
-                TextView textView = (TextView) shortcutView
+
+                if (info.getIcon() != null) {
+                    ImageView shortcutIcon = (ImageView) shortcutView
+                            .findViewById(R.id.keyboard_shortcuts_icon);
+                    shortcutIcon.setImageIcon(info.getIcon());
+                    shortcutIcon.setVisibility(View.VISIBLE);
+                }
+
+                TextView shortcutKeyword = (TextView) shortcutView
                         .findViewById(R.id.keyboard_shortcuts_keyword);
-                textView.setText(info.getLabel());
+                shortcutKeyword.setText(info.getLabel());
+                if (info.getIcon() != null) {
+                    RelativeLayout.LayoutParams lp =
+                            (RelativeLayout.LayoutParams) shortcutKeyword.getLayoutParams();
+                    lp.removeRule(RelativeLayout.ALIGN_PARENT_START);
+                    shortcutKeyword.setLayoutParams(lp);
+                }
 
                 ViewGroup shortcutItemsContainer = (ViewGroup) shortcutView
                         .findViewById(R.id.keyboard_shortcuts_item_container);
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 bf58592..d036fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1525,6 +1525,9 @@
                 // we are only transfering this notification to its parent, don't generate an animation
                 mStackScroller.setChildTransferInProgress(true);
             }
+            if (remove.isSummaryWithChildren()) {
+                remove.removeAllChildren();
+            }
             mStackScroller.removeView(remove);
             mStackScroller.setChildTransferInProgress(false);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
index 093a827..dc1b35d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -17,69 +17,57 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
 import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.R;
 
 /**
- * A view that displays a user image cropped to a circle with a frame.
+ * A view that displays a user image cropped to a circle with an optional frame.
  */
 public class UserAvatarView extends View {
 
-    private int mActiveFrameColor;
-    private int mFrameColor;
-    private float mFrameWidth;
-    private float mFramePadding;
-    private Bitmap mBitmap;
-    private Drawable mDrawable;
-    private boolean mIsDisabled;
-
-    private final Paint mFramePaint = new Paint();
-    private final Paint mBitmapPaint = new Paint();
-    private final Matrix mDrawMatrix = new Matrix();
-
-    private float mScale = 1;
+    private final UserIconDrawable mDrawable = new UserIconDrawable();
 
     public UserAvatarView(Context context, AttributeSet attrs,
             int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes);
         final int N = a.getIndexCount();
         for (int i = 0; i < N; i++) {
             int attr = a.getIndex(i);
             switch (attr) {
+                case R.styleable.UserAvatarView_avatarPadding:
+                    setAvatarPadding(a.getDimension(attr, 0));
+                    break;
                 case R.styleable.UserAvatarView_frameWidth:
                     setFrameWidth(a.getDimension(attr, 0));
                     break;
                 case R.styleable.UserAvatarView_framePadding:
                     setFramePadding(a.getDimension(attr, 0));
                     break;
-                case R.styleable.UserAvatarView_activeFrameColor:
-                    setActiveFrameColor(a.getColor(attr, 0));
-                    break;
                 case R.styleable.UserAvatarView_frameColor:
-                    setFrameColor(a.getColor(attr, 0));
+                    setFrameColor(a.getColorStateList(attr));
+                    break;
+                case R.styleable.UserAvatarView_badgeDiameter:
+                    setBadgeDiameter(a.getDimension(attr, 0));
+                    break;
+                case R.styleable.UserAvatarView_badgeMargin:
+                    setBadgeMargin(a.getDimension(attr, 0));
                     break;
             }
         }
         a.recycle();
-
-        mFramePaint.setAntiAlias(true);
-        mFramePaint.setStyle(Paint.Style.STROKE);
-        mBitmapPaint.setAntiAlias(true);
+        setBackground(mDrawable);
     }
 
     public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -94,180 +82,61 @@
         this(context, null);
     }
 
+    /**
+     * @deprecated use {@link #setAvatar(Bitmap)} instead.
+     */
+    @Deprecated
     public void setBitmap(Bitmap bitmap) {
-        setDrawable(null);
-        mBitmap = bitmap;
-        if (mBitmap != null) {
-            mBitmapPaint.setShader(new BitmapShader(
-                    bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
-        } else {
-            mBitmapPaint.setShader(null);
-        }
-        configureBounds();
-        invalidate();
+        setAvatar(bitmap);
     }
 
-    public void setFrameColor(int frameColor) {
-        mFrameColor = frameColor;
-        invalidate();
-    }
-
-    public void setActiveFrameColor(int activeFrameColor) {
-        mActiveFrameColor = activeFrameColor;
-        invalidate();
+    public void setFrameColor(ColorStateList color) {
+        mDrawable.setFrameColor(color);
     }
 
     public void setFrameWidth(float frameWidth) {
-        mFrameWidth = frameWidth;
-        invalidate();
+        mDrawable.setFrameWidth(frameWidth);
     }
 
     public void setFramePadding(float framePadding) {
-        mFramePadding = framePadding;
-        invalidate();
+        mDrawable.setFramePadding(framePadding);
     }
 
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        configureBounds();
+    public void setAvatarPadding(float avatarPadding) {
+        mDrawable.setPadding(avatarPadding);
     }
 
-    public void configureBounds() {
-        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
-        int vheight = getHeight() - mPaddingTop - mPaddingBottom;
-
-        int dwidth;
-        int dheight;
-        if (mBitmap != null) {
-            dwidth = mBitmap.getWidth();
-            dheight = mBitmap.getHeight();
-        } else if (mDrawable != null) {
-            vwidth -= 2 * (mFrameWidth - 1);
-            vheight -= 2 * (mFrameWidth - 1);
-            dwidth = vwidth;
-            dheight = vheight;
-            mDrawable.setBounds(0, 0, dwidth, dheight);
-        } else {
-            return;
-        }
-
-        float scale;
-        float dx;
-        float dy;
-
-        scale = Math.min((float) vwidth / (float) dwidth,
-                (float) vheight / (float) dheight);
-
-        dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
-        dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);
-
-        mDrawMatrix.setScale(scale, scale);
-        mDrawMatrix.postTranslate(dx, dy);
-        mScale = scale;
+    public void setBadgeDiameter(float diameter) {
+        mDrawable.setBadgeRadius(diameter * 0.5f);
     }
 
-    @Override
-    protected void onDraw(Canvas canvas) {
-        int frameColor = isActivated() ? mActiveFrameColor : mFrameColor;
-        float halfW = getWidth() / 2f;
-        float halfH = getHeight() / 2f;
-        float halfSW = Math.min(halfH, halfW);
-        updateDrawableIfDisabled();
-        if (mBitmap != null && mScale > 0) {
-            int saveCount = canvas.getSaveCount();
-            canvas.save();
-            canvas.translate(mPaddingLeft, mPaddingTop);
-            canvas.concat(mDrawMatrix);
-            float halfBW = mBitmap.getWidth() / 2f;
-            float halfBH = mBitmap.getHeight() / 2f;
-            float halfBSW = Math.min(halfBH, halfBW);
-            canvas.drawCircle(halfBW, halfBH, halfBSW - mFrameWidth / mScale + 1, mBitmapPaint);
-            canvas.restoreToCount(saveCount);
-        } else if (mDrawable != null && mScale > 0) {
-            int saveCount = canvas.getSaveCount();
-            canvas.save();
-            canvas.translate(mPaddingLeft, mPaddingTop);
-            canvas.translate(mFrameWidth - 1, mFrameWidth - 1);
-            canvas.concat(mDrawMatrix);
-            mDrawable.draw(canvas);
-            canvas.restoreToCount(saveCount);
-        }
-        if (frameColor != 0) {
-            mFramePaint.setColor(frameColor);
-            mFramePaint.setStrokeWidth(mFrameWidth);
-            canvas.drawCircle(halfW, halfH, halfSW + (mFramePadding - mFrameWidth) / 2f,
-                    mFramePaint);
-        }
+    public void setBadgeMargin(float margin) {
+        mDrawable.setBadgeMargin(margin);
+    }
+
+    public void setAvatar(Bitmap avatar) {
+        mDrawable.setIcon(avatar);
+        mDrawable.setBadge(null);
+    }
+
+    public void setAvatarWithBadge(Bitmap avatar, int userId) {
+        mDrawable.setIcon(avatar);
+        mDrawable.setBadgeIfManagedUser(getContext(), userId);
     }
 
     public void setDrawable(Drawable d) {
-        if (mDrawable != null) {
-            mDrawable.setCallback(null);
-            unscheduleDrawable(mDrawable);
+        if (d instanceof UserIconDrawable) {
+            throw new RuntimeException("Recursively adding UserIconDrawable");
         }
-        mDrawable = d;
-        if (d != null) {
-            d.setCallback(this);
-            if (d.isStateful()) {
-                d.setState(getDrawableState());
-            }
-            d.setLayoutDirection(getLayoutDirection());
-            configureBounds();
-        }
-        if (d != null) {
-            mBitmap = null;
-        }
-        configureBounds();
-        invalidate();
+        mDrawable.setIconDrawable(d);
+        mDrawable.setBadge(null);
     }
 
-    @Override
-    public void invalidateDrawable(Drawable dr) {
-        if (dr == mDrawable) {
-            invalidate();
-        } else {
-            super.invalidateDrawable(dr);
+    public void setDrawableWithBadge(Drawable d, int userId) {
+        if (d instanceof UserIconDrawable) {
+            throw new RuntimeException("Recursively adding UserIconDrawable");
         }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return who == mDrawable || super.verifyDrawable(who);
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mDrawable != null && mDrawable.isStateful()) {
-            mDrawable.setState(getDrawableState());
-        }
-    }
-
-    public void setDisabled(boolean disabled) {
-        if (mIsDisabled == disabled) {
-            return;
-        }
-        mIsDisabled = disabled;
-        invalidate();
-    }
-
-    private void updateDrawableIfDisabled() {
-        int disabledColor = getContext().getColor(R.color.qs_tile_disabled_color);
-        PorterDuffColorFilter filter = new PorterDuffColorFilter(disabledColor,
-                PorterDuff.Mode.SRC_ATOP);
-        if (mBitmap != null) {
-            if (mIsDisabled) {
-                mBitmapPaint.setColorFilter(filter);
-            } else {
-                mBitmapPaint.setColorFilter(null);
-            }
-        } else if (mDrawable != null) {
-            if (mIsDisabled) {
-                mDrawable.setColorFilter(filter);
-            } else {
-                mDrawable.setColorFilter(null);
-            }
-        }
+        mDrawable.setIconDrawable(d);
+        mDrawable.setBadgeIfManagedUser(getContext(), userId);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index fb310a6..c39d718 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -253,17 +253,13 @@
             UserDetailItemView v = (UserDetailItemView) convertView;
 
             String name = getName(mContext, item);
-            Drawable drawable;
             if (item.picture == null) {
-                drawable = getDrawable(mContext, item).mutate();
+                v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
             } else {
-                drawable = new BitmapDrawable(mContext.getResources(), item.picture);
+                v.bind(name, item.picture, item.info.id);
             }
             // Disable the icon if switching is disabled
-            if (!item.isSwitchToEnabled) {
-                drawable.setTint(mContext.getColor(R.color.qs_tile_disabled_color));
-            }
-            v.bind(name, drawable);
+            v.setAvatarEnabled(item.isSwitchToEnabled);
             convertView.setActivated(item.isCurrent);
             convertView.setTag(item);
             return convertView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index 85ac755..bae5bda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -26,7 +26,6 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.RemoteException;
@@ -37,7 +36,7 @@
 import android.util.Pair;
 
 import com.android.internal.util.UserIcons;
-import com.android.systemui.BitmapHelper;
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.R;
 
 import java.util.ArrayList;
@@ -155,8 +154,8 @@
                 Drawable avatar = null;
                 Bitmap rawAvatar = um.getUserIcon(userId);
                 if (rawAvatar != null) {
-                    avatar = new BitmapDrawable(mContext.getResources(),
-                            BitmapHelper.createCircularClip(rawAvatar, avatarSize, avatarSize));
+                    avatar = new UserIconDrawable(avatarSize)
+                            .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
                 } else {
                     avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
                             /* light= */ true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index ea0bdf2..c82ba3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -50,7 +50,6 @@
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.RestrictedLockUtils;
-import com.android.systemui.BitmapHelper;
 import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.R;
 import com.android.systemui.SystemUISecondaryUserService;
@@ -197,8 +196,6 @@
                 boolean canSwitchUsers = mUserManager.canSwitchUsers();
                 UserInfo currentUserInfo = null;
                 UserRecord guestRecord = null;
-                int avatarSize = mContext.getResources()
-                        .getDimensionPixelSize(R.dimen.max_avatar_size);
 
                 for (UserInfo info : infos) {
                     boolean isCurrent = currentId == info.id;
@@ -219,8 +216,10 @@
                                 picture = mUserManager.getUserIcon(info.id);
 
                                 if (picture != null) {
-                                    picture = BitmapHelper.createCircularClip(
-                                            picture, avatarSize, avatarSize);
+                                    int avatarSize = mContext.getResources()
+                                            .getDimensionPixelSize(R.dimen.max_avatar_size);
+                                    picture = Bitmap.createScaledBitmap(
+                                            picture, avatarSize, avatarSize, true);
                                 }
                             }
                             int index = isCurrent ? 0 : records.size();
@@ -664,8 +663,7 @@
             if (item.isAddUser) {
                 return context.getDrawable(R.drawable.ic_add_circle_qs);
             }
-            return UserIcons.getDefaultUserIcon(item.isGuest ? UserHandle.USER_NULL : item.info.id,
-                    /* light= */ true);
+            return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true);
         }
 
         public void refresh() {
@@ -718,6 +716,13 @@
                     isSwitchToEnabled);
         }
 
+        public int resolveId() {
+            if (isGuest || info == null) {
+                return UserHandle.USER_NULL;
+            }
+            return info.id;
+        }
+
         public String toString() {
             StringBuilder sb = new StringBuilder();
             sb.append("UserRecord(");
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index fe54090..a445e77 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -36,6 +36,7 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
@@ -63,6 +64,20 @@
     private static final int MAX_RUNNING_TASKS_COUNT = 10;
 
     /**
+     * List of package and class name which are considered as Settings,
+     * so PIP location should be adjusted to the left of the side panel.
+     */
+    private static final List<Pair<String, String>> sSettingsPackageAndClassNamePairList;
+    static {
+        sSettingsPackageAndClassNamePairList = new ArrayList<>();
+        sSettingsPackageAndClassNamePairList.add(new Pair<String, String>(
+                "com.android.tv.settings", null));
+        sSettingsPackageAndClassNamePairList.add(new Pair<String, String>(
+                "com.google.android.leanbacklauncher",
+                "com.google.android.leanbacklauncher.settings.HomeScreenSettingsActivity"));
+    }
+
+    /**
      * State when there's no PIP.
      */
     public static final int STATE_NO_PIP = 0;
@@ -108,6 +123,7 @@
     private int mSuspendPipResizingReason;
 
     private Context mContext;
+    private SystemServicesProxy mSystemServiceProxy;
     private PipRecentsOverlayManager mPipRecentsOverlayManager;
     private IActivityManager mActivityManager;
     private MediaSessionManager mMediaSessionManager;
@@ -117,6 +133,8 @@
     private List<MediaListener> mMediaListeners = new ArrayList<>();
     private Rect mCurrentPipBounds;
     private Rect mPipBounds;
+    private Rect mDefaultPipBounds;
+    private Rect mSettingsPipBounds;
     private Rect mMenuModePipBounds;
     private Rect mRecentsPipBounds;
     private Rect mRecentsFocusedPipBounds;
@@ -176,8 +194,10 @@
         mInitialized = true;
         mContext = context;
         Resources res = context.getResources();
-        mPipBounds = Rect.unflattenFromString(res.getString(
+        mDefaultPipBounds = Rect.unflattenFromString(res.getString(
                 com.android.internal.R.string.config_defaultPictureInPictureBounds));
+        mSettingsPipBounds = Rect.unflattenFromString(res.getString(
+                R.string.pip_settings_bounds));
         mMenuModePipBounds = Rect.unflattenFromString(res.getString(
                 R.string.pip_menu_bounds));
         mRecentsPipBounds = Rect.unflattenFromString(res.getString(
@@ -186,9 +206,11 @@
                 R.string.pip_recents_focused_bounds));
         mRecentsFocusChangedAnimationDurationMs = res.getInteger(
                 R.integer.recents_tv_pip_focus_anim_duration);
+        mPipBounds = mDefaultPipBounds;
 
         mActivityManager = ActivityManagerNative.getDefault();
-        SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
+        mSystemServiceProxy = SystemServicesProxy.getInstance(context);
+        mSystemServiceProxy.registerTaskStackListener(mTaskStackListener);
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
         mContext.registerReceiver(mBroadcastReceiver, intentFilter);
@@ -522,10 +544,25 @@
         return PLAYBACK_STATE_UNAVAILABLE;
     }
 
+    private static boolean isSettingsShown(ComponentName topActivity) {
+        for (Pair<String, String> componentName : sSettingsPackageAndClassNamePairList) {
+            String packageName = componentName.first;
+            if (topActivity.getPackageName().equals(componentName.first)) {
+                String className = componentName.second;
+                if (className == null || topActivity.getClassName().equals(className)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private TaskStackListener mTaskStackListener = new TaskStackListener() {
         @Override
         public void onTaskStackChanged() {
             if (mState != STATE_NO_PIP) {
+                boolean hasPip = false;
+
                 StackInfo stackInfo = null;
                 try {
                     stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
@@ -541,11 +578,32 @@
                 for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
                     if (stackInfo.taskIds[i] == mPipTaskId) {
                         // PIP task is still alive.
-                        return;
+                        hasPip = true;
+                        break;
                     }
                 }
-                // PIP task doesn't exist anymore in PINNED_STACK.
-                closePipInternal(true);
+                if (!hasPip) {
+                    // PIP task doesn't exist anymore in PINNED_STACK.
+                    closePipInternal(true);
+                    return;
+                }
+            }
+            if (mState == STATE_PIP_OVERLAY) {
+                try {
+                    List<RunningTaskInfo> runningTasks = mActivityManager.getTasks(1, 0);
+                    if (runningTasks == null || runningTasks.size() == 0) {
+                        return;
+                    }
+                    RunningTaskInfo topTask = runningTasks.get(0);
+                    Rect bounds = isSettingsShown(topTask.topActivity)
+                          ? mSettingsPipBounds : mDefaultPipBounds;
+                    if (mPipBounds != bounds) {
+                        mPipBounds = bounds;
+                        resizePinnedStack(STATE_PIP_OVERLAY);
+                    }
+                } catch (RemoteException e) {
+                    Log.d(TAG, "Failed to detect top activity", e);
+                }
             }
         }
 
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index b3613df..ea3cffe 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2135,6 +2135,9 @@
     // Suggestion -> Overflow -> Remove.
     ACTION_SETTINGS_DISMISS_SUGGESTION = 387;
 
+    // Settings > Apps > Gear > Special Access > Premium SMS access
+    PREMIUM_SMS_ACCESS = 388;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2741733..01d4ad6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1795,9 +1795,8 @@
                 }
                 userState.mSoftKeyboardShowMode = 0;
                 userState.mServiceChangingSoftKeyboardMode = null;
+                notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
             }
-
-            notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
         }
     }
 
@@ -4355,6 +4354,7 @@
                     }
                 } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)) {
                     if (readSoftKeyboardShowModeChangedLocked(userState)) {
+                        notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
                         onUserStateChangedLocked(userState);
                     }
                 }
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 48e96aa..8753992 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -124,6 +124,7 @@
     private boolean mLastBatteryLevelCritical;
     private int mLastMaxChargingCurrent;
     private int mLastMaxChargingVoltage;
+    private int mLastChargeCounter;
 
     private int mInvalidCharger;
     private int mLastInvalidCharger;
@@ -341,6 +342,7 @@
                     + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
                     + ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent
                     + ", maxChargingVoltage" + mBatteryProps.maxChargingVoltage
+                    + ", chargeCounter" + mBatteryProps.batteryChargeCounter
                     + ", batteryStatus=" + mBatteryProps.batteryStatus
                     + ", batteryHealth=" + mBatteryProps.batteryHealth
                     + ", batteryPresent=" + mBatteryProps.batteryPresent
@@ -373,6 +375,7 @@
                 mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
                 mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
                 mBatteryProps.maxChargingVoltage != mLastMaxChargingVoltage ||
+                mBatteryProps.batteryChargeCounter != mLastChargeCounter ||
                 mInvalidCharger != mLastInvalidCharger)) {
 
             if (mPlugType != mLastPlugType) {
@@ -501,6 +504,7 @@
             mLastBatteryTemperature = mBatteryProps.batteryTemperature;
             mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
             mLastMaxChargingVoltage = mBatteryProps.maxChargingVoltage;
+            mLastChargeCounter = mBatteryProps.batteryChargeCounter;
             mLastBatteryLevelCritical = mBatteryLevelCritical;
             mLastInvalidCharger = mInvalidCharger;
         }
@@ -527,6 +531,7 @@
         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
         intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
         intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
+        intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mBatteryProps.batteryChargeCounter);
         if (DEBUG) {
             Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryProps.batteryLevel +
                     ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
@@ -540,7 +545,8 @@
                     ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
                     ", icon:" + icon  + ", invalid charger:" + mInvalidCharger +
                     ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent +
-                    ", maxChargingVoltage:" + mBatteryProps.maxChargingVoltage);
+                    ", maxChargingVoltage:" + mBatteryProps.maxChargingVoltage +
+                    ", chargeCounter:" + mBatteryProps.batteryChargeCounter);
         }
 
         mHandler.post(new Runnable() {
@@ -772,6 +778,7 @@
                 pw.println("  Wireless powered: " + mBatteryProps.chargerWirelessOnline);
                 pw.println("  Max charging current: " + mBatteryProps.maxChargingCurrent);
                 pw.println("  Max charging voltage: " + mBatteryProps.maxChargingVoltage);
+                pw.println("  Charge counter: " + mBatteryProps.batteryChargeCounter);
                 pw.println("  status: " + mBatteryProps.batteryStatus);
                 pw.println("  health: " + mBatteryProps.batteryHealth);
                 pw.println("  present: " + mBatteryProps.batteryPresent);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4ec1f61..fdcc242 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1650,6 +1650,13 @@
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
                 if (r.finishing) {
+                    // Normally the screenshot will be taken in makeInvisible(). When an activity
+                    // is finishing, we no longer change its visibility, but we still need to take
+                    // the screenshots if startPausingLocked decided it should be taken.
+                    if (r.mUpdateTaskThumbnailWhenHidden) {
+                        r.updateThumbnailLocked(screenshotActivitiesLocked(r), null);
+                        r.mUpdateTaskThumbnailWhenHidden = false;
+                    }
                     continue;
                 }
                 final boolean isTop = r == top;
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 76dfd01..785dd47 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -119,7 +119,13 @@
         if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
             return false;
         }
-        mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId);
+        IIntentSender target = mService.getIntentSenderLocked(
+                INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingUid, mUserId, null, null, 0,
+                new Intent[] {mIntent}, new String[] {mResolvedType},
+                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT, null);
+
+        mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId,
+                new IntentSender(target));
         mCallingPid = mRealCallingPid;
         mCallingUid = mRealCallingUid;
         mResolvedType = null;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index e3c540a..a4d2cd2 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -962,6 +962,7 @@
                         mKeyEventReceiver.aquireWakeLockLocked();
                     }
                     Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+                    mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                     mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
                     try {
                         if (user.mLastMediaButtonReceiver != null) {
@@ -986,6 +987,7 @@
                     }
                     // Fallback to legacy behavior
                     Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+                    keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                     keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
                     if (needWakeLock) {
                         keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f6d2f7e..8368185 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -905,7 +905,10 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
 
         synchronized (mSessions) {
-            mSessions.get(sessionId).setPermissionsResult(accepted);
+            PackageInstallerSession session = mSessions.get(sessionId);
+            if (session != null) {
+                session.setPermissionsResult(accepted);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7a61e79..883b502 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8605,7 +8605,7 @@
                 if (abi32 >= 0) {
                     final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
                     if (abi64 >= 0) {
-                        if (cpuAbiOverride == null && pkg.use32bitAbi) {
+                        if (pkg.use32bitAbi) {
                             pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
                             pkg.applicationInfo.primaryCpuAbi = abi;
                         } else {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 24eac19..83cd978 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -363,7 +363,7 @@
     // Packages that have been uninstalled and still need their external
     // storage data deleted.
     final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
-    
+
     // Packages that have been renamed since they were first installed.
     // Keys are the new names of the packages, values are the original
     // names.  The packages appear everwhere else under their original
@@ -578,7 +578,7 @@
         }
         PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
-                p.secondaryCpuAbiString, p.secondaryCpuAbiString,
+                p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
                 p.parentPackageName, p.childPackageNames);
         mDisabledSysPackages.remove(name);
@@ -3756,7 +3756,7 @@
                 }
 
                 String tagName = parser.getName();
-                // Legacy 
+                // Legacy
                 if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
                     readDisabledComponentsLPw(packageSetting, parser, 0);
                 } else if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index bcafddc..5b9d139 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -93,6 +93,7 @@
 
     // Indicates whether we are rebooting into safe mode
     public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
+    public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode";
 
     // Indicates whether we should stay in safe mode until ro.build.date.utc is newer than this
     public static final String AUDIT_SAFEMODE_PROPERTY = "persist.sys.audit_safemode";
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index e6e5a2d..f004b45 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -490,8 +490,15 @@
     private void revokeNotificationPolicyAccess(String pkg) {
         NotificationManager nm = mContext.getSystemService(NotificationManager.class);
         if (mPreviousNotificationPolicyAccessPackage != null) {
-            nm.setNotificationPolicyAccessGranted(mPreviousNotificationPolicyAccessPackage, false);
-            mPreviousNotificationPolicyAccessPackage = null;
+            if (mPreviousNotificationPolicyAccessPackage.equals(pkg)) {
+                // Remove any DND zen rules possibly created by the package.
+                nm.removeAutomaticZenRules(mPreviousNotificationPolicyAccessPackage);
+                // Remove Notification Policy Access.
+                nm.setNotificationPolicyAccessGranted(mPreviousNotificationPolicyAccessPackage, false);
+                mPreviousNotificationPolicyAccessPackage = null;
+            } else {
+                Slog.e(TAG, "Couldn't remove Notification Policy Access for package: " + pkg);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index ebec445..1f6fb2a 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -20,17 +20,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.Signature;
 import android.os.Binder;
 import android.os.PatternMatcher;
 import android.os.Process;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
-import android.util.Base64;
 import android.util.Slog;
 import android.webkit.IWebViewUpdateService;
 import android.webkit.WebViewFactory;
@@ -40,9 +35,7 @@
 import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 
 /**
  * Private service to wait for the updatable WebView to be ready for use.
@@ -53,19 +46,16 @@
     private static final String TAG = "WebViewUpdateService";
 
     private BroadcastReceiver mWebViewUpdatedReceiver;
-    private SystemInterface mSystemInterface;
+    private WebViewUpdateServiceImpl mImpl;
 
     static final int PACKAGE_CHANGED = 0;
     static final int PACKAGE_ADDED = 1;
     static final int PACKAGE_ADDED_REPLACED = 2;
     static final int PACKAGE_REMOVED = 3;
 
-    private WebViewUpdater mWebViewUpdater;
-
     public WebViewUpdateService(Context context) {
         super(context);
-        mSystemInterface = new SystemImpl();
-        mWebViewUpdater = new WebViewUpdater(getContext(), mSystemInterface);
+        mImpl = new WebViewUpdateServiceImpl(context, new SystemImpl());
     }
 
     @Override
@@ -82,24 +72,26 @@
                             // the package that is being replaced we early-out here so that we don't
                             // run the update-logic twice.
                             if (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) return;
-                            packageStateChanged(packageNameFromIntent(intent), PACKAGE_REMOVED);
+                            mImpl.packageStateChanged(packageNameFromIntent(intent),
+                                    PACKAGE_REMOVED);
                             break;
                         case Intent.ACTION_PACKAGE_CHANGED:
                             // Ensure that we only heed PACKAGE_CHANGED intents if they change an
                             // entire package, not just a component
                             if (entirePackageChanged(intent)) {
-                                packageStateChanged(packageNameFromIntent(intent), PACKAGE_CHANGED);
+                                mImpl.packageStateChanged(packageNameFromIntent(intent),
+                                        PACKAGE_CHANGED);
                             }
                             break;
                         case Intent.ACTION_PACKAGE_ADDED:
-                            packageStateChanged(packageNameFromIntent(intent),
+                            mImpl.packageStateChanged(packageNameFromIntent(intent),
                                     (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)
                                      ? PACKAGE_ADDED_REPLACED : PACKAGE_ADDED));
                             break;
                         case Intent.ACTION_USER_ADDED:
                             int userId =
                                 intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                            handleNewUser(userId);
+                            mImpl.handleNewUser(userId);
                             break;
                     }
                 }
@@ -110,7 +102,7 @@
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addDataScheme("package");
         // Make sure we only receive intents for WebView packages from our config file.
-        for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+        for (WebViewProviderInfo provider : mImpl.getWebViewPackages()) {
             filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL);
         }
         getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
@@ -122,518 +114,14 @@
         publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/);
     }
 
-    private void packageStateChanged(String packageName, int changedState) {
-        updateFallbackState(packageName, changedState);
-        mWebViewUpdater.packageStateChanged(packageName, changedState);
-    }
-
     public void prepareWebViewInSystemServer() {
-        updateFallbackStateOnBoot();
-        mWebViewUpdater.prepareWebViewInSystemServer();
+        mImpl.prepareWebViewInSystemServer();
     }
 
     private static String packageNameFromIntent(Intent intent) {
         return intent.getDataString().substring("package:".length());
     }
 
-    private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
-        for (WebViewProviderInfo provider : providers) {
-            if (provider.availableByDefault && !provider.isFallback) {
-                try {
-                    PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider);
-                    if (isEnabledPackage(packageInfo)
-                            && mWebViewUpdater.isValidProvider(provider, packageInfo)) {
-                        return true;
-                    }
-                } catch (NameNotFoundException e) {
-                    // A non-existent provider is neither valid nor enabled
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Called when a new user has been added to update the state of its fallback package.
-     */
-    void handleNewUser(int userId) {
-        if (!mSystemInterface.isFallbackLogicEnabled()) return;
-
-        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
-        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
-        if (fallbackProvider == null) return;
-
-        mSystemInterface.enablePackageForUser(fallbackProvider.packageName,
-                !existsValidNonFallbackProvider(webviewProviders), userId);
-    }
-
-    public void updateFallbackStateOnBoot() {
-        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
-        updateFallbackState(webviewProviders, true);
-    }
-
-    /**
-     * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback
-     * package that is valid (and available by default) then disable the fallback package,
-     * otherwise, enable the fallback package.
-     */
-    public void updateFallbackState(String changedPackage, int changedState) {
-        if (!mSystemInterface.isFallbackLogicEnabled()) return;
-
-        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
-
-        // A package was changed / updated / downgraded, early out if it is not one of the
-        // webview packages that are available by default.
-        boolean changedPackageAvailableByDefault = false;
-        for (WebViewProviderInfo provider : webviewProviders) {
-            if (provider.packageName.equals(changedPackage)) {
-                if (provider.availableByDefault) {
-                    changedPackageAvailableByDefault = true;
-                }
-                break;
-            }
-        }
-        if (!changedPackageAvailableByDefault) return;
-        updateFallbackState(webviewProviders, false);
-    }
-
-    private void updateFallbackState(WebViewProviderInfo[] webviewProviders, boolean isBoot) {
-        // If there exists a valid and enabled non-fallback package - disable the fallback
-        // package, otherwise, enable it.
-        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
-        if (fallbackProvider == null) return;
-        boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
-
-        boolean isFallbackEnabled = false;
-        try {
-            isFallbackEnabled = isEnabledPackage(
-                    mSystemInterface.getPackageInfoForProvider(fallbackProvider));
-        } catch (NameNotFoundException e) {
-        }
-
-        if (existsValidNonFallbackProvider
-                // During an OTA the primary user's WebView state might differ from other users', so
-                // ignore the state of that user during boot.
-                && (isFallbackEnabled || isBoot)) {
-            mSystemInterface.uninstallAndDisablePackageForAllUsers(getContext(),
-                    fallbackProvider.packageName);
-        } else if (!existsValidNonFallbackProvider
-                // During an OTA the primary user's WebView state might differ from other users', so
-                // ignore the state of that user during boot.
-                && (!isFallbackEnabled || isBoot)) {
-            // Enable the fallback package for all users.
-            mSystemInterface.enablePackageForAllUsers(getContext(),
-                    fallbackProvider.packageName, true);
-        }
-    }
-
-    /**
-     * Returns the only fallback provider in the set of given packages, or null if there is none.
-     */
-    private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
-        for (WebViewProviderInfo provider : webviewPackages) {
-            if (provider.isFallback) {
-                return provider;
-            }
-        }
-        return null;
-    }
-
-    private boolean isFallbackPackage(String packageName) {
-        if (packageName == null || !mSystemInterface.isFallbackLogicEnabled()) return false;
-
-        WebViewProviderInfo[] webviewPackages = mSystemInterface.getWebViewPackages();
-        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
-        return (fallbackProvider != null
-                && packageName.equals(fallbackProvider.packageName));
-    }
-
-    /**
-     * Class that decides what WebView implementation to use and prepares that implementation for
-     * use.
-     */
-    private static class WebViewUpdater {
-        private Context mContext;
-        private SystemInterface mSystemInterface;
-        private int mMinimumVersionCode = -1;
-
-        public WebViewUpdater(Context context, SystemInterface systemInterface) {
-            mContext = context;
-            mSystemInterface = systemInterface;
-        }
-
-        private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
-
-        // Keeps track of the number of running relro creations
-        private int mNumRelroCreationsStarted = 0;
-        private int mNumRelroCreationsFinished = 0;
-        // Implies that we need to rerun relro creation because we are using an out-of-date package
-        private boolean mWebViewPackageDirty = false;
-        private boolean mAnyWebViewInstalled = false;
-
-        private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
-
-        // The WebView package currently in use (or the one we are preparing).
-        private PackageInfo mCurrentWebViewPackage = null;
-
-        private Object mLock = new Object();
-
-        public void packageStateChanged(String packageName, int changedState) {
-            for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
-                String webviewPackage = provider.packageName;
-
-                if (webviewPackage.equals(packageName)) {
-                    boolean updateWebView = false;
-                    boolean removedOrChangedOldPackage = false;
-                    String oldProviderName = null;
-                    PackageInfo newPackage = null;
-                    synchronized(mLock) {
-                        try {
-                            newPackage = findPreferredWebViewPackage();
-                            if (mCurrentWebViewPackage != null)
-                                oldProviderName = mCurrentWebViewPackage.packageName;
-                            // Only trigger update actions if the updated package is the one
-                            // that will be used, or the one that was in use before the
-                            // update, or if we haven't seen a valid WebView package before.
-                            updateWebView =
-                                provider.packageName.equals(newPackage.packageName)
-                                || provider.packageName.equals(oldProviderName)
-                                || mCurrentWebViewPackage == null;
-                            // We removed the old package if we received an intent to remove
-                            // or replace the old package.
-                            removedOrChangedOldPackage =
-                                provider.packageName.equals(oldProviderName);
-                            if (updateWebView) {
-                                onWebViewProviderChanged(newPackage);
-                            }
-                        } catch (WebViewFactory.MissingWebViewPackageException e) {
-                            Slog.e(TAG, "Could not find valid WebView package to create " +
-                                    "relro with " + e);
-                        }
-                    }
-                    if(updateWebView && !removedOrChangedOldPackage
-                            && oldProviderName != null) {
-                        // If the provider change is the result of adding or replacing a
-                        // package that was not the previous provider then we must kill
-                        // packages dependent on the old package ourselves. The framework
-                        // only kills dependents of packages that are being removed.
-                        mSystemInterface.killPackageDependents(oldProviderName);
-                    }
-                    return;
-                }
-            }
-        }
-
-        public void prepareWebViewInSystemServer() {
-            try {
-                synchronized(mLock) {
-                    mCurrentWebViewPackage = findPreferredWebViewPackage();
-                    onWebViewProviderChanged(mCurrentWebViewPackage);
-                }
-            } catch (Throwable t) {
-                // Log and discard errors at this stage as we must not crash the system server.
-                Slog.e(TAG, "error preparing webview provider from system server", t);
-            }
-        }
-
-        /**
-         * Change WebView provider and provider setting and kill packages using the old provider.
-         * Return the new provider (in case we are in the middle of creating relro files this new
-         * provider will not be in use directly, but will when the relros are done).
-         */
-        public String changeProviderAndSetting(String newProviderName) {
-            PackageInfo oldPackage = null;
-            PackageInfo newPackage = null;
-            synchronized(mLock) {
-                oldPackage = mCurrentWebViewPackage;
-                mSystemInterface.updateUserSetting(mContext, newProviderName);
-
-                try {
-                    newPackage = findPreferredWebViewPackage();
-                    if (oldPackage != null
-                            && newPackage.packageName.equals(oldPackage.packageName)) {
-                        // If we don't perform the user change, revert the settings change.
-                        mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
-                        return newPackage.packageName;
-                    }
-                } catch (WebViewFactory.MissingWebViewPackageException e) {
-                    Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " +
-                            "package " + e);
-                    // If we don't perform the user change but don't have an installed WebView
-                    // package, we will have changed the setting and it will be used when a package
-                    // is available.
-                    return newProviderName;
-                }
-                onWebViewProviderChanged(newPackage);
-            }
-            // Kill apps using the old provider
-            if (oldPackage != null) {
-                mSystemInterface.killPackageDependents(oldPackage.packageName);
-            }
-            return newPackage.packageName;
-        }
-
-        /**
-         * This is called when we change WebView provider, either when the current provider is
-         * updated or a new provider is chosen / takes precedence.
-         */
-        private void onWebViewProviderChanged(PackageInfo newPackage) {
-            synchronized(mLock) {
-                mAnyWebViewInstalled = true;
-                if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
-                    mCurrentWebViewPackage = newPackage;
-                    mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
-
-                    // The relro creations might 'finish' (not start at all) before
-                    // WebViewFactory.onWebViewProviderChanged which means we might not know the
-                    // number of started creations before they finish.
-                    mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
-                    mNumRelroCreationsFinished = 0;
-                    mNumRelroCreationsStarted =
-                        mSystemInterface.onWebViewProviderChanged(newPackage);
-                    // If the relro creations finish before we know the number of started creations
-                    // we will have to do any cleanup/notifying here.
-                    checkIfRelrosDoneLocked();
-                } else {
-                    mWebViewPackageDirty = true;
-                }
-            }
-        }
-
-        private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
-            WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
-            List<ProviderAndPackageInfo> providers = new ArrayList<>();
-            for(int n = 0; n < allProviders.length; n++) {
-                try {
-                    PackageInfo packageInfo =
-                        mSystemInterface.getPackageInfoForProvider(allProviders[n]);
-                    if (isValidProvider(allProviders[n], packageInfo)) {
-                        providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
-                    }
-                } catch (NameNotFoundException e) {
-                    // Don't add non-existent packages
-                }
-            }
-            return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
-        }
-
-        /**
-         * Fetch only the currently valid WebView packages.
-         **/
-        public WebViewProviderInfo[] getValidWebViewPackages() {
-            ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
-            WebViewProviderInfo[] providers =
-                new WebViewProviderInfo[providersAndPackageInfos.length];
-            for(int n = 0; n < providersAndPackageInfos.length; n++) {
-                providers[n] = providersAndPackageInfos[n].provider;
-            }
-            return providers;
-        }
-
-
-        private class ProviderAndPackageInfo {
-            public final WebViewProviderInfo provider;
-            public final PackageInfo packageInfo;
-
-            public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
-                this.provider = provider;
-                this.packageInfo = packageInfo;
-            }
-        }
-
-        /**
-         * Returns either the package info of the WebView provider determined in the following way:
-         * If the user has chosen a provider then use that if it is valid,
-         * otherwise use the first package in the webview priority list that is valid.
-         *
-         */
-        private PackageInfo findPreferredWebViewPackage() {
-            ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
-
-            String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
-
-            // If the user has chosen provider, use that
-            for (ProviderAndPackageInfo providerAndPackage : providers) {
-                if (providerAndPackage.provider.packageName.equals(userChosenProvider)
-                        && isEnabledPackage(providerAndPackage.packageInfo)) {
-                    return providerAndPackage.packageInfo;
-                }
-            }
-
-            // User did not choose, or the choice failed; use the most stable provider that is
-            // enabled and available by default (not through user choice).
-            for (ProviderAndPackageInfo providerAndPackage : providers) {
-                if (providerAndPackage.provider.availableByDefault
-                        && isEnabledPackage(providerAndPackage.packageInfo)) {
-                    return providerAndPackage.packageInfo;
-                }
-            }
-
-            // Could not find any enabled package either, use the most stable provider.
-            for (ProviderAndPackageInfo providerAndPackage : providers) {
-                return providerAndPackage.packageInfo;
-            }
-
-            mAnyWebViewInstalled = false;
-            throw new WebViewFactory.MissingWebViewPackageException(
-                    "Could not find a loadable WebView package");
-        }
-
-        public void notifyRelroCreationCompleted() {
-            synchronized (mLock) {
-                mNumRelroCreationsFinished++;
-                checkIfRelrosDoneLocked();
-            }
-        }
-
-        public WebViewProviderResponse waitForAndGetProvider() {
-            PackageInfo webViewPackage = null;
-            final long NS_PER_MS = 1000000;
-            final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
-            boolean webViewReady = false;
-            int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
-            synchronized (mLock) {
-                webViewReady = webViewIsReadyLocked();
-                while (!webViewReady) {
-                    final long timeNowMs = System.nanoTime() / NS_PER_MS;
-                    if (timeNowMs >= timeoutTimeMs) break;
-                    try {
-                        mLock.wait(timeoutTimeMs - timeNowMs);
-                    } catch (InterruptedException e) {}
-                    webViewReady = webViewIsReadyLocked();
-                }
-                // Make sure we return the provider that was used to create the relro file
-                webViewPackage = mCurrentWebViewPackage;
-                if (webViewReady) {
-                } else if (!mAnyWebViewInstalled) {
-                    webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
-                } else {
-                    // Either the current relro creation  isn't done yet, or the new relro creatioin
-                    // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
-                    webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
-                }
-            }
-            if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
-            return new WebViewProviderResponse(webViewPackage, webViewStatus);
-        }
-
-        public String getCurrentWebViewPackageName() {
-            synchronized(mLock) {
-                if (mCurrentWebViewPackage == null)
-                    return null;
-                return mCurrentWebViewPackage.packageName;
-            }
-        }
-
-        /**
-         * Returns whether WebView is ready and is not going to go through its preparation phase
-         * again directly.
-         */
-        private boolean webViewIsReadyLocked() {
-            return !mWebViewPackageDirty
-                && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
-                // The current package might be replaced though we haven't received an intent
-                // declaring this yet, the following flag makes anyone loading WebView to wait in
-                // this case.
-                && mAnyWebViewInstalled;
-        }
-
-        private void checkIfRelrosDoneLocked() {
-            if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
-                if (mWebViewPackageDirty) {
-                    mWebViewPackageDirty = false;
-                    // If we have changed provider since we started the relro creation we need to
-                    // redo the whole process using the new package instead.
-                    PackageInfo newPackage = findPreferredWebViewPackage();
-                    onWebViewProviderChanged(newPackage);
-                } else {
-                    mLock.notifyAll();
-                }
-            }
-        }
-
-        /**
-         * Returns whether this provider is valid for use as a WebView provider.
-         */
-        public boolean isValidProvider(WebViewProviderInfo configInfo,
-                PackageInfo packageInfo) {
-            if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
-                    && packageInfo.versionCode < getMinimumVersionCode()
-                    && !mSystemInterface.systemIsDebuggable()) {
-                // Non-system package webview providers may be downgraded arbitrarily low, prevent
-                // that by enforcing minimum version code. This check is only enforced for user
-                // builds.
-                return false;
-            }
-            if (providerHasValidSignature(configInfo, packageInfo, mSystemInterface) &&
-                    WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) {
-                return true;
-            }
-            return false;
-        }
-
-        /**
-         * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
-         * of all available-by-default and non-fallback WebView provider packages. If there is no
-         * such WebView provider package on the system, then return -1, which means all positive
-         * versionCode WebView packages are accepted.
-         */
-        private int getMinimumVersionCode() {
-            if (mMinimumVersionCode > 0) {
-                return mMinimumVersionCode;
-            }
-
-            for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
-                if (provider.availableByDefault && !provider.isFallback) {
-                    try {
-                        int versionCode =
-                            mSystemInterface.getFactoryPackageVersion(provider.packageName);
-                        if (mMinimumVersionCode < 0 || versionCode < mMinimumVersionCode) {
-                            mMinimumVersionCode = versionCode;
-                        }
-                    } catch (PackageManager.NameNotFoundException e) {
-                        // Safe to ignore.
-                    }
-                }
-            }
-
-            return mMinimumVersionCode;
-        }
-    }
-
-    private static boolean providerHasValidSignature(WebViewProviderInfo provider,
-            PackageInfo packageInfo, SystemInterface systemInterface) {
-        if (systemInterface.systemIsDebuggable()) {
-            return true;
-        }
-        Signature[] packageSignatures;
-        // If no signature is declared, instead check whether the package is included in the
-        // system.
-        if (provider.signatures == null || provider.signatures.length == 0) {
-            return packageInfo.applicationInfo.isSystemApp();
-        }
-        packageSignatures = packageInfo.signatures;
-        if (packageSignatures.length != 1)
-            return false;
-
-        final byte[] packageSignature = packageSignatures[0].toByteArray();
-        // Return whether the package signature matches any of the valid signatures
-        for (String signature : provider.signatures) {
-            final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
-            if (Arrays.equals(packageSignature, validSignature))
-                return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns whether the given package is enabled.
-     * This state can be changed by the user from Settings->Apps
-     */
-    private static boolean isEnabledPackage(PackageInfo packageInfo) {
-        return packageInfo.applicationInfo.enabled;
-    }
-
     /**
      * Returns whether the entire package from an ACTION_PACKAGE_CHANGED intent was changed (rather
      * than just one of its components).
@@ -673,7 +161,7 @@
 
             long callingId = Binder.clearCallingIdentity();
             try {
-                WebViewUpdateService.this.mWebViewUpdater.notifyRelroCreationCompleted();
+                WebViewUpdateService.this.mImpl.notifyRelroCreationCompleted();
             } finally {
                 Binder.restoreCallingIdentity(callingId);
             }
@@ -693,7 +181,7 @@
                 throw new IllegalStateException("Cannot create a WebView from the SystemServer");
             }
 
-            return WebViewUpdateService.this.mWebViewUpdater.waitForAndGetProvider();
+            return WebViewUpdateService.this.mImpl.waitForAndGetProvider();
         }
 
         /**
@@ -714,7 +202,7 @@
 
             long callingId = Binder.clearCallingIdentity();
             try {
-                return WebViewUpdateService.this.mWebViewUpdater.changeProviderAndSetting(
+                return WebViewUpdateService.this.mImpl.changeProviderAndSetting(
                         newProvider);
             } finally {
                 Binder.restoreCallingIdentity(callingId);
@@ -723,22 +211,22 @@
 
         @Override // Binder call
         public WebViewProviderInfo[] getValidWebViewPackages() {
-            return WebViewUpdateService.this.mWebViewUpdater.getValidWebViewPackages();
+            return WebViewUpdateService.this.mImpl.getValidWebViewPackages();
         }
 
         @Override // Binder call
         public WebViewProviderInfo[] getAllWebViewPackages() {
-            return WebViewUpdateService.this.mSystemInterface.getWebViewPackages();
+            return WebViewUpdateService.this.mImpl.getWebViewPackages();
         }
 
         @Override // Binder call
         public String getCurrentWebViewPackageName() {
-            return WebViewUpdateService.this.mWebViewUpdater.getCurrentWebViewPackageName();
+            return WebViewUpdateService.this.mImpl.getCurrentWebViewPackageName();
         }
 
         @Override // Binder call
         public boolean isFallbackPackage(String packageName) {
-            return WebViewUpdateService.this.isFallbackPackage(packageName);
+            return WebViewUpdateService.this.mImpl.isFallbackPackage(packageName);
         }
 
         @Override // Binder call
@@ -754,7 +242,7 @@
                 throw new SecurityException(msg);
             }
 
-            WebViewUpdateService.this.mSystemInterface.enableFallbackLogic(enable);
+            WebViewUpdateService.this.mImpl.enableFallbackLogic(enable);
         }
     }
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
new file mode 100644
index 0000000..32b195b
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -0,0 +1,588 @@
+/*
+ * 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.webkit;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
+import android.util.Base64;
+import android.util.Slog;
+import android.webkit.WebViewFactory;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewProviderResponse;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Implementation of the WebViewUpdateService.
+ * This class doesn't depend on the android system like the actual Service does and can be used
+ * directly by tests (as long as they implement a SystemInterface).
+ * @hide
+ */
+public class WebViewUpdateServiceImpl {
+    private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName();
+
+    private SystemInterface mSystemInterface;
+    private WebViewUpdater mWebViewUpdater;
+    private Context mContext;
+
+    public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
+        mContext = context;
+        mSystemInterface = systemInterface;
+        mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface);
+    }
+
+    void packageStateChanged(String packageName, int changedState) {
+        updateFallbackStateOnPackageChange(packageName, changedState);
+        mWebViewUpdater.packageStateChanged(packageName, changedState);
+    }
+
+    void prepareWebViewInSystemServer() {
+        updateFallbackStateOnBoot();
+        mWebViewUpdater.prepareWebViewInSystemServer();
+    }
+
+    private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
+        for (WebViewProviderInfo provider : providers) {
+            if (provider.availableByDefault && !provider.isFallback) {
+                try {
+                    PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider);
+                    if (isEnabledPackage(packageInfo)
+                            && mWebViewUpdater.isValidProvider(provider, packageInfo)) {
+                        return true;
+                    }
+                } catch (NameNotFoundException e) {
+                    // A non-existent provider is neither valid nor enabled
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called when a new user has been added to update the state of its fallback package.
+     */
+    void handleNewUser(int userId) {
+        if (!mSystemInterface.isFallbackLogicEnabled()) return;
+
+        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
+        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
+        if (fallbackProvider == null) return;
+
+        mSystemInterface.enablePackageForUser(fallbackProvider.packageName,
+                !existsValidNonFallbackProvider(webviewProviders), userId);
+    }
+
+    void notifyRelroCreationCompleted() {
+        mWebViewUpdater.notifyRelroCreationCompleted();
+    }
+
+    WebViewProviderResponse waitForAndGetProvider() {
+        return mWebViewUpdater.waitForAndGetProvider();
+    }
+
+    String changeProviderAndSetting(String newProvider) {
+        return mWebViewUpdater.changeProviderAndSetting(newProvider);
+    }
+
+    WebViewProviderInfo[] getValidWebViewPackages() {
+        return mWebViewUpdater.getValidWebViewPackages();
+    }
+
+    WebViewProviderInfo[] getWebViewPackages() {
+        return mSystemInterface.getWebViewPackages();
+    }
+
+    String getCurrentWebViewPackageName() {
+        return mWebViewUpdater.getCurrentWebViewPackageName();
+    }
+
+    void enableFallbackLogic(boolean enable) {
+        mSystemInterface.enableFallbackLogic(enable);
+    }
+
+    private void updateFallbackStateOnBoot() {
+        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
+        updateFallbackState(webviewProviders, true);
+    }
+
+    /**
+     * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback
+     * package that is valid (and available by default) then disable the fallback package,
+     * otherwise, enable the fallback package.
+     */
+    private void updateFallbackStateOnPackageChange(String changedPackage, int changedState) {
+        if (!mSystemInterface.isFallbackLogicEnabled()) return;
+
+        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
+
+        // A package was changed / updated / downgraded, early out if it is not one of the
+        // webview packages that are available by default.
+        boolean changedPackageAvailableByDefault = false;
+        for (WebViewProviderInfo provider : webviewProviders) {
+            if (provider.packageName.equals(changedPackage)) {
+                if (provider.availableByDefault) {
+                    changedPackageAvailableByDefault = true;
+                }
+                break;
+            }
+        }
+        if (!changedPackageAvailableByDefault) return;
+        updateFallbackState(webviewProviders, false);
+    }
+
+    private void updateFallbackState(WebViewProviderInfo[] webviewProviders, boolean isBoot) {
+        // If there exists a valid and enabled non-fallback package - disable the fallback
+        // package, otherwise, enable it.
+        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
+        if (fallbackProvider == null) return;
+        boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
+
+        boolean isFallbackEnabled = false;
+        try {
+            isFallbackEnabled = isEnabledPackage(
+                    mSystemInterface.getPackageInfoForProvider(fallbackProvider));
+        } catch (NameNotFoundException e) {
+        }
+
+        if (existsValidNonFallbackProvider
+                // During an OTA the primary user's WebView state might differ from other users', so
+                // ignore the state of that user during boot.
+                && (isFallbackEnabled || isBoot)) {
+            mSystemInterface.uninstallAndDisablePackageForAllUsers(mContext,
+                    fallbackProvider.packageName);
+        } else if (!existsValidNonFallbackProvider
+                // During an OTA the primary user's WebView state might differ from other users', so
+                // ignore the state of that user during boot.
+                && (!isFallbackEnabled || isBoot)) {
+            // Enable the fallback package for all users.
+            mSystemInterface.enablePackageForAllUsers(mContext,
+                    fallbackProvider.packageName, true);
+        }
+    }
+
+    /**
+     * Returns the only fallback provider in the set of given packages, or null if there is none.
+     */
+    private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
+        for (WebViewProviderInfo provider : webviewPackages) {
+            if (provider.isFallback) {
+                return provider;
+            }
+        }
+        return null;
+    }
+
+    boolean isFallbackPackage(String packageName) {
+        if (packageName == null || !mSystemInterface.isFallbackLogicEnabled()) return false;
+
+        WebViewProviderInfo[] webviewPackages = mSystemInterface.getWebViewPackages();
+        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
+        return (fallbackProvider != null
+                && packageName.equals(fallbackProvider.packageName));
+    }
+
+    /**
+     * Class that decides what WebView implementation to use and prepares that implementation for
+     * use.
+     */
+    private static class WebViewUpdater {
+        private Context mContext;
+        private SystemInterface mSystemInterface;
+        private int mMinimumVersionCode = -1;
+
+        public WebViewUpdater(Context context, SystemInterface systemInterface) {
+            mContext = context;
+            mSystemInterface = systemInterface;
+        }
+
+        private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
+
+        // Keeps track of the number of running relro creations
+        private int mNumRelroCreationsStarted = 0;
+        private int mNumRelroCreationsFinished = 0;
+        // Implies that we need to rerun relro creation because we are using an out-of-date package
+        private boolean mWebViewPackageDirty = false;
+        private boolean mAnyWebViewInstalled = false;
+
+        private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
+
+        // The WebView package currently in use (or the one we are preparing).
+        private PackageInfo mCurrentWebViewPackage = null;
+
+        private Object mLock = new Object();
+
+        public void packageStateChanged(String packageName, int changedState) {
+            for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+                String webviewPackage = provider.packageName;
+
+                if (webviewPackage.equals(packageName)) {
+                    boolean updateWebView = false;
+                    boolean removedOrChangedOldPackage = false;
+                    String oldProviderName = null;
+                    PackageInfo newPackage = null;
+                    synchronized(mLock) {
+                        try {
+                            newPackage = findPreferredWebViewPackage();
+                            if (mCurrentWebViewPackage != null)
+                                oldProviderName = mCurrentWebViewPackage.packageName;
+                            // Only trigger update actions if the updated package is the one
+                            // that will be used, or the one that was in use before the
+                            // update, or if we haven't seen a valid WebView package before.
+                            updateWebView =
+                                provider.packageName.equals(newPackage.packageName)
+                                || provider.packageName.equals(oldProviderName)
+                                || mCurrentWebViewPackage == null;
+                            // We removed the old package if we received an intent to remove
+                            // or replace the old package.
+                            removedOrChangedOldPackage =
+                                provider.packageName.equals(oldProviderName);
+                            if (updateWebView) {
+                                onWebViewProviderChanged(newPackage);
+                            }
+                        } catch (WebViewFactory.MissingWebViewPackageException e) {
+                            Slog.e(TAG, "Could not find valid WebView package to create " +
+                                    "relro with " + e);
+                        }
+                    }
+                    if(updateWebView && !removedOrChangedOldPackage
+                            && oldProviderName != null) {
+                        // If the provider change is the result of adding or replacing a
+                        // package that was not the previous provider then we must kill
+                        // packages dependent on the old package ourselves. The framework
+                        // only kills dependents of packages that are being removed.
+                        mSystemInterface.killPackageDependents(oldProviderName);
+                    }
+                    return;
+                }
+            }
+        }
+
+        public void prepareWebViewInSystemServer() {
+            try {
+                synchronized(mLock) {
+                    mCurrentWebViewPackage = findPreferredWebViewPackage();
+                    onWebViewProviderChanged(mCurrentWebViewPackage);
+                }
+            } catch (Throwable t) {
+                // Log and discard errors at this stage as we must not crash the system server.
+                Slog.e(TAG, "error preparing webview provider from system server", t);
+            }
+        }
+
+        /**
+         * Change WebView provider and provider setting and kill packages using the old provider.
+         * Return the new provider (in case we are in the middle of creating relro files this new
+         * provider will not be in use directly, but will when the relros are done).
+         */
+        public String changeProviderAndSetting(String newProviderName) {
+            PackageInfo oldPackage = null;
+            PackageInfo newPackage = null;
+            synchronized(mLock) {
+                oldPackage = mCurrentWebViewPackage;
+                mSystemInterface.updateUserSetting(mContext, newProviderName);
+
+                try {
+                    newPackage = findPreferredWebViewPackage();
+                    if (oldPackage != null
+                            && newPackage.packageName.equals(oldPackage.packageName)) {
+                        // If we don't perform the user change, revert the settings change.
+                        mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
+                        return newPackage.packageName;
+                    }
+                } catch (WebViewFactory.MissingWebViewPackageException e) {
+                    Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " +
+                            "package " + e);
+                    // If we don't perform the user change but don't have an installed WebView
+                    // package, we will have changed the setting and it will be used when a package
+                    // is available.
+                    return newProviderName;
+                }
+                onWebViewProviderChanged(newPackage);
+            }
+            // Kill apps using the old provider
+            if (oldPackage != null) {
+                mSystemInterface.killPackageDependents(oldPackage.packageName);
+            }
+            return newPackage.packageName;
+        }
+
+        /**
+         * This is called when we change WebView provider, either when the current provider is
+         * updated or a new provider is chosen / takes precedence.
+         */
+        private void onWebViewProviderChanged(PackageInfo newPackage) {
+            synchronized(mLock) {
+                mAnyWebViewInstalled = true;
+                if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+                    mCurrentWebViewPackage = newPackage;
+                    mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
+
+                    // The relro creations might 'finish' (not start at all) before
+                    // WebViewFactory.onWebViewProviderChanged which means we might not know the
+                    // number of started creations before they finish.
+                    mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
+                    mNumRelroCreationsFinished = 0;
+                    mNumRelroCreationsStarted =
+                        mSystemInterface.onWebViewProviderChanged(newPackage);
+                    // If the relro creations finish before we know the number of started creations
+                    // we will have to do any cleanup/notifying here.
+                    checkIfRelrosDoneLocked();
+                } else {
+                    mWebViewPackageDirty = true;
+                }
+            }
+        }
+
+        private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
+            WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+            List<ProviderAndPackageInfo> providers = new ArrayList<>();
+            for(int n = 0; n < allProviders.length; n++) {
+                try {
+                    PackageInfo packageInfo =
+                        mSystemInterface.getPackageInfoForProvider(allProviders[n]);
+                    if (isValidProvider(allProviders[n], packageInfo)) {
+                        providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
+                    }
+                } catch (NameNotFoundException e) {
+                    // Don't add non-existent packages
+                }
+            }
+            return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
+        }
+
+        /**
+         * Fetch only the currently valid WebView packages.
+         **/
+        public WebViewProviderInfo[] getValidWebViewPackages() {
+            ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
+            WebViewProviderInfo[] providers =
+                new WebViewProviderInfo[providersAndPackageInfos.length];
+            for(int n = 0; n < providersAndPackageInfos.length; n++) {
+                providers[n] = providersAndPackageInfos[n].provider;
+            }
+            return providers;
+        }
+
+
+        private class ProviderAndPackageInfo {
+            public final WebViewProviderInfo provider;
+            public final PackageInfo packageInfo;
+
+            public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
+                this.provider = provider;
+                this.packageInfo = packageInfo;
+            }
+        }
+
+        /**
+         * Returns either the package info of the WebView provider determined in the following way:
+         * If the user has chosen a provider then use that if it is valid,
+         * otherwise use the first package in the webview priority list that is valid.
+         *
+         */
+        private PackageInfo findPreferredWebViewPackage() {
+            ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
+
+            String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
+
+            // If the user has chosen provider, use that
+            for (ProviderAndPackageInfo providerAndPackage : providers) {
+                if (providerAndPackage.provider.packageName.equals(userChosenProvider)
+                        && isEnabledPackage(providerAndPackage.packageInfo)) {
+                    return providerAndPackage.packageInfo;
+                }
+            }
+
+            // User did not choose, or the choice failed; use the most stable provider that is
+            // enabled and available by default (not through user choice).
+            for (ProviderAndPackageInfo providerAndPackage : providers) {
+                if (providerAndPackage.provider.availableByDefault
+                        && isEnabledPackage(providerAndPackage.packageInfo)) {
+                    return providerAndPackage.packageInfo;
+                }
+            }
+
+            // Could not find any enabled package either, use the most stable provider.
+            for (ProviderAndPackageInfo providerAndPackage : providers) {
+                return providerAndPackage.packageInfo;
+            }
+
+            mAnyWebViewInstalled = false;
+            throw new WebViewFactory.MissingWebViewPackageException(
+                    "Could not find a loadable WebView package");
+        }
+
+        public void notifyRelroCreationCompleted() {
+            synchronized (mLock) {
+                mNumRelroCreationsFinished++;
+                checkIfRelrosDoneLocked();
+            }
+        }
+
+        public WebViewProviderResponse waitForAndGetProvider() {
+            PackageInfo webViewPackage = null;
+            final long NS_PER_MS = 1000000;
+            final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
+            boolean webViewReady = false;
+            int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
+            synchronized (mLock) {
+                webViewReady = webViewIsReadyLocked();
+                while (!webViewReady) {
+                    final long timeNowMs = System.nanoTime() / NS_PER_MS;
+                    if (timeNowMs >= timeoutTimeMs) break;
+                    try {
+                        mLock.wait(timeoutTimeMs - timeNowMs);
+                    } catch (InterruptedException e) {}
+                    webViewReady = webViewIsReadyLocked();
+                }
+                // Make sure we return the provider that was used to create the relro file
+                webViewPackage = mCurrentWebViewPackage;
+                if (webViewReady) {
+                } else if (!mAnyWebViewInstalled) {
+                    webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+                } else {
+                    // Either the current relro creation  isn't done yet, or the new relro creatioin
+                    // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
+                    webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
+                }
+            }
+            if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
+            return new WebViewProviderResponse(webViewPackage, webViewStatus);
+        }
+
+        public String getCurrentWebViewPackageName() {
+            synchronized(mLock) {
+                if (mCurrentWebViewPackage == null)
+                    return null;
+                return mCurrentWebViewPackage.packageName;
+            }
+        }
+
+        /**
+         * Returns whether WebView is ready and is not going to go through its preparation phase
+         * again directly.
+         */
+        private boolean webViewIsReadyLocked() {
+            return !mWebViewPackageDirty
+                && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
+                // The current package might be replaced though we haven't received an intent
+                // declaring this yet, the following flag makes anyone loading WebView to wait in
+                // this case.
+                && mAnyWebViewInstalled;
+        }
+
+        private void checkIfRelrosDoneLocked() {
+            if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+                if (mWebViewPackageDirty) {
+                    mWebViewPackageDirty = false;
+                    // If we have changed provider since we started the relro creation we need to
+                    // redo the whole process using the new package instead.
+                    PackageInfo newPackage = findPreferredWebViewPackage();
+                    onWebViewProviderChanged(newPackage);
+                } else {
+                    mLock.notifyAll();
+                }
+            }
+        }
+
+        /**
+         * Returns whether this provider is valid for use as a WebView provider.
+         */
+        public boolean isValidProvider(WebViewProviderInfo configInfo,
+                PackageInfo packageInfo) {
+            if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+                    && packageInfo.versionCode < getMinimumVersionCode()
+                    && !mSystemInterface.systemIsDebuggable()) {
+                // Non-system package webview providers may be downgraded arbitrarily low, prevent
+                // that by enforcing minimum version code. This check is only enforced for user
+                // builds.
+                return false;
+            }
+            if (providerHasValidSignature(configInfo, packageInfo, mSystemInterface) &&
+                    WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) {
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
+         * of all available-by-default and non-fallback WebView provider packages. If there is no
+         * such WebView provider package on the system, then return -1, which means all positive
+         * versionCode WebView packages are accepted.
+         */
+        private int getMinimumVersionCode() {
+            if (mMinimumVersionCode > 0) {
+                return mMinimumVersionCode;
+            }
+
+            for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+                if (provider.availableByDefault && !provider.isFallback) {
+                    try {
+                        int versionCode =
+                            mSystemInterface.getFactoryPackageVersion(provider.packageName);
+                        if (mMinimumVersionCode < 0 || versionCode < mMinimumVersionCode) {
+                            mMinimumVersionCode = versionCode;
+                        }
+                    } catch (NameNotFoundException e) {
+                        // Safe to ignore.
+                    }
+                }
+            }
+
+            return mMinimumVersionCode;
+        }
+    }
+
+    private static boolean providerHasValidSignature(WebViewProviderInfo provider,
+            PackageInfo packageInfo, SystemInterface systemInterface) {
+        if (systemInterface.systemIsDebuggable()) {
+            return true;
+        }
+        Signature[] packageSignatures;
+        // If no signature is declared, instead check whether the package is included in the
+        // system.
+        if (provider.signatures == null || provider.signatures.length == 0) {
+            return packageInfo.applicationInfo.isSystemApp();
+        }
+        packageSignatures = packageInfo.signatures;
+        if (packageSignatures.length != 1)
+            return false;
+
+        final byte[] packageSignature = packageSignatures[0].toByteArray();
+        // Return whether the package signature matches any of the valid signatures
+        for (String signature : provider.signatures) {
+            final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
+            if (Arrays.equals(packageSignature, validSignature))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether the given package is enabled.
+     * This state can be changed by the user from Settings->Apps
+     */
+    private static boolean isEnabledPackage(PackageInfo packageInfo) {
+        return packageInfo.applicationInfo.enabled;
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index bae628a..3ef077b 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1214,7 +1214,10 @@
             window.type = windowState.mAttrs.type;
             window.layer = windowState.mLayer;
             window.token = windowState.mClient.asBinder();
-            window.title = windowState.mAttrs.getTitle();
+            window.title = windowState.mAttrs.accessibilityTitle;
+            if (window.title == null) {
+                window.title = windowState.mAttrs.getTitle();
+            }
             window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor;
 
             WindowState attachedWindow = windowState.mAttachedWindow;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e32c976..27126e6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7624,7 +7624,8 @@
         mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0
                 || volumeDownState > 0;
         try {
-            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0) {
+            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0
+                    || SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) != 0) {
                 int auditSafeMode = SystemProperties.getInt(ShutdownThread.AUDIT_SAFEMODE_PROPERTY, 0);
 
                 if (auditSafeMode == 0) {
@@ -7648,6 +7649,7 @@
         if (mSafeMode) {
             Log.i(TAG_WM, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState
                     + " dpad=" + dpadState + " trackball=" + trackballState + ")");
+            SystemProperties.set(ShutdownThread.RO_SAFEMODE_PROPERTY, "1");
         } else {
             Log.i(TAG_WM, "SAFE MODE not enabled");
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index de485de..7f40079 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2355,6 +2355,10 @@
         return mDragResizing;
     }
 
+    boolean isDockedResizing() {
+        return mDragResizing && getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+    }
+
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         final TaskStack stack = getStack();
         pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9e3a4db..9c25f63 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1125,13 +1125,21 @@
         final int top = w.mYOffset + w.mFrame.top;
 
         // Initialize the decor rect to the entire frame.
-        if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER) {
+        if (w.isDockedResizing() ||
+                (w.isChildWindow() && w.mAttachedWindow.isDockedResizing())) {
 
             // If we are resizing with the divider, the task bounds might be smaller than the
             // stack bounds. The system decor is used to clip to the task bounds, which we don't
             // want in this case in order to avoid holes.
+            //
+            // We take care to not shrink the width, for surfaces which are larger than
+            // the display region. Of course this area will not eventually be visible
+            // but if we truncate the width now, we will calculate incorrectly
+            // when adjusting to the stack bounds.
             final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
-            mSystemDecorRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+            mSystemDecorRect.set(0, 0,
+                    Math.max(width, displayInfo.logicalWidth),
+                    Math.max(height, displayInfo.logicalHeight));
         } else {
             mSystemDecorRect.set(0, 0, width, height);
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fdea84b..72eebb5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8078,7 +8078,8 @@
                     }
                 }
             }
-            return null;
+            // We're not specifying the device admin because there isn't one.
+            return intent;
         }
     }
 
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index ba74439..b7e7f90 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -21,7 +21,7 @@
 
 namespace aapt {
 
-void AnnotationProcessor::appendCommentLine(const std::string& comment) {
+void AnnotationProcessor::appendCommentLine(std::string& comment) {
     static const std::string sDeprecated = "@deprecated";
     static const std::string sSystemApi = "@SystemApi";
 
@@ -29,8 +29,14 @@
         mAnnotationBitMask |= kDeprecated;
     }
 
-    if (comment.find(sSystemApi) != std::string::npos) {
+    std::string::size_type idx = comment.find(sSystemApi);
+    if (idx != std::string::npos) {
         mAnnotationBitMask |= kSystemApi;
+        comment.erase(comment.begin() + idx, comment.begin() + idx + sSystemApi.size());
+    }
+
+    if (util::trimWhitespace(comment).empty()) {
+        return;
     }
 
     if (!mHasComments) {
@@ -46,7 +52,8 @@
     for (StringPiece16 line : util::tokenize(comment, u'\n')) {
         line = util::trimWhitespace(line);
         if (!line.empty()) {
-            appendCommentLine(util::utf16ToUtf8(line));
+            std::string utf8Line = util::utf16ToUtf8(line);
+            appendCommentLine(utf8Line);
         }
     }
 }
@@ -55,7 +62,8 @@
     for (StringPiece line : util::tokenize(comment, '\n')) {
         line = util::trimWhitespace(line);
         if (!line.empty()) {
-            appendCommentLine(line.toString());
+            std::string utf8Line = line.toString();
+            appendCommentLine(utf8Line);
         }
     }
 }
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index 0fc5b08..8309dd9 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -43,7 +43,6 @@
  *  /\*
  *   * This is meant to be hidden because
  *   * It is system api. Also it is @deprecated
- *   * @SystemApi
  *   *\/
  *
  * Output Annotations:
@@ -79,7 +78,7 @@
     bool mHasComments = false;
     uint32_t mAnnotationBitMask = 0;
 
-    void appendCommentLine(const std::string& line);
+    void appendCommentLine(std::string& line);
 };
 
 } // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index d3860a5..5a39add 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -14,59 +14,18 @@
  * limitations under the License.
  */
 
-#include "ResourceParser.h"
-#include "ResourceTable.h"
-#include "ResourceValues.h"
 #include "java/AnnotationProcessor.h"
-#include "test/Builders.h"
-#include "test/Context.h"
-#include "xml/XmlPullParser.h"
-
-#include <gtest/gtest.h>
+#include "test/Test.h"
 
 namespace aapt {
 
-struct AnnotationProcessorTest : public ::testing::Test {
-    std::unique_ptr<IAaptContext> mContext;
-    ResourceTable mTable;
-
-    void SetUp() override {
-        mContext = test::ContextBuilder().build();
-    }
-
-    ::testing::AssertionResult parse(const StringPiece& str) {
-        ResourceParserOptions options;
-        ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{}, ConfigDescription{},
-                              options);
-        std::stringstream in;
-        in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
-        xml::XmlPullParser xmlParser(in);
-        if (parser.parse(&xmlParser)) {
-            return ::testing::AssertionSuccess();
-        }
-        return ::testing::AssertionFailure();
-    }
-};
-
-TEST_F(AnnotationProcessorTest, EmitsDeprecated) {
-    const char* xmlInput = R"EOF(
-    <resources>
-      <declare-styleable name="foo">
-        <!-- Some comment, and it should contain
-             a marker word, something that marks
-             this resource as nor needed.
-             {@deprecated That's the marker! } -->
-        <attr name="autoText" format="boolean" />
-      </declare-styleable>
-    </resources>)EOF";
-
-    ASSERT_TRUE(parse(xmlInput));
-
-    Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/autoText");
-    ASSERT_NE(nullptr, attr);
+TEST(AnnotationProcessorTest, EmitsDeprecated) {
+    const char* comment = "Some comment, and it should contain a marker word, "
+                          "something that marks this resource as nor needed. "
+                          "{@deprecated That's the marker! }";
 
     AnnotationProcessor processor;
-    processor.appendComment(attr->getComment());
+    processor.appendComment(comment);
 
     std::stringstream result;
     processor.writeToStream(&result, "");
@@ -75,6 +34,19 @@
     EXPECT_NE(std::string::npos, annotations.find("@Deprecated"));
 }
 
+TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
+    AnnotationProcessor processor;
+    processor.appendComment("@SystemApi This is a system API");
+
+    std::stringstream result;
+    processor.writeToStream(&result, "");
+    std::string annotations = result.str();
+
+    EXPECT_NE(std::string::npos, annotations.find("@android.annotation.SystemApi"));
+    EXPECT_EQ(std::string::npos, annotations.find("@SystemApi"));
+    EXPECT_NE(std::string::npos, annotations.find("This is a system API"));
+}
+
 } // namespace aapt
 
 
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 53e0f6f..d45328f 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -125,7 +125,7 @@
     void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
         ClassMember::writeToStream(prefix, final, out);
 
-        *out << "public static final int[] " << mName << "={";
+        *out << prefix << "public static final int[] " << mName << "={";
 
         const auto begin = mElements.begin();
         const auto end = mElements.end();
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 32b8600..24347a1 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -188,8 +188,8 @@
 
 struct StyleableAttr {
     const Reference* attrRef;
-    std::shared_ptr<Attribute> attribute;
     std::string fieldName;
+    std::unique_ptr<SymbolTable::Symbol> symbol;
 };
 
 static bool lessStyleableAttr(const StyleableAttr& lhs, const StyleableAttr& rhs) {
@@ -245,8 +245,9 @@
         // legal values for this attribute.
         const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference(
                 mangledReference);
-        if (symbol) {
-            styleableAttr.attribute = symbol->attribute;
+        if (symbol && symbol->attribute) {
+            // Copy the symbol data structure because the returned instance can be destroyed.
+            styleableAttr.symbol = util::make_unique<SymbolTable::Symbol>(*symbol);
         }
         sortedAttributes.push_back(std::move(styleableAttr));
     }
@@ -273,6 +274,16 @@
                 "<tr><th>Attribute</th><th>Description</th></tr>\n";
 
         for (const StyleableAttr& entry : sortedAttributes) {
+            if (!entry.symbol) {
+                continue;
+            }
+
+            if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
+                    !entry.symbol->isPublic) {
+                // Don't write entries for non-public attributes.
+                continue;
+            }
+
             const ResourceName& attrName = entry.attrRef->name.value();
             styleableComment << "<tr><td>";
             styleableComment << "<code>{@link #"
@@ -284,14 +295,30 @@
             styleableComment << "</td>";
 
             styleableComment << "<td>";
-            if (entry.attribute) {
-                styleableComment << entry.attribute->getComment();
+
+            // Only use the comment up until the first '.'. This is to stay compatible with
+            // the way old AAPT did it (presumably to keep it short and to avoid including
+            // annotations like @hide which would affect this Styleable).
+            StringPiece16 attrCommentLine = entry.symbol->attribute->getComment();
+            auto iter = std::find(attrCommentLine.begin(), attrCommentLine.end(), u'.');
+            if (iter != attrCommentLine.end()) {
+                attrCommentLine = attrCommentLine.substr(
+                        0, (iter - attrCommentLine.begin()) + 1);
             }
-            styleableComment << "</td></tr>\n";
+            styleableComment << attrCommentLine << "</td></tr>\n";
         }
         styleableComment << "</table>\n";
 
         for (const StyleableAttr& entry : sortedAttributes) {
+            if (!entry.symbol) {
+                continue;
+            }
+
+            if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
+                    !entry.symbol->isPublic) {
+                // Don't write entries for non-public attributes.
+                continue;
+            }
             styleableComment << "@see #" << entry.fieldName << "\n";
         }
 
@@ -310,6 +337,17 @@
     // Now we emit the indices into the array.
     for (size_t i = 0; i < attrCount; i++) {
         const StyleableAttr& styleableAttr = sortedAttributes[i];
+
+        if (!styleableAttr.symbol) {
+            continue;
+        }
+
+        if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
+                !styleableAttr.symbol->isPublic) {
+            // Don't write entries for non-public attributes.
+            continue;
+        }
+
         const ResourceName& attrName = styleableAttr.attrRef->name.value();
 
         StringPiece16 packageName = attrName.package;
@@ -323,8 +361,8 @@
         AnnotationProcessor* attrProcessor = indexMember->getCommentBuilder();
 
         StringPiece16 comment = styleableAttr.attrRef->getComment();
-        if (styleableAttr.attribute && comment.empty()) {
-            comment = styleableAttr.attribute->getComment();
+        if (styleableAttr.symbol->attribute && comment.empty()) {
+            comment = styleableAttr.symbol->attribute->getComment();
         }
 
         if (!comment.empty()) {
@@ -342,10 +380,8 @@
 
         attrProcessor->appendNewLine();
 
-        if (styleableAttr.attribute) {
-            addAttributeFormatDoc(attrProcessor, styleableAttr.attribute.get());
-            attrProcessor->appendNewLine();
-        }
+        addAttributeFormatDoc(attrProcessor, styleableAttr.symbol->attribute.get());
+        attrProcessor->appendNewLine();
 
         std::stringstream doclavaName;
         doclavaName << "@attr name " << packageName << ":" << attrName.entry;;
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 370e78a..7d0aa83 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -43,7 +43,8 @@
     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
             .setPackageId(u"android", 0x01)
             .addSimple(u"@android:id/hey-man", ResourceId(0x01020000))
-            .addSimple(u"@android:attr/cool.attr", ResourceId(0x01010000))
+            .addValue(u"@android:attr/cool.attr", ResourceId(0x01010000),
+                      test::AttributeBuilder(false).build())
             .addValue(u"@android:styleable/hey.dude", ResourceId(0x01030000),
                       test::StyleableBuilder()
                               .addItem(u"@android:attr/cool.attr", ResourceId(0x01010000))
@@ -199,8 +200,10 @@
     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
                 .setPackageId(u"android", 0x01)
                 .setPackageId(u"com.lib", 0x02)
-                .addSimple(u"@android:attr/bar", ResourceId(0x01010000))
-                .addSimple(u"@com.lib:attr/bar", ResourceId(0x02010000))
+                .addValue(u"@android:attr/bar", ResourceId(0x01010000),
+                          test::AttributeBuilder(false).build())
+                .addValue(u"@com.lib:attr/bar", ResourceId(0x02010000),
+                           test::AttributeBuilder(false).build())
                 .addValue(u"@android:styleable/foo", ResourceId(0x01030000),
                           test::StyleableBuilder()
                                   .addItem(u"@android:attr/bar", ResourceId(0x01010000))
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index e7210db..d3bca70 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -126,7 +126,6 @@
 R"EOF(    /**
      * This is a private permission for system only!
      * @hide
-     * @SystemApi
      */
     @android.annotation.SystemApi
     public static final String SECRET="android.permission.SECRET";)EOF";
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 0a6a4a5..e684bb0 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -51,9 +51,28 @@
 class SymbolTable {
 public:
     struct Symbol {
+        Symbol() : Symbol(Maybe<ResourceId>{}) {
+        }
+
+        Symbol(const Maybe<ResourceId>& i) : Symbol(i, nullptr) {
+        }
+
+        Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr) :
+                Symbol(i, attr, false) {
+        }
+
+        Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr, bool pub) :
+                id(i), attribute(attr), isPublic(pub) {
+        }
+
+        Symbol(const Symbol&) = default;
+        Symbol(Symbol&&) = default;
+        Symbol& operator=(const Symbol&) = default;
+        Symbol& operator=(Symbol&&) = default;
+
         Maybe<ResourceId> id;
         std::shared_ptr<Attribute> attribute;
-        bool isPublic;
+        bool isPublic = false;
     };
 
     SymbolTable() : mCache(200), mIdCache(200) {