blob: fbb50902ef73d3deda5ee9a48549245d7552fd54 [file] [log] [blame]
Svetoslavc6d1c342015-02-26 14:44:43 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17package com.android.server.pm;
18
19import android.os.UserHandle;
20import android.util.ArrayMap;
21import android.util.ArraySet;
Jeff Sharkey00f39042015-03-23 16:51:22 -070022
Svetoslavc6d1c342015-02-26 14:44:43 -080023import com.android.internal.util.ArrayUtils;
24
25import java.util.Arrays;
26import java.util.Collections;
27import java.util.Set;
28
29/**
30 * This class encapsulates the permissions for a package or a shared user.
31 * <p>
32 * There are two types of permissions: install (granted at installation)
33 * and runtime (granted at runtime). Install permissions are granted to
34 * all device users while runtime permissions are granted explicitly to
35 * specific users.
36 * </p>
37 * <p>
38 * The permissions are kept on a per device user basis. For example, an
39 * application may have some runtime permissions granted under the device
40 * owner but not granted under the secondary user.
41 * <p>
42 * This class is also responsible for keeping track of the Linux gids per
43 * user for a package or a shared user. The gids are computed as a set of
44 * the gids for all granted permissions' gids on a per user basis.
45 * </p>
46 */
47public final class PermissionsState {
48
49 /** The permission operation succeeded and no gids changed. */
50 public static final int PERMISSION_OPERATION_SUCCESS = 1;
51
52 /** The permission operation succeeded and gids changed. */
53 public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 2;
54
55 /** The permission operation failed. */
56 public static final int PERMISSION_OPERATION_FAILURE = 3;
57
Svet Ganovd5752bd2015-03-25 22:57:39 -070058 public static final int[] USERS_ALL = {UserHandle.USER_ALL};
Svetoslavc6d1c342015-02-26 14:44:43 -080059
Svet Ganovd5752bd2015-03-25 22:57:39 -070060 public static final int[] USERS_NONE = {};
Svetoslavc6d1c342015-02-26 14:44:43 -080061
62 private static final int[] NO_GIDS = {};
63
64 private ArrayMap<String, PermissionData> mPermissions;
65
66 private int[] mGlobalGids = NO_GIDS;
67
68 public PermissionsState() {
69 /* do nothing */
70 }
71
72 public PermissionsState(PermissionsState prototype) {
73 copyFrom(prototype);
74 }
75
76 /**
77 * Sets the global gids, applicable to all users.
78 *
79 * @param globalGids The global gids.
80 */
81 public void setGlobalGids(int[] globalGids) {
82 if (!ArrayUtils.isEmpty(globalGids)) {
83 mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
84 }
85 }
86
87 /**
88 * Initialized this instance from another one.
89 *
90 * @param other The other instance.
91 */
92 public void copyFrom(PermissionsState other) {
93 if (mPermissions != null) {
94 if (other.mPermissions == null) {
95 mPermissions = null;
96 } else {
97 mPermissions.clear();
98 }
99 }
100 if (other.mPermissions != null) {
101 if (mPermissions == null) {
102 mPermissions = new ArrayMap<>();
103 }
104 final int permissionCount = other.mPermissions.size();
105 for (int i = 0; i < permissionCount; i++) {
106 String name = other.mPermissions.keyAt(i);
107 PermissionData permissionData = other.mPermissions.valueAt(i);
108 mPermissions.put(name, new PermissionData(permissionData));
109 }
110 }
111
112 mGlobalGids = NO_GIDS;
113 if (other.mGlobalGids != NO_GIDS) {
114 mGlobalGids = Arrays.copyOf(other.mGlobalGids,
115 other.mGlobalGids.length);
116 }
117 }
118
119 /**
120 * Grant an install permission.
121 *
122 * @param permission The permission to grant.
123 * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
124 * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
125 * #PERMISSION_OPERATION_FAILURE}.
126 */
127 public int grantInstallPermission(BasePermission permission) {
128 return grantPermission(permission, UserHandle.USER_ALL);
129 }
130
131 /**
132 * Revoke an install permission.
133 *
134 * @param permission The permission to revoke.
135 * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
136 * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
137 * #PERMISSION_OPERATION_FAILURE}.
138 */
139 public int revokeInstallPermission(BasePermission permission) {
140 return revokePermission(permission, UserHandle.USER_ALL);
141 }
142
143 /**
144 * Grant a runtime permission.
145 *
146 * @param permission The permission to grant.
147 * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
148 * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
149 * #PERMISSION_OPERATION_FAILURE}.
150 */
151 public int grantRuntimePermission(BasePermission permission, int userId) {
Svet Ganovd5752bd2015-03-25 22:57:39 -0700152 if (userId == UserHandle.USER_ALL) {
153 return PERMISSION_OPERATION_FAILURE;
154 }
Svetoslavc6d1c342015-02-26 14:44:43 -0800155 return grantPermission(permission, userId);
156 }
157
158 /**
159 * Revoke a runtime permission for a given device user.
160 *
161 * @param permission The permission to revoke.
162 * @param userId The device user id.
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}.
166 */
167 public int revokeRuntimePermission(BasePermission permission, int userId) {
Svet Ganovd5752bd2015-03-25 22:57:39 -0700168 if (userId == UserHandle.USER_ALL) {
169 return PERMISSION_OPERATION_FAILURE;
170 }
Svetoslavc6d1c342015-02-26 14:44:43 -0800171 return revokePermission(permission, userId);
172 }
173
174 /**
175 * Gets whether this state has a given permission, regardless if
176 * it is install time or runtime one.
177 *
178 * @param name The permission name.
179 * @return Whether this state has the permission.
180 */
181 public boolean hasPermission(String name) {
182 return mPermissions != null && mPermissions.get(name) != null;
183 }
184
185 /**
186 * Gets whether this state has a given runtime permission for a
187 * given device user id.
188 *
189 * @param name The permission name.
190 * @param userId The device user id.
191 * @return Whether this state has the permission.
192 */
193 public boolean hasRuntimePermission(String name, int userId) {
194 return !hasInstallPermission(name) && hasPermission(name, userId);
195 }
196
197 /**
198 * Gets whether this state has a given install permission.
199 *
200 * @param name The permission name.
201 * @return Whether this state has the permission.
202 */
203 public boolean hasInstallPermission(String name) {
204 return hasPermission(name, UserHandle.USER_ALL);
205 }
206
207 /**
208 * Revokes a permission for all users regardless if it is an install or
209 * a runtime permission.
210 *
211 * @param permission The permission to revoke.
212 * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
213 * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
214 * #PERMISSION_OPERATION_FAILURE}.
215 */
216 public int revokePermission(BasePermission permission) {
217 if (!hasPermission(permission.name)) {
218 return PERMISSION_OPERATION_FAILURE;
219 }
220
221 int result = PERMISSION_OPERATION_SUCCESS;
222
223 PermissionData permissionData = mPermissions.get(permission.name);
Svetoslavb3a6def2015-04-10 15:31:21 -0700224 for (int userId : permissionData.getUserIds()) {
225 if (revokePermission(permission, userId)
226 == PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
227 result = PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
228 break;
Svetoslavc6d1c342015-02-26 14:44:43 -0800229 }
230 }
231
232 mPermissions.remove(permission.name);
233
Jeff Sharkey00f39042015-03-23 16:51:22 -0700234 return result;
Svetoslavc6d1c342015-02-26 14:44:43 -0800235 }
236
237 /**
238 * Gets whether the state has a given permission for the specified
239 * user, regardless if this is an install or a runtime permission.
240 *
241 * @param name The permission name.
242 * @param userId The device user id.
243 * @return Whether the user has the permission.
244 */
245 public boolean hasPermission(String name, int userId) {
246 enforceValidUserId(userId);
247
248 if (mPermissions == null) {
249 return false;
250 }
251
252 PermissionData permissionData = mPermissions.get(name);
253 return permissionData != null && permissionData.hasUserId(userId);
254 }
255
256 /**
257 * Gets all permissions regardless if they are install or runtime.
258 *
259 * @return The permissions or an empty set.
260 */
261 public Set<String> getPermissions() {
262 if (mPermissions != null) {
263 return mPermissions.keySet();
264 }
265
266 return Collections.emptySet();
267 }
268
269 /**
270 * Gets all permissions for a given device user id regardless if they
271 * are install time or runtime permissions.
272 *
273 * @param userId The device user id.
274 * @return The permissions or an empty set.
275 */
276 public Set<String> getPermissions(int userId) {
277 enforceValidUserId(userId);
278
279 if (mPermissions == null) {
280 return Collections.emptySet();
281 }
282
283 Set<String> permissions = new ArraySet<>();
284
285 final int permissionCount = mPermissions.size();
286 for (int i = 0; i < permissionCount; i++) {
287 String permission = mPermissions.keyAt(i);
288 if (userId == UserHandle.USER_ALL) {
289 if (hasInstallPermission(permission)) {
290 permissions.add(permission);
291 }
292 } else {
293 if (hasRuntimePermission(permission, userId)) {
294 permissions.add(permission);
295 }
296 }
297 }
298
299 return permissions;
300 }
301
302 /**
303 * Gets all runtime permissions.
304 *
305 * @return The permissions or an empty set.
306 */
307 public Set<String> getRuntimePermissions(int userId) {
308 return getPermissions(userId);
309 }
310
311 /**
312 * Gets all install permissions.
313 *
314 * @return The permissions or an empty set.
315 */
316 public Set<String> getInstallPermissions() {
317 return getPermissions(UserHandle.USER_ALL);
318 }
319
320 /**
321 * Compute the Linux gids for a given device user from the permissions
322 * granted to this user. Note that these are computed to avoid additional
323 * state as they are rarely accessed.
324 *
325 * @param userId The device user id.
326 * @return The gids for the device user.
327 */
328 public int[] computeGids(int userId) {
329 enforceValidUserId(userId);
330
331 int[] gids = mGlobalGids;
332
333 if (mPermissions != null) {
334 final int permissionCount = mPermissions.size();
335 for (int i = 0; i < permissionCount; i++) {
336 String permission = mPermissions.keyAt(i);
337 if (!hasPermission(permission, userId)) {
338 continue;
339 }
340 PermissionData permissionData = mPermissions.valueAt(i);
Jeff Sharkey00f39042015-03-23 16:51:22 -0700341 final int[] permGids = permissionData.computeGids(userId);
Svetoslavc6d1c342015-02-26 14:44:43 -0800342 if (permGids != NO_GIDS) {
343 gids = appendInts(gids, permGids);
344 }
345 }
346 }
347
348 return gids;
349 }
350
351 /**
352 * Compute the Linux gids for all device users from the permissions
353 * granted to these users.
354 *
355 * @return The gids for all device users.
356 */
357 public int[] computeGids() {
358 int[] gids = mGlobalGids;
359
360 for (int userId : UserManagerService.getInstance().getUserIds()) {
361 final int[] userGids = computeGids(userId);
362 gids = appendInts(gids, userGids);
363 }
364
365 return gids;
366 }
367
368 /**
369 * Resets the internal state of this object.
370 */
371 public void reset() {
372 mGlobalGids = NO_GIDS;
373 mPermissions = null;
374 }
375
376 private int grantPermission(BasePermission permission, int userId) {
377 if (hasPermission(permission.name, userId)) {
378 return PERMISSION_OPERATION_FAILURE;
379 }
380
Svetoslavb3a6def2015-04-10 15:31:21 -0700381 final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
Svetoslavc6d1c342015-02-26 14:44:43 -0800382 final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
383
384 if (mPermissions == null) {
385 mPermissions = new ArrayMap<>();
386 }
387
388 PermissionData permissionData = mPermissions.get(permission.name);
389 if (permissionData == null) {
Jeff Sharkey00f39042015-03-23 16:51:22 -0700390 permissionData = new PermissionData(permission);
Svetoslavc6d1c342015-02-26 14:44:43 -0800391 mPermissions.put(permission.name, permissionData);
392 }
393
394 if (!permissionData.addUserId(userId)) {
395 return PERMISSION_OPERATION_FAILURE;
396 }
397
398 if (hasGids) {
399 final int[] newGids = computeGids(userId);
400 if (oldGids.length != newGids.length) {
401 return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
402 }
403 }
404
405 return PERMISSION_OPERATION_SUCCESS;
406 }
407
408 private int revokePermission(BasePermission permission, int userId) {
409 if (!hasPermission(permission.name, userId)) {
410 return PERMISSION_OPERATION_FAILURE;
411 }
412
Svetoslavb3a6def2015-04-10 15:31:21 -0700413 final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
Svetoslavc6d1c342015-02-26 14:44:43 -0800414 final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
415
416 PermissionData permissionData = mPermissions.get(permission.name);
417
418 if (!permissionData.removeUserId(userId)) {
419 return PERMISSION_OPERATION_FAILURE;
420 }
421
422 if (permissionData.getUserIds() == USERS_NONE) {
423 mPermissions.remove(permission.name);
424 }
425
426 if (mPermissions.isEmpty()) {
427 mPermissions = null;
428 }
429
430 if (hasGids) {
431 final int[] newGids = computeGids(userId);
432 if (oldGids.length != newGids.length) {
433 return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
434 }
435 }
436
437 return PERMISSION_OPERATION_SUCCESS;
438 }
439
440 private static int[] appendInts(int[] current, int[] added) {
441 if (current != null && added != null) {
442 for (int guid : added) {
443 current = ArrayUtils.appendInt(current, guid);
444 }
445 }
446 return current;
447 }
448
449 private static void enforceValidUserId(int userId) {
450 if (userId != UserHandle.USER_ALL && userId < 0) {
451 throw new IllegalArgumentException("Invalid userId:" + userId);
452 }
453 }
454
455 private static final class PermissionData {
Jeff Sharkey00f39042015-03-23 16:51:22 -0700456 private final BasePermission mPerm;
Svetoslavc6d1c342015-02-26 14:44:43 -0800457 private int[] mUserIds = USERS_NONE;
458
Jeff Sharkey00f39042015-03-23 16:51:22 -0700459 public PermissionData(BasePermission perm) {
460 mPerm = perm;
Svetoslavc6d1c342015-02-26 14:44:43 -0800461 }
462
463 public PermissionData(PermissionData other) {
Jeff Sharkey00f39042015-03-23 16:51:22 -0700464 this(other.mPerm);
Svetoslavc6d1c342015-02-26 14:44:43 -0800465
466 if (other.mUserIds == USERS_ALL || other.mUserIds == USERS_NONE) {
467 mUserIds = other.mUserIds;
468 } else {
469 mUserIds = Arrays.copyOf(other.mUserIds, other.mUserIds.length);
470 }
471 }
472
Jeff Sharkey00f39042015-03-23 16:51:22 -0700473 public int[] computeGids(int userId) {
474 return mPerm.computeGids(userId);
Svetoslavc6d1c342015-02-26 14:44:43 -0800475 }
476
477 public int[] getUserIds() {
478 return mUserIds;
479 }
480
481 public boolean hasUserId(int userId) {
482 if (mUserIds == USERS_ALL) {
483 return true;
484 }
485
486 if (userId != UserHandle.USER_ALL) {
487 return ArrayUtils.contains(mUserIds, userId);
488 }
489
490 return false;
491 }
492
493 public boolean addUserId(int userId) {
494 if (hasUserId(userId)) {
495 return false;
496 }
497
498 if (userId == UserHandle.USER_ALL) {
499 mUserIds = USERS_ALL;
500 return true;
501 }
502
503 mUserIds = ArrayUtils.appendInt(mUserIds, userId);
504
505 return true;
506 }
507
508 public boolean removeUserId(int userId) {
509 if (!hasUserId(userId)) {
510 return false;
511 }
512
513 if (mUserIds == USERS_ALL) {
514 mUserIds = UserManagerService.getInstance().getUserIds();
515 }
516
517 mUserIds = ArrayUtils.removeInt(mUserIds, userId);
518
519 if (mUserIds.length == 0) {
520 mUserIds = USERS_NONE;
521 }
522
523 return true;
524 }
525 }
526}