diff options
author | Svet Ganov <svetoslavganov@google.com> | 2015-05-07 10:48:44 -0700 |
---|---|---|
committer | Svetoslav <svetoslavganov@google.com> | 2015-05-11 17:13:57 -0700 |
commit | 8c7f700a59ad26e75c9791335d78f14322cad49a (patch) | |
tree | 151d514ffaf4c6bde3ee650e8f94b7f64eb74fd8 /services | |
parent | 3ce72722ca14ad9e84ca54920bb2829696f68d53 (diff) | |
download | frameworks_base-8c7f700a59ad26e75c9791335d78f14322cad49a.zip frameworks_base-8c7f700a59ad26e75c9791335d78f14322cad49a.tar.gz frameworks_base-8c7f700a59ad26e75c9791335d78f14322cad49a.tar.bz2 |
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
Diffstat (limited to 'services')
6 files changed, 552 insertions, 204 deletions
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.PackageParser; 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.ServiceThread; 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { } } + 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 @@ public class PackageManagerService extends IPackageManager.Stub { } @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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { } @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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { } 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.os.UserHandle; 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 @@ public final class PermissionsState { /** 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 @@ public final class PermissionsState { } /** - * 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 @@ public final class PermissionsState { } /** - * 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 @@ public final class PermissionsState { } /** - * 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 @@ public final class PermissionsState { * @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 @@ public final class PermissionsState { } /** - * 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,49 +217,133 @@ public final class PermissionsState { } PermissionData permissionData = mPermissions.get(name); - return permissionData != null && permissionData.hasUserId(userId); + return permissionData != null && permissionData.isGranted(userId); } /** - * Gets all permissions regardless if they are install or runtime. + * Gets all permissions for a given device user id regardless if they + * are install time or runtime permissions. * + * @param userId The device user id. * @return The permissions or an empty set. */ - public Set<String> getPermissions() { - if (mPermissions != null) { - return mPermissions.keySet(); + public Set<String> getPermissions(int userId) { + enforceValidUserId(userId); + + if (mPermissions == null) { + return Collections.emptySet(); } - 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 permissions for a given device user id regardless if they - * are install time or runtime permissions. + * Gets the state for an install permission or null if no such. * + * @param name The permission name. + * @return The permission state. + */ + public PermissionState getInstallPermissionState(String name) { + return getPermissionState(name, UserHandle.USER_ALL); + } + + /** + * Gets the state for a runtime permission or null if no such. + * + * @param name The permission name. * @param userId The device user id. - * @return The permissions or an empty set. + * @return The permission state. */ - public Set<String> getPermissions(int userId) { - return getPermissionsInternal(FLAG_INSTALL_PERMISSIONS | FLAG_RUNTIME_PERMISSIONS, userId); + public PermissionState getRuntimePermissionState(String name, int userId) { + enforceValidUserId(userId); + return getPermissionState(name, userId); } /** - * Gets all runtime permissions. + * Gets all install permission states. * - * @return The permissions or an empty set. + * @return The permission states or an empty set. */ - public Set<String> getRuntimePermissions(int userId) { - return getPermissionsInternal(FLAG_RUNTIME_PERMISSIONS, userId); + public List<PermissionState> getInstallPermissionStates() { + return getPermissionStatesInternal(UserHandle.USER_ALL); } /** - * Gets all install permissions. + * Gets all runtime permission states. * - * @return The permissions or an empty set. + * @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 Set<String> getInstallPermissions() { - return getPermissionsInternal(FLAG_INSTALL_PERMISSIONS, UserHandle.USER_ALL); + 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 @@ public final class PermissionsState { mPermissions = null; } - private Set<String> getPermissionsInternal(int flags, int userId) { - enforceValidUserId(userId); - + private PermissionState getPermissionState(String name, int userId) { if (mPermissions == null) { - return Collections.emptySet(); + return null; } + PermissionData permissionData = mPermissions.get(name); + if (permissionData == null) { + return null; + } + return permissionData.getPermissionState(userId); + } - if (userId == UserHandle.USER_ALL) { - flags = FLAG_INSTALL_PERMISSIONS; + private List<PermissionState> getPermissionStatesInternal(int userId) { + enforceValidUserId(userId); + + if (mPermissions == null) { + return Collections.emptyList(); } - 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 @@ public final class PermissionsState { final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId)); final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS; - if (mPermissions == null) { - mPermissions = new ArrayMap<>(); - } - - PermissionData permissionData = mPermissions.get(permission.name); - if (permissionData == null) { - permissionData = new PermissionData(permission); - mPermissions.put(permission.name, permissionData); - } + PermissionData permissionData = ensurePermissionData(permission); - if (!permissionData.addUserId(userId)) { + if (!permissionData.grant(userId)) { return PERMISSION_OPERATION_FAILURE; } @@ -431,16 +469,12 @@ public final class PermissionsState { 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 @@ public final class PermissionsState { } } + 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 final class PermissionsState { 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 @@ public final class PermissionsState { 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 false; + return userState.mGranted; } - public boolean addUserId(int userId) { - if (hasUserId(userId)) { + public boolean grant(int userId) { + if (!isCompatibleUserId(userId)) { return false; } - if (userId == UserHandle.USER_ALL) { - mUserIds = USERS_ALL; - return true; + if (isGranted(userId)) { + return false; } - mUserIds = ArrayUtils.appendInt(mUserIds, userId); + PermissionState userState = mUserStates.get(userId); + if (userState == null) { + userState = new PermissionState(mPerm.name); + mUserStates.put(userId, userState); + } + + userState.mGranted = true; return true; } - public boolean removeUserId(int userId) { - if (!hasUserId(userId)) { + public boolean revoke(int userId) { + if (!isCompatibleUserId(userId)) { return false; } - if (mUserIds == USERS_ALL) { - mUserIds = UserManagerService.getInstance().getUserIds(); + if (!isGranted(userId)) { + return false; } - mUserIds = ArrayUtils.removeInt(mUserIds, userId); + PermissionState userState = mUserStates.get(userId); + userState.mGranted = false; - if (mUserIds.length == 0) { - mUserIds = USERS_NONE; + 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; + } + + private boolean isCompatibleUserId(int userId) { + return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId)); + } + + private boolean isInstallPermission() { + return mUserStates.size() == 1 + && mUserStates.get(UserHandle.USER_ALL) != null; + } + } + + public static final class PermissionState { + private final String mName; + private boolean mGranted; + private int mFlags; + + public PermissionState(String name) { + mName = name; + } + + public PermissionState(PermissionState other) { + mName = other.mName; + mGranted = other.mGranted; + mFlags = other.mFlags; + } + + 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 android.content.pm.ApplicationInfo; 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 @@ abstract class SettingBase { 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.Preconditions; 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 @@ final class Settings { 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 @@ final class Settings { } 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 @@ final class Settings { 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 @@ final class Settings { } } - 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 @@ final class Settings { 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 @@ final class Settings { // 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 @@ final class Settings { 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 @@ final class Settings { 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 @@ final class Settings { 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 @@ final class Settings { } } - 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 @@ final class Settings { 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 @@ final class Settings { 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 @@ final class Settings { 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 class Settings { 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 @@ final class Settings { 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 @@ final class Settings { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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) { |