Fix reset permissions on clear data and package uninstall.

If the user clears data for an app we reset the permission but
only the changes made by the user. We do not modify syste or
policy flags and also ensure the permission that were granted
by default are granted after the data wipe. This is the same
as starting with a clean slate.

If the package whose data is cleared is a part of a shared user
we resent to initial state only the permissions that the cleared
package contributed. Hence, if another package also declared the
permission as used we do not clear the permission state as it is
still in use.

When a package is deleted for a user but still present for another
user we reset its permissions to their inital state follwoing
above described strategy.

Lastly when a preinstalled package wtih an upgrade is diabled
(triggers upgrade uninstall) and this package is a part of a
shared user, we do not drop permission state (grants and flags)
for permissions used by the shadowed system package. This ensures
that we do not drop runtime permission state (such state is
default grants and user changes).i

bug:22248525

Change-Id: I3a3007476d2cb9f4ff824e1e137a6e1a4d04408b
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 49386f9..62c2e8c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4627,12 +4627,12 @@
     /** {@hide} */
     public static String permissionFlagToString(int flag) {
         switch (flag) {
-            case FLAG_PERMISSION_GRANTED_BY_DEFAULT: return "FLAG_PERMISSION_GRANTED_BY_DEFAULT";
-            case FLAG_PERMISSION_POLICY_FIXED: return "FLAG_PERMISSION_POLICY_FIXED";
-            case FLAG_PERMISSION_SYSTEM_FIXED: return "FLAG_PERMISSION_SYSTEM_FIXED";
-            case FLAG_PERMISSION_USER_SET: return "FLAG_PERMISSION_USER_SET";
-            case FLAG_PERMISSION_REVOKE_ON_UPGRADE: return "FLAG_PERMISSION_REVOKE_ON_UPGRADE";
-            case FLAG_PERMISSION_USER_FIXED: return "FLAG_PERMISSION_USER_FIXED";
+            case FLAG_PERMISSION_GRANTED_BY_DEFAULT: return "GRANTED_BY_DEFAULT";
+            case FLAG_PERMISSION_POLICY_FIXED: return "POLICY_FIXED";
+            case FLAG_PERMISSION_SYSTEM_FIXED: return "SYSTEM_FIXED";
+            case FLAG_PERMISSION_USER_SET: return "USER_SET";
+            case FLAG_PERMISSION_REVOKE_ON_UPGRADE: return "REVOKE_ON_UPGRADE";
+            case FLAG_PERMISSION_USER_FIXED: return "USER_FIXED";
             default: return Integer.toString(flag);
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 124214c..ed20345 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -25,6 +25,12 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
 import static android.content.pm.PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
@@ -72,6 +78,9 @@
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
 import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
+import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
+import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
+import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
 
 import android.Manifest;
 import android.app.ActivityManager;
@@ -12665,7 +12674,7 @@
                                                 KILL_APP_REASON_GIDS_CHANGED);
                                     }
                                 });
-                            break;
+                                break;
                             }
                         }
                     }
@@ -12781,8 +12790,14 @@
         // writer
         synchronized (mPackages) {
             PackageSetting ps = mSettings.mPackages.get(newPkg.packageName);
+
+            // Propagate the permissions state as we do want to drop on the floor
+            // runtime permissions. The update permissions method below will take
+            // care of removing obsolete permissions and grant install permissions.
+            ps.getPermissionsState().copyFrom(disabledPs.getPermissionsState());
             updatePermissionsLPw(newPkg.packageName, newPkg,
                     UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
+
             if (applyUserRestrictions) {
                 if (DEBUG_REMOVE) {
                     Slog.d(TAG, "Propagating install state across reinstall");
@@ -12943,8 +12958,7 @@
                 if (clearPackagePreferredActivitiesLPw(packageName, removeUser)) {
                     scheduleWritePackageRestrictionsLocked(removeUser);
                 }
-                revokeRuntimePermissionsAndClearAllFlagsLocked(ps.getPermissionsState(),
-                        removeUser);
+                resetUserChangesToRuntimePermissionsAndFlagsLocked(ps, removeUser);
             }
             return true;
         }
@@ -13105,8 +13119,7 @@
             }
 
             PackageSetting ps = (PackageSetting) pkg.mExtras;
-            PermissionsState permissionsState = ps.getPermissionsState();
-            revokeRuntimePermissionsAndClearUserSetFlagsLocked(permissionsState, userId);
+            resetUserChangesToRuntimePermissionsAndFlagsLocked(ps, userId);
         }
 
         // Always delete data directories for package, even if we found no other
