Add permission meta-state flags to support grant/revoke permission policy.

We now maintain a mata-state with each permission in the form of flags
specyfying the policy for this permission. This enables support of the
following use cases:

1. The user denies a permission with prejudice in which case an app cannot
   request the permission at runtime. If an app requests such a permssion
   it gets a denial unless the user grants the permission from settings.

2. A legacy app with disabled app-ops being upgraded to support runtime
   permissions. The disabled app ops are converted to permission revocations.
   The app ops manager is a part of the activity manger which sits on top
   of the package manager, hence the latter cannot have a dependency on the
   former. To avoid this the package installer which is the global
   permission managment authority marks the permission as revoked on
   upgrade and the package manager revokes it on upgrade.

3. A device policy fixing a permission in a granted or revoked state. This
   additional information is folded in the meta-state flags and neither
   apps can request such permissions if revoked not the user can change
   the permission state in the UI.

Change-Id: I443e8a7bb94bfcb4ff6003d158e1408c26149811
diff --git a/api/system-current.txt b/api/system-current.txt
index 79908fa..53db75d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9505,6 +9505,7 @@
     method public abstract android.content.pm.PackageInstaller getPackageInstaller();
     method public abstract java.lang.String[] getPackagesForUid(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
+    method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
@@ -9522,7 +9523,7 @@
     method public abstract android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public abstract void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9538,10 +9539,11 @@
     method public abstract android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
     method public abstract android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
     method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
-    method public abstract void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public abstract void verifyPendingInstall(int, int);
     field public static final java.lang.String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -9627,6 +9629,10 @@
     field public static final java.lang.String FEATURE_WEBVIEW = "android.software.webview";
     field public static final java.lang.String FEATURE_WIFI = "android.hardware.wifi";
     field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
+    field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4
+    field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+    field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
+    field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
     field public static final int GET_ACTIVITIES = 1; // 0x1
     field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
     field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
@@ -9679,6 +9685,7 @@
     field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99
     field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a
     field public static final int INSTALL_SUCCEEDED = 1; // 0x1
+    field public static final int MASK_PERMISSION_FLAGS = 15; // 0xf
     field public static final int MATCH_ALL = 131072; // 0x20000
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
@@ -9699,6 +9706,9 @@
     ctor public PackageManager.NameNotFoundException(java.lang.String);
   }
 
+  public static abstract class PackageManager.PermissionFlags implements java.lang.annotation.Annotation {
+  }
+
   public class PackageStats implements android.os.Parcelable {
     ctor public PackageStats(java.lang.String);
     ctor public PackageStats(android.os.Parcel);
@@ -34042,6 +34052,7 @@
     method public android.content.pm.PackageInstaller getPackageInstaller();
     method public java.lang.String[] getPackagesForUid(int);
     method public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
+    method public int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
     method public android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
@@ -34059,7 +34070,7 @@
     method public android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -34075,11 +34086,12 @@
     method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
     method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
     method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
-    method public void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public boolean setDefaultBrowserPackageName(java.lang.String, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public void verifyPendingInstall(int, int);
   }
 
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 39de1dc7..eb834f2 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1609,9 +1609,9 @@
 
         try {
             if (grant) {
-                mPm.grantPermission(pkg, perm, userId);
+                mPm.grantRuntimePermission(pkg, perm, userId);
             } else {
-                mPm.revokePermission(pkg, perm, userId);
+                mPm.revokeRuntimePermission(pkg, perm, userId);
             }
             return 0;
         } catch (RemoteException e) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 07eee12..04f6430 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -446,18 +446,40 @@
     }
 
     @Override
