2 * Copyright (C) 2015 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.server.pm;
19 import android.content.pm.PackageManager;
20 import android.os.UserHandle;
21 import android.util.ArrayMap;
22 import android.util.ArraySet;
24 import android.util.SparseArray;
25 import android.util.SparseBooleanArray;
26 import com.android.internal.util.ArrayUtils;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.List;
35 * This class encapsulates the permissions for a package or a shared user.
37 * There are two types of permissions: install (granted at installation)
38 * and runtime (granted at runtime). Install permissions are granted to
39 * all device users while runtime permissions are granted explicitly to
43 * The permissions are kept on a per device user basis. For example, an
44 * application may have some runtime permissions granted under the device
45 * owner but not granted under the secondary user.
47 * This class is also responsible for keeping track of the Linux gids per
48 * user for a package or a shared user. The gids are computed as a set of
49 * the gids for all granted permissions' gids on a per user basis.
52 public final class PermissionsState {
54 /** The permission operation failed. */
55 public static final int PERMISSION_OPERATION_FAILURE = -1;
57 /** The permission operation succeeded and no gids changed. */
58 public static final int PERMISSION_OPERATION_SUCCESS = 0;
60 /** The permission operation succeeded and gids changed. */
61 public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1;
63 private static final int[] NO_GIDS = {};
65 private ArrayMap<String, PermissionData> mPermissions;
67 private int[] mGlobalGids = NO_GIDS;
69 private SparseBooleanArray mPermissionReviewRequired;
71 public PermissionsState() {
75 public PermissionsState(PermissionsState prototype) {
80 * Sets the global gids, applicable to all users.
82 * @param globalGids The global gids.
84 public void setGlobalGids(int[] globalGids) {
85 if (!ArrayUtils.isEmpty(globalGids)) {
86 mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
91 * Initialized this instance from another one.
93 * @param other The other instance.
95 public void copyFrom(PermissionsState other) {
99 if (mPermissions != null) {
100 if (other.mPermissions == null) {
103 mPermissions.clear();
106 if (other.mPermissions != null) {
107 if (mPermissions == null) {
108 mPermissions = new ArrayMap<>();
110 final int permissionCount = other.mPermissions.size();
111 for (int i = 0; i < permissionCount; i++) {
112 String name = other.mPermissions.keyAt(i);
113 PermissionData permissionData = other.mPermissions.valueAt(i);
114 mPermissions.put(name, new PermissionData(permissionData));
118 mGlobalGids = NO_GIDS;
119 if (other.mGlobalGids != NO_GIDS) {
120 mGlobalGids = Arrays.copyOf(other.mGlobalGids,
121 other.mGlobalGids.length);
124 if (mPermissionReviewRequired != null) {
125 if (other.mPermissionReviewRequired == null) {
126 mPermissionReviewRequired = null;
128 mPermissionReviewRequired.clear();
131 if (other.mPermissionReviewRequired != null) {
132 if (mPermissionReviewRequired == null) {
133 mPermissionReviewRequired = new SparseBooleanArray();
135 final int userCount = other.mPermissionReviewRequired.size();
136 for (int i = 0; i < userCount; i++) {
137 final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i);
138 mPermissionReviewRequired.put(i, reviewRequired);
143 public boolean isPermissionReviewRequired(int userId) {
144 return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId);
148 * Grant an install permission.
150 * @param permission The permission to grant.
151 * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
152 * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
153 * #PERMISSION_OPERATION_FAILURE}.
155 public int grantInstallPermission(BasePermission permission) {
156 return grantPermission(permission, UserHandle.USER_ALL);
160 * Revoke an install permission.
162 * @param permission The permission to revoke.
163 * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
164 * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
165 * #PERMISSION_OPERATION_FAILURE}.
167 public int revokeInstallPermission(BasePermission permission) {
168 return revokePermission(permission, UserHandle.USER_ALL);
172 * Grant a runtime permission for a given device user.
174 * @param permission The permission to grant.
175 * @param userId The device user id.
176 * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
177 * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
178 * #PERMISSION_OPERATION_FAILURE}.
180 public int grantRuntimePermission(BasePermission permission, int userId) {
181 enforceValidUserId(userId);
182 if (userId == UserHandle.USER_ALL) {
183 return PERMISSION_OPERATION_FAILURE;
185 return grantPermission(permission, userId);
189 * Revoke a runtime permission for a given device user.
191 * @param permission The permission to revoke.
192 * @param userId The device user id.
193 * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
194 * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
195 * #PERMISSION_OPERATION_FAILURE}.
197 public int revokeRuntimePermission(BasePermission permission, int userId) {
198 enforceValidUserId(userId);
199 if (userId == UserHandle.USER_ALL) {
200 return PERMISSION_OPERATION_FAILURE;
202 return revokePermission(permission, userId);
206 * Gets whether this state has a given runtime permission for a
207 * given device user id.
209 * @param name The permission name.
210 * @param userId The device user id.
211 * @return Whether this state has the permission.
213 public boolean hasRuntimePermission(String name, int userId) {
214 enforceValidUserId(userId);
215 return !hasInstallPermission(name) && hasPermission(name, userId);
219 * Gets whether this state has a given install permission.
221 * @param name The permission name.
222 * @return Whether this state has the permission.
224 public boolean hasInstallPermission(String name) {
225 return hasPermission(name, UserHandle.USER_ALL);
229 * Gets whether the state has a given permission for the specified
230 * user, regardless if this is an install or a runtime permission.
232 * @param name The permission name.
233 * @param userId The device user id.
234 * @return Whether the user has the permission.
236 public boolean hasPermission(String name, int userId) {
237 enforceValidUserId(userId);
239 if (mPermissions == null) {
243 PermissionData permissionData = mPermissions.get(name);
244 return permissionData != null && permissionData.isGranted(userId);
248 * Returns whether the state has any known request for the given permission name,
249 * whether or not it has been granted.
251 public boolean hasRequestedPermission(ArraySet<String> names) {
252 if (mPermissions == null) {
255 for (int i=names.size()-1; i>=0; i--) {
256 if (mPermissions.get(names.valueAt(i)) != null) {
264 * Gets all permissions for a given device user id regardless if they
265 * are install time or runtime permissions.
267 * @param userId The device user id.
268 * @return The permissions or an empty set.
270 public Set<String> getPermissions(int userId) {
271 enforceValidUserId(userId);
273 if (mPermissions == null) {
274 return Collections.emptySet();
277 Set<String> permissions = new ArraySet<>(mPermissions.size());
279 final int permissionCount = mPermissions.size();
280 for (int i = 0; i < permissionCount; i++) {
281 String permission = mPermissions.keyAt(i);
283 if (hasInstallPermission(permission)) {
284 permissions.add(permission);
288 if (userId != UserHandle.USER_ALL) {
289 if (hasRuntimePermission(permission, userId)) {
290 permissions.add(permission);
299 * Gets the state for an install permission or null if no such.
301 * @param name The permission name.
302 * @return The permission state.
304 public PermissionState getInstallPermissionState(String name) {
305 return getPermissionState(name, UserHandle.USER_ALL);
309 * Gets the state for a runtime permission or null if no such.
311 * @param name The permission name.
312 * @param userId The device user id.
313 * @return The permission state.
315 public PermissionState getRuntimePermissionState(String name, int userId) {
316 enforceValidUserId(userId);
317 return getPermissionState(name, userId);
321 * Gets all install permission states.
323 * @return The permission states or an empty set.
325 public List<PermissionState> getInstallPermissionStates() {
326 return getPermissionStatesInternal(UserHandle.USER_ALL);
330 * Gets all runtime permission states.
332 * @return The permission states or an empty set.
334 public List<PermissionState> getRuntimePermissionStates(int userId) {
335 enforceValidUserId(userId);
336 return getPermissionStatesInternal(userId);
340 * Gets the flags for a permission regardless if it is install or
341 * runtime permission.
343 * @param name The permission name.
344 * @return The permission state or null if no such.
346 public int getPermissionFlags(String name, int userId) {
347 PermissionState installPermState = getInstallPermissionState(name);
348 if (installPermState != null) {
349 return installPermState.getFlags();
351 PermissionState runtimePermState = getRuntimePermissionState(name, userId);
352 if (runtimePermState != null) {
353 return runtimePermState.getFlags();
359 * Update the flags associated with a given permission.
360 * @param permission The permission whose flags to update.
361 * @param userId The user for which to update.
362 * @param flagMask Mask for which flags to change.
363 * @param flagValues New values for the mask flags.
364 * @return Whether the permission flags changed.
366 public boolean updatePermissionFlags(BasePermission permission, int userId,
367 int flagMask, int flagValues) {
368 enforceValidUserId(userId);
370 final boolean mayChangeFlags = flagValues != 0 || flagMask != 0;
372 if (mPermissions == null) {
373 if (!mayChangeFlags) {
376 ensurePermissionData(permission);
379 PermissionData permissionData = mPermissions.get(permission.name);
380 if (permissionData == null) {
381 if (!mayChangeFlags) {
384 permissionData = ensurePermissionData(permission);
387 final int oldFlags = permissionData.getFlags(userId);
389 final boolean updated = permissionData.updateFlags(userId, flagMask, flagValues);
391 final int newFlags = permissionData.getFlags(userId);
392 if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
393 && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
394 if (mPermissionReviewRequired == null) {
395 mPermissionReviewRequired = new SparseBooleanArray();
397 mPermissionReviewRequired.put(userId, true);
398 } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
399 && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
400 if (mPermissionReviewRequired != null) {
401 mPermissionReviewRequired.delete(userId);
402 if (mPermissionReviewRequired.size() <= 0) {
403 mPermissionReviewRequired = null;
411 public boolean updatePermissionFlagsForAllPermissions(
412 int userId, int flagMask, int flagValues) {
413 enforceValidUserId(userId);
415 if (mPermissions == null) {
418 boolean changed = false;
419 final int permissionCount = mPermissions.size();
420 for (int i = 0; i < permissionCount; i++) {
421 PermissionData permissionData = mPermissions.valueAt(i);
422 changed |= permissionData.updateFlags(userId, flagMask, flagValues);
428 * Compute the Linux gids for a given device user from the permissions
429 * granted to this user. Note that these are computed to avoid additional
430 * state as they are rarely accessed.
432 * @param userId The device user id.
433 * @return The gids for the device user.
435 public int[] computeGids(int userId) {
436 enforceValidUserId(userId);
438 int[] gids = mGlobalGids;
440 if (mPermissions != null) {
441 final int permissionCount = mPermissions.size();
442 for (int i = 0; i < permissionCount; i++) {
443 String permission = mPermissions.keyAt(i);
444 if (!hasPermission(permission, userId)) {
447 PermissionData permissionData = mPermissions.valueAt(i);
448 final int[] permGids = permissionData.computeGids(userId);
449 if (permGids != NO_GIDS) {
450 gids = appendInts(gids, permGids);
459 * Compute the Linux gids for all device users from the permissions
460 * granted to these users.
462 * @return The gids for all device users.
464 public int[] computeGids(int[] userIds) {
465 int[] gids = mGlobalGids;
467 for (int userId : userIds) {
468 final int[] userGids = computeGids(userId);
469 gids = appendInts(gids, userGids);
476 * Resets the internal state of this object.
478 public void reset() {
479 mGlobalGids = NO_GIDS;
481 mPermissionReviewRequired = null;
484 private PermissionState getPermissionState(String name, int userId) {
485 if (mPermissions == null) {
488 PermissionData permissionData = mPermissions.get(name);
489 if (permissionData == null) {
492 return permissionData.getPermissionState(userId);
495 private List<PermissionState> getPermissionStatesInternal(int userId) {
496 enforceValidUserId(userId);
498 if (mPermissions == null) {
499 return Collections.emptyList();
502 List<PermissionState> permissionStates = new ArrayList<>();
504 final int permissionCount = mPermissions.size();
505 for (int i = 0; i < permissionCount; i++) {
506 PermissionData permissionData = mPermissions.valueAt(i);
508 PermissionState permissionState = permissionData.getPermissionState(userId);
509 if (permissionState != null) {
510 permissionStates.add(permissionState);
514 return permissionStates;
517 private int grantPermission(BasePermission permission, int userId) {
518 if (hasPermission(permission.name, userId)) {
519 return PERMISSION_OPERATION_FAILURE;
522 final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
523 final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
525 PermissionData permissionData = ensurePermissionData(permission);
527 if (!permissionData.grant(userId)) {
528 return PERMISSION_OPERATION_FAILURE;
532 final int[] newGids = computeGids(userId);
533 if (oldGids.length != newGids.length) {
534 return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
538 return PERMISSION_OPERATION_SUCCESS;
541 private int revokePermission(BasePermission permission, int userId) {
542 if (!hasPermission(permission.name, userId)) {
543 return PERMISSION_OPERATION_FAILURE;
546 final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
547 final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
549 PermissionData permissionData = mPermissions.get(permission.name);
551 if (!permissionData.revoke(userId)) {
552 return PERMISSION_OPERATION_FAILURE;
555 if (permissionData.isDefault()) {
556 ensureNoPermissionData(permission.name);
560 final int[] newGids = computeGids(userId);
561 if (oldGids.length != newGids.length) {
562 return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
566 return PERMISSION_OPERATION_SUCCESS;
569 private static int[] appendInts(int[] current, int[] added) {
570 if (current != null && added != null) {
571 for (int guid : added) {
572 current = ArrayUtils.appendInt(current, guid);
578 private static void enforceValidUserId(int userId) {
579 if (userId != UserHandle.USER_ALL && userId < 0) {
580 throw new IllegalArgumentException("Invalid userId:" + userId);
584 private PermissionData ensurePermissionData(BasePermission permission) {
585 if (mPermissions == null) {
586 mPermissions = new ArrayMap<>();
588 PermissionData permissionData = mPermissions.get(permission.name);
589 if (permissionData == null) {
590 permissionData = new PermissionData(permission);
591 mPermissions.put(permission.name, permissionData);
593 return permissionData;
596 private void ensureNoPermissionData(String name) {
597 if (mPermissions == null) {
600 mPermissions.remove(name);
601 if (mPermissions.isEmpty()) {
606 private static final class PermissionData {
607 private final BasePermission mPerm;
608 private SparseArray<PermissionState> mUserStates = new SparseArray<>();
610 public PermissionData(BasePermission perm) {
614 public PermissionData(PermissionData other) {
616 final int otherStateCount = other.mUserStates.size();
617 for (int i = 0; i < otherStateCount; i++) {
618 final int otherUserId = other.mUserStates.keyAt(i);
619 PermissionState otherState = other.mUserStates.valueAt(i);
620 mUserStates.put(otherUserId, new PermissionState(otherState));
624 public int[] computeGids(int userId) {
625 return mPerm.computeGids(userId);
628 public boolean isGranted(int userId) {
629 if (isInstallPermission()) {
630 userId = UserHandle.USER_ALL;
633 PermissionState userState = mUserStates.get(userId);
634 if (userState == null) {
638 return userState.mGranted;
641 public boolean grant(int userId) {
642 if (!isCompatibleUserId(userId)) {
646 if (isGranted(userId)) {
650 PermissionState userState = mUserStates.get(userId);
651 if (userState == null) {
652 userState = new PermissionState(mPerm.name);
653 mUserStates.put(userId, userState);
656 userState.mGranted = true;
661 public boolean revoke(int userId) {
662 if (!isCompatibleUserId(userId)) {
666 if (!isGranted(userId)) {
670 PermissionState userState = mUserStates.get(userId);
671 userState.mGranted = false;
673 if (userState.isDefault()) {
674 mUserStates.remove(userId);
680 public PermissionState getPermissionState(int userId) {
681 return mUserStates.get(userId);
684 public int getFlags(int userId) {
685 PermissionState userState = mUserStates.get(userId);
686 if (userState != null) {
687 return userState.mFlags;
692 public boolean isDefault() {
693 return mUserStates.size() <= 0;
696 public static boolean isInstallPermissionKey(int userId) {
697 return userId == UserHandle.USER_ALL;
700 public boolean updateFlags(int userId, int flagMask, int flagValues) {
701 if (isInstallPermission()) {
702 userId = UserHandle.USER_ALL;
705 if (!isCompatibleUserId(userId)) {
709 final int newFlags = flagValues & flagMask;
711 PermissionState userState = mUserStates.get(userId);
712 if (userState != null) {
713 final int oldFlags = userState.mFlags;
714 userState.mFlags = (userState.mFlags & ~flagMask) | newFlags;
715 if (userState.isDefault()) {
716 mUserStates.remove(userId);
718 return userState.mFlags != oldFlags;
719 } else if (newFlags != 0) {
720 userState = new PermissionState(mPerm.name);
721 userState.mFlags = newFlags;
722 mUserStates.put(userId, userState);
729 private boolean isCompatibleUserId(int userId) {
730 return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId));
733 private boolean isInstallPermission() {
734 return mUserStates.size() == 1
735 && mUserStates.get(UserHandle.USER_ALL) != null;
739 public static final class PermissionState {
740 private final String mName;
741 private boolean mGranted;
744 public PermissionState(String name) {
748 public PermissionState(PermissionState other) {
750 mGranted = other.mGranted;
751 mFlags = other.mFlags;
754 public boolean isDefault() {
755 return !mGranted && mFlags == 0;
758 public String getName() {
762 public boolean isGranted() {
766 public int getFlags() {