@@ -13137,66 +13150,118 @@
         return true;
     }
 
-
     /**
-     * Revokes granted runtime permissions and clears resettable flags
-     * which are flags that can be set by a user interaction.
+     * Reverts user permission state changes (permissions and flags).
      *
-     * @param permissionsState The permission state to reset.
+     * @param ps The package for which to reset.
      * @param userId The device user for which to do a reset.
      */
-    private void revokeRuntimePermissionsAndClearUserSetFlagsLocked(
-            PermissionsState permissionsState, int userId) {
-        final int userSetFlags = PackageManager.FLAG_PERMISSION_USER_SET
-                | PackageManager.FLAG_PERMISSION_USER_FIXED
-                | PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+    private void resetUserChangesToRuntimePermissionsAndFlagsLocked(
+            final PackageSetting ps, final int userId) {
+        if (ps.pkg == null) {
+            return;
+        }
 
-        revokeRuntimePermissionsAndClearFlagsLocked(permissionsState, userId, userSetFlags);
-    }
+        final int userSettableFlags = FLAG_PERMISSION_USER_SET
+                | FLAG_PERMISSION_USER_FIXED
+                | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
 
-    /**
-     * Revokes granted runtime permissions and clears all flags.
-     *
-     * @param permissionsState The permission state to reset.
-     * @param userId The device user for which to do a reset.
-     */
-    private void revokeRuntimePermissionsAndClearAllFlagsLocked(
-            PermissionsState permissionsState, int userId) {
-        revokeRuntimePermissionsAndClearFlagsLocked(permissionsState, userId,
-                PackageManager.MASK_PERMISSION_FLAGS);
-    }
+        final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
+                | FLAG_PERMISSION_POLICY_FIXED;
 
-    /**
-     * Revokes granted runtime permissions and clears certain flags.
-     *
-     * @param permissionsState The permission state to reset.
-     * @param userId The device user for which to do a reset.
-     * @param flags The flags that is going to be reset.
-     */
-    private void revokeRuntimePermissionsAndClearFlagsLocked(
-            PermissionsState permissionsState, final int userId, int flags) {
-        boolean needsWrite = false;
+        boolean writeInstallPermissions = false;
+        boolean writeRuntimePermissions = false;
 
-        for (PermissionState state : permissionsState.getRuntimePermissionStates(userId)) {
-            BasePermission bp = mSettings.mPermissions.get(state.getName());
-            if (bp != null) {
-                permissionsState.revokeRuntimePermission(bp, userId);
-                permissionsState.updatePermissionFlags(bp, userId, flags, 0);
-                needsWrite = true;
+        final int permissionCount = ps.pkg.requestedPermissions.size();
+        for (int i = 0; i < permissionCount; i++) {
+            String permission = ps.pkg.requestedPermissions.get(i);
+
+            BasePermission bp = mSettings.mPermissions.get(permission);
+            if (bp == null) {
+                continue;
+            }
+
+            // If shared user we just reset the state to which only this app contributed.
+            if (ps.sharedUser != null) {
+                boolean used = false;
+                final int packageCount = ps.sharedUser.packages.size();
+                for (int j = 0; j < packageCount; j++) {
+                    PackageSetting pkg = ps.sharedUser.packages.valueAt(j);
+                    if (pkg.pkg != null && !pkg.pkg.packageName.equals(ps.pkg.packageName)
+                            && pkg.pkg.requestedPermissions.contains(permission)) {
+                        used = true;
+                        break;
+                    }
+                }
+                if (used) {
+                    continue;
+                }
+            }
+
+            PermissionsState permissionsState = ps.getPermissionsState();
+
+            final int oldFlags = permissionsState.getPermissionFlags(bp.name, userId);
+
+            // Always clear the user settable flags.
+            final boolean hasInstallState = permissionsState.getInstallPermissionState(
+                    bp.name) != null;
+            if (permissionsState.updatePermissionFlags(bp, userId, userSettableFlags, 0)) {
+                if (hasInstallState) {
+                    writeInstallPermissions = true;
+                } else {
+                    writeRuntimePermissions = true;
+                }
+            }
+
+            // Below is only runtime permission handling.
+            if (!bp.isRuntime()) {
+                continue;
+            }
+
+            // Never clobber system or policy.
+            if ((oldFlags & policyOrSystemFlags) != 0) {
+                continue;
+            }
+
+            // If this permission was granted by default, make sure it is.
+            if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0) {
+                if (permissionsState.grantRuntimePermission(bp, userId)
+                        != PERMISSION_OPERATION_FAILURE) {
+                    writeRuntimePermissions = true;
+                }
+            } else {
+                // Otherwise, reset the permission.
+                final int revokeResult = permissionsState.revokeRuntimePermission(bp, userId);
+                switch (revokeResult) {
+                    case PERMISSION_OPERATION_SUCCESS: {
+                        writeRuntimePermissions = true;
+                    } break;
+
+                    case PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
+                        writeRuntimePermissions = true;
+                        // If gids changed for this user, kill all affected packages.
+                        mHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                // This has to happen with no lock held.
+                                killSettingPackagesForUser(ps, userId,
+                                        KILL_APP_REASON_GIDS_CHANGED);
+                            }
+                        });
+                    } break;
+                }
             }
         }
 