-    public void grantPermission(String packageName, String permissionName, UserHandle user) {
+    public void grantRuntimePermission(String packageName, String permissionName,
+            UserHandle user) {
         try {
-            mPM.grantPermission(packageName, permissionName, user.getIdentifier());
+            mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
     }
 
     @Override
-    public void revokePermission(String packageName, String permissionName, UserHandle user) {
+    public void revokeRuntimePermission(String packageName, String permissionName,
+            UserHandle user) {
         try {
-            mPM.revokePermission(packageName, permissionName, user.getIdentifier());
+            mPM.revokeRuntimePermission(packageName, permissionName, user.getIdentifier());
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    @Override
+    public int getPermissionFlags(String permissionName, String packageName, UserHandle user) {
+        try {
+            return mPM.getPermissionFlags(permissionName, packageName, user.getIdentifier());
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    @Override
+    public void updatePermissionFlags(String permissionName, String packageName,
+            int flagMask, int flagValues, UserHandle user) {
+        try {
+            mPM.updatePermissionFlags(permissionName, packageName, flagMask,
+                    flagValues, user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 94b0223..ddff782 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -96,9 +96,14 @@
 
     void removePermission(String name);
 
-    void grantPermission(String packageName, String permissionName, int userId);
+    void grantRuntimePermission(String packageName, String permissionName, int userId);
 
-    void revokePermission(String packageName, String permissionName, int userId);
+    void revokeRuntimePermission(String packageName, String permissionName, int userId);
+
+    int getPermissionFlags(String permissionName, String packageName, int userId);
+
+    void updatePermissionFlags(String permissionName, String packageName, int flagMask,
+            int flagValues, int userId);
 
     boolean isProtectedBroadcast(String actionName);
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 51fa075..6401fe6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1888,6 +1888,57 @@
     public static final String EXTRA_FAILURE_EXISTING_PERMISSION
             = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
 
+   /**
+    * Permission flag: The permission is set in its current state
+    * by the user and apps can still request it at runtime.
+    *
+    * @hide
+    */
+    @SystemApi
+    public static final int FLAG_PERMISSION_USER_SET = 1 << 0;
+
+    /**
+     * Permission flag: The permission is set in its current state
+     * by the user and it is fixed, i.e. apps can no longer request
+     * this permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_USER_FIXED =  1 << 1;
+
+    /**
+     * Permission flag: The permission is set in its current state
+     * by device policy and neither apps nor the user can change
+     * its state.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_POLICY_FIXED =  1 << 2;
+
+    /**
+     * Permission flag: The permission is set in a granted state but
+     * access to resources it guards is restricted by other means to
+     * enable revoking a permission on legacy apps that do not support
+     * runtime permissions. If this permission is upgraded to runtime
+     * because the app was updated to support runtime permissions, the
+     * the permission will be revoked in the upgrade process.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE =  1 << 3;
+
+
+    /**
+     * Mask for all permission flags.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MASK_PERMISSION_FLAGS = 0xF;
+
     /**
      * Retrieve overall information about an application package that is
      * installed on the system.
@@ -2374,6 +2425,20 @@
      */
     public abstract void removePermission(String name);
 
+
+    /**
+     * Permission flags set when granting or revoking a permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    @IntDef({FLAG_PERMISSION_USER_SET,
+            FLAG_PERMISSION_USER_FIXED,
+            FLAG_PERMISSION_POLICY_FIXED,
+            FLAG_PERMISSION_REVOKE_ON_UPGRADE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionFlags {}
+
     /**
      * Grant a runtime permission to an application which the application does not
      * already have. The permission must have been requested by the application.
@@ -2389,19 +2454,20 @@
      * @param permissionName The permission name to grant.
      * @param user The user for which to grant the permission.
      *
-     * @see #revokePermission(String, String, android.os.UserHandle)
+     * @see #revokeRuntimePermission(String, String, android.os.UserHandle)
+     * @see android.content.pm.PackageManager.PermissionFlags
      *
      * @hide
      */
     @SystemApi
-    public abstract void grantPermission(@NonNull String packageName,
+    public abstract void grantRuntimePermission(@NonNull String packageName,
             @NonNull String permissionName, @NonNull UserHandle user);
 
     /**
      * Revoke a runtime permission that was previously granted by {@link
-     * #grantPermission(String, String, android.os.UserHandle)}. The permission
-     * must have been requested by and granted to the application. If the
-     * application is not allowed to hold the permission, a {@link
+     * #grantRuntimePermission(String, String, android.os.UserHandle)}. The
+     * permission must have been requested by and granted to the application.
+     * If the application is not allowed to hold the permission, a {@link
      * java.lang.SecurityException} is thrown.
      * <p>
      * <strong>Note: </strong>Using this API requires holding
@@ -2413,15 +2479,47 @@
      * @param permissionName The permission name to revoke.
      * @param user The user for which to revoke the permission.
      *
-     * @see #grantPermission(String, String, android.os.UserHandle)
+     * @see #grantRuntimePermission(String, String, android.os.UserHandle)
+     * @see android.content.pm.PackageManager.PermissionFlags
      *
      * @hide
      */
     @SystemApi
-    public abstract void revokePermission(@NonNull String packageName,
+    public abstract void revokeRuntimePermission(@NonNull String packageName,
             @NonNull String permissionName, @NonNull UserHandle user);
 
     /**
+     * Gets the state flags associated with a permission.
+     *
+     * @param permissionName The permission for which to get the flags.
+     * @param packageName The package name for which to get the flags.
+     * @param user The user for which to get permission flags.
+     * @return The permission flags.
+     *
+     * @hide
+     */
+    @SystemApi
+    public abstract @PermissionFlags int getPermissionFlags(String permissionName,
+            String packageName, @NonNull UserHandle user);
+
+    /**
+     * Updates the flags associated with a permission by replacing the flags in
+     * the specified mask with the provided flag values.
+     *
+     * @param permissionName The permission for which to update the flags.
+     * @param packageName The package name for which to update the flags.
+     * @param flagMask The flags which to replace.
+     * @param flagValues The flags with which to replace.
+     * @param user The user for which to update the permission flags.
+     *
+     * @hide
+     */
+    @SystemApi
+    public abstract void updatePermissionFlags(String permissionName,
+            String packageName, @PermissionFlags int flagMask, int flagValues,
+            @NonNull UserHandle user);
+
+    /**
      * Returns an {@link android.content.Intent} suitable for passing to
      * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
      * which prompts the user to grant permissions to this application.
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 6feb94b..bb4a948 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -244,7 +244,7 @@
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
                     PackageManager pm = getContext().getPackageManager();
-                    pm.revokePermission(mPackageName, mPerm.name,
+                    pm.revokeRuntimePermission(mPackageName, mPerm.name,
                             new UserHandle(mContext.getUserId()));
                     PermissionItemView.this.setVisibility(View.GONE);
                 }
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 30f8b37..18407c9 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -20,8 +20,6 @@
 import android.content.pm.PermissionInfo;
 import android.os.UserHandle;
 
-import com.android.internal.util.ArrayUtils;
-
 final class BasePermission {
     final static int TYPE_NORMAL = 0;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a120c1f..aeaa272 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -206,6 +206,7 @@
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
 import com.android.server.pm.Settings.DatabaseVersion;
+import com.android.server.pm.PermissionsState.PermissionState;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -304,6 +305,8 @@
 
     static final int REMOVE_CHATTY = 1<<16;
 
+    private static final int[] EMPTY_INT_ARRAY = new int[0];
+
     /**
      * Timeout (in milliseconds) after which the watchdog should declare that
      * our handler thread is wedged.  The usual default for such things is one
@@ -3139,18 +3142,26 @@
         }
     }
 
+    private static void enforceOnlySystemUpdatesPermissionPolicyFlags(int flagMask, int flagValues) {
+        if (((flagMask & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0
+                || (flagValues & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0)
+                && getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only the system can modify policy flags");
+        }
+    }
+
     @Override
-    public void grantPermission(String packageName, String name, int userId) {
+    public void grantRuntimePermission(String packageName, String name, int userId) {
         if (!sUserManager.exists(userId)) {
             return;
         }
 
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
-                "grantPermission");
+                "grantRuntimePermission");
 
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
-                "grantPermission");
+                "grantRuntimePermission");
 
         boolean gidsChanged = false;
         final SettingBase sb;
@@ -3197,17 +3208,17 @@
     }
 
     @Override
-    public void revokePermission(String packageName, String name, int userId) {
+    public void revokeRuntimePermission(String packageName, String name, int userId) {
         if (!sUserManager.exists(userId)) {
             return;
         }
 
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
-                "revokePermission");
+                "revokeRuntimePermission");
 
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
-                "revokePermission");
+                "revokeRuntimePermission");
 
         final SettingBase sb;
 
@@ -3236,7 +3247,7 @@
                 return;
             }
 
-            // Critical, after this call all should never have the permission.
+            // Critical, after this call app should never have the permission.
             mSettings.writeRuntimePermissionsForUserLPr(userId, true);
         }
 
@@ -3244,6 +3255,86 @@
     }
 
     @Override
+    public int getPermissionFlags(String name, String packageName, int userId) {
+        if (!sUserManager.exists(userId)) {
+            return 0;
+        }
+
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+                "getPermissionFlags");
+
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "getPermissionFlags");
+
+        synchronized (mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+
+            final BasePermission bp = mSettings.mPermissions.get(name);
+            if (bp == null) {
+                throw new IllegalArgumentException("Unknown permission: " + name);
+            }
+
+            SettingBase sb = (SettingBase) pkg.mExtras;
+            if (sb == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+
+            PermissionsState permissionsState = sb.getPermissionsState();
+            return permissionsState.getPermissionFlags(name, userId);
+        }
+    }
+
+    @Override
+    public void updatePermissionFlags(String name, String packageName, int flagMask,
+            int flagValues, int userId) {
+        if (!sUserManager.exists(userId)) {
+            return;
+        }
+
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+                "updatePermissionFlags");
+
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "updatePermissionFlags");
+
+        enforceOnlySystemUpdatesPermissionPolicyFlags(flagMask, flagValues);
+
+        synchronized (mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+
+            final BasePermission bp = mSettings.mPermissions.get(name);
+            if (bp == null) {
+                throw new IllegalArgumentException("Unknown permission: " + name);
+            }
+
+            SettingBase sb = (SettingBase) pkg.mExtras;
+            if (sb == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+
+            PermissionsState permissionsState = sb.getPermissionsState();
+
+            if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) {
+                // Install and runtime permissions are stored in different places,
+                // so figure out what permission changed and persist the change.
+                if (permissionsState.getInstallPermissionState(name) != null) {
+                    scheduleWriteSettingsLocked();
+                } else if (permissionsState.getRuntimePermissionState(name, userId) != null) {
+                    mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+                }
+            }
+        }
+    }
+
+    @Override
     public boolean isProtectedBroadcast(String actionName) {
         synchronized (mPackages) {
             return mProtectedBroadcasts.contains(actionName);
@@ -7530,8 +7621,8 @@
 
         final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
 
-        int[] upgradeUserIds = PermissionsState.USERS_NONE;
-        int[] changedRuntimePermissionUserIds = PermissionsState.USERS_NONE;
+        int[] upgradeUserIds = EMPTY_INT_ARRAY;
+        int[] changedRuntimePermissionUserIds = EMPTY_INT_ARRAY;
 
         boolean changedInstallPermission = false;
 
@@ -7657,11 +7748,18 @@
                         // Grant previously granted runtime permissions.
                         for (int userId : UserManagerService.getInstance().getUserIds()) {
                             if (origPermissions.hasRuntimePermission(bp.name, userId)) {
+                                PermissionState permissionState = origPermissions
+                                        .getRuntimePermissionState(bp.name, userId);
+                                final int flags = permissionState.getFlags();
                                 if (permissionsState.grantRuntimePermission(bp, userId) ==
                                         PermissionsState.PERMISSION_OPERATION_FAILURE) {
                                     // If we cannot put the permission as it was, we have to write.
                                     changedRuntimePermissionUserIds = ArrayUtils.appendInt(
                                             changedRuntimePermissionUserIds, userId);
+                                } else {
+                                    // Propagate the permission flags.
+                                    permissionsState.updatePermissionFlags(bp, userId,
+                                            flags, flags);
                                 }
                             }
                         }
@@ -7669,13 +7767,28 @@
 
                     case GRANT_UPGRADE: {
                         // Grant runtime permissions for a previously held install permission.
-                        permissionsState.revokeInstallPermission(bp);
-                        for (int userId : upgradeUserIds) {
-                            if (permissionsState.grantRuntimePermission(bp, userId) !=
-                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                // If we granted the permission, we have to write.
-                                changedRuntimePermissionUserIds = ArrayUtils.appendInt(
-                                        changedRuntimePermissionUserIds, userId);
+                        PermissionState permissionState = origPermissions
+                                .getInstallPermissionState(bp.name);
+                        final int flags = permissionState != null ? permissionState.getFlags() : 0;
+
+                        origPermissions.revokeInstallPermission(bp);
+                        // We will be transferring the permission flags, so clear them.
+                        origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL,
+                                PackageManager.MASK_PERMISSION_FLAGS, 0);
+
+                        // If the permission is not to be promoted to runtime we ignore it and
+                        // also its other flags as they are not applicable to install permissions.
+                        if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) {
+                            for (int userId : upgradeUserIds) {
+                                if (permissionsState.grantRuntimePermission(bp, userId) !=
+                                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                    // Transfer the permission flags.
+                                    permissionsState.updatePermissionFlags(bp, userId,
+                                            flags, flags);
+                                    // If we granted the permission, we have to write.
+                                    changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+                                            changedRuntimePermissionUserIds, userId);
+                                }
                             }
                         }
                     } break;
@@ -7692,6 +7805,9 @@
             } else {
                 if (permissionsState.revokeInstallPermission(bp) !=
                         PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    // Also drop the permission flags.
+                    permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+                            PackageManager.MASK_PERMISSION_FLAGS, 0);
                     changedInstallPermission = true;
                     Slog.i(TAG, "Un-granting permission " + perm
                             + " from package " + pkg.packageName
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
index 3749957..171a50d 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -20,10 +20,13 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
+import android.util.SparseArray;
 import com.android.internal.util.ArrayUtils;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -55,15 +58,8 @@
     /** The permission operation failed. */
     public static final int PERMISSION_OPERATION_FAILURE = 3;
 
-    public static final int[] USERS_ALL = {UserHandle.USER_ALL};
-
-    public static final int[] USERS_NONE = {};
-
     private static final int[] NO_GIDS = {};
 
-    private static final int FLAG_INSTALL_PERMISSIONS = 1 << 0;
-    private static final int FLAG_RUNTIME_PERMISSIONS = 1 << 1;
-
     private ArrayMap<String, PermissionData> mPermissions;
 
     private int[] mGlobalGids = NO_GIDS;
@@ -147,14 +143,16 @@
     }
 
     /**
-     * Grant a runtime permission.
+     * Grant a runtime permission for a given device user.
      *
      * @param permission The permission to grant.
+     * @param userId The device user id.
      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
      *     #PERMISSION_OPERATION_FAILURE}.
      */
     public int grantRuntimePermission(BasePermission permission, int userId) {
+        enforceValidUserId(userId);
         if (userId == UserHandle.USER_ALL) {
             return PERMISSION_OPERATION_FAILURE;
         }
@@ -162,15 +160,18 @@
     }
 
     /**
-     * Revoke a runtime permission for a given device user.
+     *  Revoke a runtime permission for a given device user.
      *
      * @param permission The permission to revoke.
      * @param userId The device user id.
      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
      *     #PERMISSION_OPERATION_FAILURE}.
+     *
+     * @see android.content.pm.PackageManager.PermissionFlags
      */
     public int revokeRuntimePermission(BasePermission permission, int userId) {
+        enforceValidUserId(userId);
         if (userId == UserHandle.USER_ALL) {
             return PERMISSION_OPERATION_FAILURE;
         }
@@ -178,17 +179,6 @@
     }
 
     /**
-     * Gets whether this state has a given permission, regardless if
-     * it is install time or runtime one.
-     *
-     * @param name The permission name.
-     * @return Whether this state has the permission.
-     */
-    public boolean hasPermission(String name) {
-        return mPermissions != null && mPermissions.get(name) != null;
-    }
-
-    /**
      * Gets whether this state has a given runtime permission for a
      * given device user id.
      *
@@ -197,6 +187,7 @@
      * @return Whether this state has the permission.
      */
     public boolean hasRuntimePermission(String name, int userId) {
+        enforceValidUserId(userId);
         return !hasInstallPermission(name) && hasPermission(name, userId);
     }
 
@@ -211,36 +202,6 @@
     }
 
     /**
-     * Revokes a permission for all users regardless if it is an install or
-     * a runtime permission.
-     *
-     * @param permission The permission to revoke.
-     * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
-     *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
-     *     #PERMISSION_OPERATION_FAILURE}.
-     */
-    public int revokePermission(BasePermission permission) {
-        if (!hasPermission(permission.name)) {
-            return PERMISSION_OPERATION_FAILURE;
-        }
-
-        int result = PERMISSION_OPERATION_SUCCESS;
-
-        PermissionData permissionData = mPermissions.get(permission.name);
-        for (int userId : permissionData.getUserIds()) {
-            if (revokePermission(permission, userId)
-                    == PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
-                result = PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
-                break;
-            }
-        }
-
-        mPermissions.remove(permission.name);
-
-        return result;
-    }
-
-    /**
      * Gets whether the state has a given permission for the specified
      * user, regardless if this is an install or a runtime permission.
      *
@@ -256,20 +217,7 @@
         }
 
         PermissionData permissionData = mPermissions.get(name);
-        return permissionData != null && permissionData.hasUserId(userId);
-    }
-
-    /**
-     * Gets all permissions regardless if they are install or runtime.
-     *
-     * @return The permissions or an empty set.
-     */
-    public Set<String> getPermissions() {
-        if (mPermissions != null) {
-            return mPermissions.keySet();
-        }
-
-        return Collections.emptySet();
+        return permissionData != null && permissionData.isGranted(userId);
     }
 
     /**
@@ -280,25 +228,122 @@
      * @return The permissions or an empty set.
      */
     public Set<String> getPermissions(int userId) {
-        return getPermissionsInternal(FLAG_INSTALL_PERMISSIONS | FLAG_RUNTIME_PERMISSIONS, userId);
+        enforceValidUserId(userId);
+
+        if (mPermissions == null) {
+            return Collections.emptySet();
+        }
+
+        Set<String> permissions = new ArraySet<>();
+
+        final int permissionCount = mPermissions.size();
+        for (int i = 0; i < permissionCount; i++) {
+            String permission = mPermissions.keyAt(i);
+
+            if (hasInstallPermission(permission)) {
+                permissions.add(permission);
+            }
+
+            if (userId != UserHandle.USER_ALL) {
+                if (hasRuntimePermission(permission, userId)) {
+                    permissions.add(permission);
+                }
+            }
+        }
+
+        return permissions;
     }
 
     /**
-     * Gets all runtime permissions.
+     * Gets the state for an install permission or null if no such.
      *
-     * @return The permissions or an empty set.
+     * @param name The permission name.
+     * @return The permission state.
      */
-    public Set<String> getRuntimePermissions(int userId) {
-        return getPermissionsInternal(FLAG_RUNTIME_PERMISSIONS, userId);
+    public PermissionState getInstallPermissionState(String name) {
+        return getPermissionState(name, UserHandle.USER_ALL);
     }
 
     /**
-     * Gets all install permissions.
+     * Gets the state for a runtime permission or null if no such.
      *
-     * @return The permissions or an empty set.
+     * @param name The permission name.
+     * @param userId The device user id.
+     * @return The permission state.
      */
-    public Set<String> getInstallPermissions() {
-        return getPermissionsInternal(FLAG_INSTALL_PERMISSIONS, UserHandle.USER_ALL);
+    public PermissionState getRuntimePermissionState(String name, int userId) {
+        enforceValidUserId(userId);
+        return getPermissionState(name, userId);
+    }
+
+    /**
+     * Gets all install permission states.
+     *
+     * @return The permission states or an empty set.
+     */
+    public List<PermissionState> getInstallPermissionStates() {
+        return getPermissionStatesInternal(UserHandle.USER_ALL);
+    }
+
+    /**
+     * Gets all runtime permission states.
+     *
+     * @return The permission states or an empty set.
+     */
+    public List<PermissionState> getRuntimePermissionStates(int userId) {
+        enforceValidUserId(userId);
+        return getPermissionStatesInternal(userId);
+    }
+
+    /**
+     * Gets the flags for a permission regardless if it is install or
+     * runtime permission.
+     *
+     * @param name The permission name.
+     * @return The permission state or null if no such.
+     */
+    public int getPermissionFlags(String name, int userId) {
+        PermissionState installPermState = getInstallPermissionState(name);
+        if (installPermState != null) {
+            return installPermState.getFlags();
+        }
+        PermissionState runtimePermState = getRuntimePermissionState(name, userId);
+        if (runtimePermState != null) {
+            return runtimePermState.getFlags();
+        }
+        return 0;
+    }
+
+    /**
+     * Update the flags associated with a given permission.
+     * @param permission The permission whose flags to update.
+     * @param userId The user for which to update.
+     * @param flagMask Mask for which flags to change.
+     * @param flagValues New values for the mask flags.
+     * @return Whether the permission flags changed.
+     */
+    public boolean updatePermissionFlags(BasePermission permission, int userId,
+            int flagMask, int flagValues) {
+        enforceValidUserId(userId);
+
+        final boolean mayChangeFlags = flagValues != 0 || flagMask != 0;
+
+        if (mPermissions == null) {
+            if (!mayChangeFlags) {
+                return false;
+            }
+            ensurePermissionData(permission);
+        }
+
+        PermissionData permissionData = mPermissions.get(permission.name);
+        if (permissionData == null) {
+            if (!mayChangeFlags) {
+                return false;
+            }
+            permissionData = ensurePermissionData(permission);
+        }
+
+        return permissionData.updateFlags(userId, flagMask, flagValues);
     }
 
     /**
@@ -357,36 +402,37 @@
         mPermissions = null;
     }
 
-    private Set<String> getPermissionsInternal(int flags, int userId) {
+    private PermissionState getPermissionState(String name, int userId) {
+        if (mPermissions == null) {
+            return null;
+        }
+        PermissionData permissionData = mPermissions.get(name);
+        if (permissionData == null) {
+            return null;
+        }
+        return permissionData.getPermissionState(userId);
+    }
+
+    private List<PermissionState> getPermissionStatesInternal(int userId) {
         enforceValidUserId(userId);
 
         if (mPermissions == null) {
-            return Collections.emptySet();
+            return Collections.emptyList();
         }
 
-        if (userId == UserHandle.USER_ALL) {
-            flags = FLAG_INSTALL_PERMISSIONS;
-        }
-
-        Set<String> permissions = new ArraySet<>();
+        List<PermissionState> permissionStates = new ArrayList<>();
 
         final int permissionCount = mPermissions.size();
         for (int i = 0; i < permissionCount; i++) {
-            String permission = mPermissions.keyAt(i);
+            PermissionData permissionData = mPermissions.valueAt(i);
 
-            if ((flags & FLAG_INSTALL_PERMISSIONS) != 0) {
-                if (hasInstallPermission(permission)) {
-                    permissions.add(permission);
-                }
-            }
-            if ((flags & FLAG_RUNTIME_PERMISSIONS) != 0) {
-                if (hasRuntimePermission(permission, userId)) {
-                    permissions.add(permission);
-                }
+            PermissionState permissionState = permissionData.getPermissionState(userId);
+            if (permissionState != null) {
+                permissionStates.add(permissionState);
             }
         }
 
-        return  permissions;
+        return  permissionStates;
     }
 
     private int grantPermission(BasePermission permission, int userId) {
@@ -397,17 +443,9 @@
         final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
         final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
 
-        if (mPermissions == null) {
-            mPermissions = new ArrayMap<>();
-        }
+        PermissionData permissionData = ensurePermissionData(permission);
 
-        PermissionData permissionData = mPermissions.get(permission.name);
-        if (permissionData == null) {
-            permissionData = new PermissionData(permission);
-            mPermissions.put(permission.name, permissionData);
-        }
-
-        if (!permissionData.addUserId(userId)) {
+        if (!permissionData.grant(userId)) {
             return PERMISSION_OPERATION_FAILURE;
         }
 
@@ -431,16 +469,12 @@
 
         PermissionData permissionData = mPermissions.get(permission.name);
 
-        if (!permissionData.removeUserId(userId)) {
+        if (!permissionData.revoke(userId)) {
             return PERMISSION_OPERATION_FAILURE;
         }
 
-        if (permissionData.getUserIds() == USERS_NONE) {
-            mPermissions.remove(permission.name);
-        }
-
-        if (mPermissions.isEmpty()) {
-            mPermissions = null;
+        if (permissionData.isDefault()) {
+            ensureNoPermissionData(permission.name);
         }
 
         if (hasGids) {
@@ -468,9 +502,31 @@
         }
     }
 
+    private PermissionData ensurePermissionData(BasePermission permission) {
+        if (mPermissions == null) {
+            mPermissions = new ArrayMap<>();
+        }
+        PermissionData permissionData = mPermissions.get(permission.name);
+        if (permissionData == null) {
+            permissionData = new PermissionData(permission);
+            mPermissions.put(permission.name, permissionData);
+        }
+        return permissionData;
+    }
+
+    private void ensureNoPermissionData(String name) {
+        if (mPermissions == null) {
+            return;
+        }
+        mPermissions.remove(name);
+        if (mPermissions.isEmpty()) {
+            mPermissions = null;
+        }
+    }
+
     private static final class PermissionData {
         private final BasePermission mPerm;
-        private int[] mUserIds = USERS_NONE;
+        private SparseArray<PermissionState> mUserStates = new SparseArray<>();
 
         public PermissionData(BasePermission perm) {
             mPerm = perm;
@@ -478,11 +534,11 @@
 
         public PermissionData(PermissionData other) {
             this(other.mPerm);
-
-            if (other.mUserIds == USERS_ALL || other.mUserIds == USERS_NONE) {
-                mUserIds = other.mUserIds;
-            } else {
-                mUserIds = Arrays.copyOf(other.mUserIds, other.mUserIds.length);
+            final int otherStateCount = other.mUserStates.size();
+            for (int i = 0; i < otherStateCount; i++) {
+                final int otherUserId = other.mUserStates.keyAt(i);
+                PermissionState otherState = other.mUserStates.valueAt(i);
+                mUserStates.put(otherUserId, new PermissionState(otherState));
             }
         }
 
@@ -490,53 +546,146 @@
             return mPerm.computeGids(userId);
         }
 
-        public int[] getUserIds() {
-            return mUserIds;
-        }
-
-        public boolean hasUserId(int userId) {
-            if (mUserIds == USERS_ALL) {
-                return true;
+        public boolean isGranted(int userId) {
+            if (isInstallPermission()) {
+                userId = UserHandle.USER_ALL;
             }
 
-            if (userId != UserHandle.USER_ALL) {
-                return ArrayUtils.contains(mUserIds, userId);
+            PermissionState userState = mUserStates.get(userId);
+            if (userState == null) {
+                return false;
+            }
+
+            return userState.mGranted;
+        }
+
+        public boolean grant(int userId) {
+            if (!isCompatibleUserId(userId)) {
+                return false;
+            }
+
+            if (isGranted(userId)) {
+                return false;
+            }
+
+            PermissionState userState = mUserStates.get(userId);
+            if (userState == null) {
+                userState = new PermissionState(mPerm.name);
+                mUserStates.put(userId, userState);
+            }
+
+            userState.mGranted = true;
+
+            return true;
+        }
+
+        public boolean revoke(int userId) {
+            if (!isCompatibleUserId(userId)) {
+                return false;
+            }
+
+            if (!isGranted(userId)) {
+                return false;
+            }
+
+            PermissionState userState = mUserStates.get(userId);
+            userState.mGranted = false;
+
+            if (userState.isDefault()) {
+                mUserStates.remove(userId);
+            }
+
+            return true;
+        }
+
+        public PermissionState getPermissionState(int userId) {
+            return mUserStates.get(userId);
+        }
+
+        public int getFlags(int userId) {
+            PermissionState userState = mUserStates.get(userId);
+            if (userState != null) {
+                return userState.mFlags;
+            }
+            return 0;
+        }
+
+        public boolean isDefault() {
+            return mUserStates.size() <= 0;
+        }
+
+        public static boolean isInstallPermissionKey(int userId) {
+            return userId == UserHandle.USER_ALL;
+        }
+
+        public boolean updateFlags(int userId, int flagMask, int flagValues) {
+            if (isInstallPermission()) {
+                userId = UserHandle.USER_ALL;
+            }
+
+            if (!isCompatibleUserId(userId)) {
+                return false;
+            }
+
+            final int newFlags = flagValues & flagMask;
+
+            PermissionState userState = mUserStates.get(userId);
+            if (userState != null) {
+                final int oldFlags = userState.mFlags;
+                userState.mFlags = (userState.mFlags & ~flagMask) | newFlags;
+                if (userState.isDefault()) {
+                    mUserStates.remove(userId);
+                }
+                return userState.mFlags != oldFlags;
+            } else if (newFlags != 0) {
+                userState = new PermissionState(mPerm.name);
+                userState.mFlags = newFlags;
+                mUserStates.put(userId, userState);
+                return true;
             }
 
             return false;
         }
 
-        public boolean addUserId(int userId) {
-            if (hasUserId(userId)) {
-                return false;
-            }
-
-            if (userId == UserHandle.USER_ALL) {
-                mUserIds = USERS_ALL;
-                return true;
-            }
-
-            mUserIds = ArrayUtils.appendInt(mUserIds, userId);
-
-            return true;
+        private boolean isCompatibleUserId(int userId) {
+            return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId));
         }
 
-        public boolean removeUserId(int userId) {
-            if (!hasUserId(userId)) {
-                return false;
-            }
+        private boolean isInstallPermission() {
+            return mUserStates.size() == 1
+                    && mUserStates.get(UserHandle.USER_ALL) != null;
+        }
+    }
 
-            if (mUserIds == USERS_ALL) {
-                mUserIds = UserManagerService.getInstance().getUserIds();
-            }
+    public static final class PermissionState {
+        private final String mName;
+        private boolean mGranted;
+        private int mFlags;
 
-            mUserIds = ArrayUtils.removeInt(mUserIds, userId);
+        public PermissionState(String name) {
+            mName = name;
+        }
 
-            if (mUserIds.length == 0) {
-                mUserIds = USERS_NONE;
-            }
+        public PermissionState(PermissionState other) {
+            mName = other.mName;
+            mGranted = other.mGranted;
+            mFlags = other.mFlags;
+        }
 
-            return true;
+        public boolean isDefault() {
+            return !mGranted && mFlags == 0;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public boolean isGranted() {
+            return mGranted;
+        }
+
+        public int getFlags() {
+            return mFlags;
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 0c7f79d..c35258a 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -21,11 +21,13 @@
 import java.util.Arrays;
 
 abstract class SettingBase {
+    private static final int[] USERS_NONE = new int[0];
+
     int pkgFlags;
     int pkgPrivateFlags;
 
     protected final PermissionsState mPermissionsState;
-    private int[] mPermissionsUpdatedForUserIds = PermissionsState.USERS_NONE;
+    private int[] mPermissionsUpdatedForUserIds = USERS_NONE;
 
     SettingBase(int pkgFlags, int pkgPrivateFlags) {
         setFlags(pkgFlags);
@@ -53,7 +55,7 @@
             return;
         }
 
-        if (userIds == PermissionsState.USERS_NONE || userIds == PermissionsState.USERS_ALL) {
+        if (userIds == USERS_NONE) {
             mPermissionsUpdatedForUserIds = userIds;
         } else {
             mPermissionsUpdatedForUserIds = Arrays.copyOf(userIds, userIds.length);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fd70ce1..2e9656a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -60,6 +60,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.backup.PreferredActivityBackupHelper;
 import com.android.server.pm.PackageManagerService.DumpState;
+import com.android.server.pm.PermissionsState.PermissionState;
 
 import java.io.FileNotFoundException;
 import java.util.Collection;
@@ -178,6 +179,8 @@
     private static final String ATTR_CODE = "code";
     private static final String ATTR_NOT_LAUNCHED = "nl";
     private static final String ATTR_ENABLED = "enabled";
+    private static final String ATTR_GRANTED = "granted";
+    private static final String ATTR_FLAGS = "flags";
     private static final String ATTR_ENABLED_CALLER = "enabledCaller";
     private static final String ATTR_STOPPED = "stopped";
     // Legacy, here for reading older versions of the package-restrictions.
@@ -820,14 +823,20 @@
             }
 
             if (!used) {
+                PermissionsState permissionsState = sus.getPermissionsState();
+
                 // Try to revoke as an install permission which is for all users.
-                if (sus.getPermissionsState().revokeInstallPermission(bp) ==
+                // The package is gone - no need to keep flags for applying policy.
+                permissionsState.updatePermissionFlags(bp, userId,
+                        PackageManager.MASK_PERMISSION_FLAGS, 0);
+
+                if (permissionsState.revokeInstallPermission(bp) ==
                         PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
                     return UserHandle.USER_ALL;
                 }
 
                 // Try to revoke as an install permission which is per user.
-                if (sus.getPermissionsState().revokeRuntimePermission(bp, userId) ==
+                if (permissionsState.revokeRuntimePermission(bp, userId) ==
                         PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
                     return userId;
                 }
@@ -1724,10 +1733,32 @@
                     continue;
                 }
 
-                if (permissionsState.grantInstallPermission(bp) ==
-                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                    Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
-                    XmlUtils.skipCurrentTag(parser);
+                String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED);
+                final boolean granted = grantedStr == null
+                        || Boolean.parseBoolean(grantedStr);
+
+                String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS);
+                final int flags = (flagsStr != null)
+                        ? Integer.parseInt(flagsStr, 16) : 0;
+
+                if (granted) {
+                    if (permissionsState.grantInstallPermission(bp) ==
+                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                        Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
+                        XmlUtils.skipCurrentTag(parser);
+                    } else {
+                        permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+                                PackageManager.MASK_PERMISSION_FLAGS, flags);
+                    }
+                } else {
+                    if (permissionsState.revokeInstallPermission(bp) ==
+                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                        Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
+                        XmlUtils.skipCurrentTag(parser);
+                    } else {
+                        permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+                                PackageManager.MASK_PERMISSION_FLAGS, flags);
+                    }
                 }
             } else {
                 Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
@@ -1737,17 +1768,19 @@
         }
     }
 
-    void writePermissionsLPr(XmlSerializer serializer, Set<String> permissions)
+    void writePermissionsLPr(XmlSerializer serializer, List<PermissionState> permissionStates)
             throws IOException {
-        if (permissions.isEmpty()) {
+        if (permissionStates.isEmpty()) {
             return;
         }
 
         serializer.startTag(null, TAG_PERMISSIONS);
 
-        for (String permission : permissions) {
+        for (PermissionState permissionState : permissionStates) {
             serializer.startTag(null, TAG_ITEM);
-            serializer.attribute(null, ATTR_NAME, permission);
+            serializer.attribute(null, ATTR_NAME, permissionState.getName());
+            serializer.attribute(null, ATTR_GRANTED, String.valueOf(permissionState.isGranted()));
+            serializer.attribute(null, ATTR_FLAGS, Integer.toHexString(permissionState.getFlags()));
             serializer.endTag(null, TAG_ITEM);
         }
 
@@ -1945,7 +1978,8 @@
                 serializer.attribute(null, "userId",
                         Integer.toString(usr.userId));
                 usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
-                writePermissionsLPr(serializer, usr.getPermissionsState().getInstallPermissions());
+                writePermissionsLPr(serializer, usr.getPermissionsState()
+                        .getInstallPermissionStates());
                 serializer.endTag(null, "shared-user");
             }
 
@@ -2120,7 +2154,8 @@
 
         // If this is a shared user, the permissions will be written there.
         if (pkg.sharedUser == null) {
-            writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
+            writePermissionsLPr(serializer, pkg.getPermissionsState()
+                    .getInstallPermissionStates());
         }
 
         serializer.endTag(null, "updated-package");
@@ -2175,9 +2210,9 @@
             serializer.attribute(null, "volumeUuid", pkg.volumeUuid);
         }
         pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
-        if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-            writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
-        }
+
+        writePermissionsLPr(serializer, pkg.getPermissionsState()
+                    .getInstallPermissionStates());
 
         writeSigningKeySetLPr(serializer, pkg.keySetData);
         writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
@@ -3922,7 +3957,7 @@
                 PermissionsState permissionsState = ps.getPermissionsState();
                 dumpGidsLPr(pw, prefix + "    ", permissionsState.computeGids(user.id));
                 dumpRuntimePermissionsLPr(pw, prefix + "    ", permissionsState
-                        .getRuntimePermissions(user.id));
+                        .getRuntimePermissionStates(user.id));
             }
 
             ArraySet<String> cmp = ps.getDisabledComponents(user.id);
@@ -4071,7 +4106,8 @@
 
                 for (int userId : UserManagerService.getInstance().getUserIds()) {
                     final int[] gids = permissionsState.computeGids(userId);
-                    Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+                    List<PermissionState> permissions = permissionsState
+                            .getRuntimePermissionStates(userId);
                     if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
                         pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": ");
                         dumpGidsLPr(pw, prefix + "  ", gids);
@@ -4120,22 +4156,29 @@
         }
     }
 
-    void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, Set<String> permissions) {
-        if (!permissions.isEmpty()) {
+    void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix,
+            List<PermissionState> permissionStates) {
+        if (!permissionStates.isEmpty()) {
             pw.print(prefix); pw.println("runtime permissions:");
-            for (String permission : permissions) {
-                pw.print(prefix); pw.print("  "); pw.println(permission);
+            for (PermissionState permissionState : permissionStates) {
+                pw.print(prefix); pw.print("  "); pw.print(permissionState.getName());
+                pw.print(", granted="); pw.print(permissionState.isGranted());
+                    pw.print(", flags=0x"); pw.println(Integer.toHexString(
+                        permissionState.getFlags()));
             }
         }
     }
 
     void dumpInstallPermissionsLPr(PrintWriter pw, String prefix,
             PermissionsState permissionsState) {
-        Set<String> permissions = permissionsState.getInstallPermissions();
-        if (!permissions.isEmpty()) {
+        List<PermissionState> permissionStates = permissionsState.getInstallPermissionStates();
+        if (!permissionStates.isEmpty()) {
             pw.print(prefix); pw.println("install permissions:");
-            for (String permission : permissions) {
-                pw.print(prefix); pw.print("  "); pw.println(permission);
+            for (PermissionState permissionState : permissionStates) {
+                pw.print(prefix); pw.print("  "); pw.print(permissionState.getName());
+                    pw.print(", granted="); pw.print(permissionState.isGranted());
+                    pw.print(", flags=0x"); pw.println(Integer.toHexString(
+                        permissionState.getFlags()));
             }
         }
     }
@@ -4207,8 +4250,8 @@
         private void writePermissionsSync(int userId) {
             AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId));
 
-            ArrayMap<String, Set<String>> permissionsForPackage = new ArrayMap<>();
-            ArrayMap<String, Set<String>> permissionsForSharedUser = new ArrayMap<>();
+            ArrayMap<String, List<PermissionState>> permissionsForPackage = new ArrayMap<>();
+            ArrayMap<String, List<PermissionState>> permissionsForSharedUser = new ArrayMap<>();
 
             synchronized (mLock) {
                 mWriteScheduled.delete(userId);
@@ -4219,9 +4262,10 @@
                     PackageSetting packageSetting = mPackages.valueAt(i);
                     if (packageSetting.sharedUser == null) {
                         PermissionsState permissionsState = packageSetting.getPermissionsState();
-                        Set<String> permissions = permissionsState.getRuntimePermissions(userId);
-                        if (!permissions.isEmpty()) {
-                            permissionsForPackage.put(packageName, permissions);
+                        List<PermissionState> permissionsStates = permissionsState
+                                .getRuntimePermissionStates(userId);
+                        if (!permissionsStates.isEmpty()) {
+                            permissionsForPackage.put(packageName, permissionsStates);
                         }
                     }
                 }
@@ -4231,9 +4275,10 @@
                     String sharedUserName = mSharedUsers.keyAt(i);
                     SharedUserSetting sharedUser = mSharedUsers.valueAt(i);
                     PermissionsState permissionsState = sharedUser.getPermissionsState();
-                    Set<String> permissions = permissionsState.getRuntimePermissions(userId);
-                    if (!permissions.isEmpty()) {
-                        permissionsForSharedUser.put(sharedUserName, permissions);
+                    List<PermissionState> permissionsStates = permissionsState
+                            .getRuntimePermissionStates(userId);
+                    if (!permissionsStates.isEmpty()) {
+                        permissionsForSharedUser.put(sharedUserName, permissionsStates);
                     }
                 }
             }
@@ -4252,20 +4297,20 @@
                 final int packageCount = permissionsForPackage.size();
                 for (int i = 0; i < packageCount; i++) {
                     String packageName = permissionsForPackage.keyAt(i);
-                    Set<String> permissions = permissionsForPackage.valueAt(i);
+                    List<PermissionState> permissionStates = permissionsForPackage.valueAt(i);
                     serializer.startTag(null, TAG_PACKAGE);
                     serializer.attribute(null, ATTR_NAME, packageName);
-                    writePermissions(serializer, permissions);
+                    writePermissions(serializer, permissionStates);
                     serializer.endTag(null, TAG_PACKAGE);
                 }
 
                 final int sharedUserCount = permissionsForSharedUser.size();
                 for (int i = 0; i < sharedUserCount; i++) {
                     String packageName = permissionsForSharedUser.keyAt(i);
-                    Set<String> permissions = permissionsForSharedUser.valueAt(i);
+                    List<PermissionState> permissionStates = permissionsForSharedUser.valueAt(i);
                     serializer.startTag(null, TAG_SHARED_USER);
                     serializer.attribute(null, ATTR_NAME, packageName);
-                    writePermissions(serializer, permissions);
+                    writePermissions(serializer, permissionStates);
                     serializer.endTag(null, TAG_SHARED_USER);
                 }
 
@@ -4290,20 +4335,23 @@
             mHandler.removeMessages(userId);
 
             for (SettingBase sb : mPackages.values()) {
-                revokeRuntimePermissions(sb, userId);
+                revokeRuntimePermissionsAndClearFlags(sb, userId);
             }
 
             for (SettingBase sb : mSharedUsers.values()) {
-                revokeRuntimePermissions(sb, userId);
+                revokeRuntimePermissionsAndClearFlags(sb, userId);
             }
         }
 
-        private void revokeRuntimePermissions(SettingBase sb, int userId) {
+        private void revokeRuntimePermissionsAndClearFlags(SettingBase sb, int userId) {
             PermissionsState permissionsState = sb.getPermissionsState();
-            for (String permission : permissionsState.getRuntimePermissions(userId)) {
-                BasePermission bp = mPermissions.get(permission);
+            for (PermissionState permissionState
+                    : permissionsState.getRuntimePermissionStates(userId)) {
+                BasePermission bp = mPermissions.get(permissionState.getName());
                 if (bp != null) {
                     permissionsState.revokeRuntimePermission(bp, userId);
+                    permissionsState.updatePermissionFlags(bp, userId,
+                            PackageManager.MASK_PERMISSION_FLAGS, 0);
                 }
             }
         }
@@ -4391,20 +4439,47 @@
                             continue;
                         }
 
-                        if (permissionsState.grantRuntimePermission(bp, userId) ==
-                                PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                            Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+                        String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED);
+                        final boolean granted = grantedStr == null
+                                || Boolean.parseBoolean(grantedStr);
+
+                        String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS);
+                        final int flags = (flagsStr != null)
+                                ? Integer.parseInt(flagsStr, 16) : 0;
+
+                        if (granted) {
+                            if (permissionsState.grantRuntimePermission(bp, userId) ==
+                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+                            } else {
+                                permissionsState.updatePermissionFlags(bp, userId,
+                                        PackageManager.MASK_PERMISSION_FLAGS, flags);
+
+                            }
+                        } else {
+                            if (permissionsState.revokeRuntimePermission(bp, userId) ==
+                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+                            } else {
+                                permissionsState.updatePermissionFlags(bp, userId,
+                                        PackageManager.MASK_PERMISSION_FLAGS, flags);
+                            }
                         }
+
                     } break;
                 }
             }
         }
 
-        private void writePermissions(XmlSerializer serializer, Set<String> permissions)
-                throws IOException {
-            for (String permission : permissions) {
+        private void writePermissions(XmlSerializer serializer,
+                List<PermissionState> permissionStates) throws IOException {
+            for (PermissionState permissionState : permissionStates) {
                 serializer.startTag(null, TAG_ITEM);
-                serializer.attribute(null, ATTR_NAME, permission);
+                serializer.attribute(null, ATTR_NAME,permissionState.getName());
+                serializer.attribute(null, ATTR_GRANTED,
+                        String.valueOf(permissionState.isGranted()));
+                serializer.attribute(null, ATTR_FLAGS,
+                        Integer.toHexString(permissionState.getFlags()));
                 serializer.endTag(null, TAG_ITEM);
             }
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 67c198f..c6816b0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6344,10 +6344,18 @@
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             long ident = Binder.clearCallingIdentity();
             try {
+                PackageManager packageManager = mContext.getPackageManager();
                 if (granted) {
-                    mContext.getPackageManager().grantPermission(packageName, permission, user);
+                    packageManager.grantRuntimePermission(packageName, permission, user);
+                    packageManager.updatePermissionFlags(permission, packageName,
+                            PackageManager.FLAG_PERMISSION_POLICY_FIXED,
+                            PackageManager.FLAG_PERMISSION_POLICY_FIXED, user);
                 } else {
-                    mContext.getPackageManager().revokePermission(packageName, permission, user);
+                    packageManager.revokeRuntimePermission(packageName,
+                            permission, user);
+                    packageManager.updatePermissionFlags(permission, packageName,
+                            PackageManager.FLAG_PERMISSION_POLICY_FIXED,
+                            PackageManager.FLAG_PERMISSION_POLICY_FIXED, user);
                 }
                 return true;
             } catch (SecurityException se) {
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 9efea0d..bf1ea4d 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -44,14 +44,12 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.VerificationParams;
 import android.content.pm.VerifierDeviceIdentity;
-import android.content.pm.PackageManager.MoveCallback;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Handler;
-import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
 
@@ -195,13 +193,28 @@
 
     /** @hide */
     @Override
-    public void grantPermission(String packageName, String permissionName, UserHandle user) {
+    public void grantRuntimePermission(String packageName, String permissionName,
+            UserHandle user) {
         throw new UnsupportedOperationException();
     }
 
     /** @hide */
     @Override
-    public void revokePermission(String packageName, String permissionName, UserHandle user) {
+    public void revokeRuntimePermission(String packageName, String permissionName,
+            UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public int getPermissionFlags(String permissionName, String packageName, UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public void updatePermissionFlags(String permissionName, String packageName,
+            int flagMask, int flagValues, UserHandle user) {
         throw new UnsupportedOperationException();
     }