-        // Ensure default permissions are never cleared.
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mDefaultPermissionPolicy.grantDefaultPermissions(userId);
-            }
-        });
-
-        if (needsWrite) {
+        // Synchronously write as we are taking permissions away.
+        if (writeRuntimePermissions) {
             mSettings.writeRuntimePermissionsForUserLPr(userId, true);
         }
+
+        // Synchronously write as we are taking permissions away.
+        if (writeInstallPermissions) {
+            mSettings.writeLPr();
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4e85e39..bdcd714 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -824,14 +824,8 @@
                 continue;
             }
 
-            // If no user has the permission, nothing to remove.
-            if (!sus.getPermissionsState().hasPermission(bp.name, userId)) {
-                 continue;
-            }
-
-            boolean used = false;
-
             // Check if another package in the shared user needs the permission.
+            boolean used = false;
             for (PackageSetting pkg : sus.packages) {
                 if (pkg.pkg != null
                         && !pkg.pkg.packageName.equals(deletedPs.pkg.packageName)
@@ -840,26 +834,43 @@
                     break;
                 }
             }
+            if (used) {
+                continue;
+            }
 
-            if (!used) {
-                PermissionsState permissionsState = sus.getPermissionsState();
+            PermissionsState permissionsState = sus.getPermissionsState();
+            PackageSetting disabledPs = getDisabledSystemPkgLPr(deletedPs.pkg.packageName);
 
-                // Try to revoke as an install permission which is for all users.
-                // 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;
+            // If the package is shadowing is a disabled system package,
+            // do not drop permissions that the shadowed package requests.
+            if (disabledPs != null) {
+                boolean reqByDisabledSysPkg = false;
+                for (String permission : disabledPs.pkg.requestedPermissions) {
+                    if (permission.equals(eachPerm)) {
+                        reqByDisabledSysPkg = true;
+                        break;
+                    }
                 }
-
-                // Try to revoke as an install permission which is per user.
-                if (permissionsState.revokeRuntimePermission(bp, userId) ==
-                        PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
-                    return userId;
+                if (reqByDisabledSysPkg) {
+                    continue;
                 }
             }
+
+            // Try to revoke as an install permission which is for all users.
+            // 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 (permissionsState.revokeRuntimePermission(bp, userId) ==
+                    PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+                return userId;
+            }
         }
 
         return UserHandle.USER_NULL;