1 /*
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 
17 package com.android.server.pm.permission;
18 
19 import android.content.pm.PackageManager;
20 import android.os.UserHandle;
21 import android.util.ArrayMap;
22 import android.util.ArraySet;
23 import android.util.SparseArray;
24 import android.util.SparseBooleanArray;
25 
26 import com.android.internal.util.ArrayUtils;
27 
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.List;
32 import java.util.Set;
33 import com.android.internal.annotations.GuardedBy;
34 
35 /**
36  * This class encapsulates the permissions for a package or a shared user.
37  * <p>
38  * There are two types of permissions: install (granted at installation)
39  * and runtime (granted at runtime). Install permissions are granted to
40  * all device users while runtime permissions are granted explicitly to
41  * specific users.
42  * </p>
43  * <p>
44  * The permissions are kept on a per device user basis. For example, an
45  * application may have some runtime permissions granted under the device
46  * owner but not granted under the secondary user.
47  * <p>
48  * This class is also responsible for keeping track of the Linux gids per
49  * user for a package or a shared user. The gids are computed as a set of
50  * the gids for all granted permissions' gids on a per user basis.
51  * </p>
52  */
53 public final class PermissionsState {
54 
55     /** The permission operation failed. */
56     public static final int PERMISSION_OPERATION_FAILURE = -1;
57 
58     /** The permission operation succeeded and no gids changed. */
59     public static final int PERMISSION_OPERATION_SUCCESS = 0;
60 
61     /** The permission operation succeeded and gids changed. */
62     public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1;
63 
64     private static final int[] NO_GIDS = {};
65 
66     private final Object mLock = new Object();
67 
68     @GuardedBy("mLock")
69     private ArrayMap<String, PermissionData> mPermissions;
70 
71     private int[] mGlobalGids = NO_GIDS;
72 
73     private SparseBooleanArray mPermissionReviewRequired;
74 
PermissionsState()75     public PermissionsState() {
76         /* do nothing */
77     }
78 
PermissionsState(PermissionsState prototype)79     public PermissionsState(PermissionsState prototype) {
80         copyFrom(prototype);
81     }
82 
83     /**
84      * Sets the global gids, applicable to all users.
85      *
86      * @param globalGids The global gids.
87      */
setGlobalGids(int[] globalGids)88     public void setGlobalGids(int[] globalGids) {
89         if (!ArrayUtils.isEmpty(globalGids)) {
90             mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
91         }
92     }
93 
94     /**
95      * Initialized this instance from another one.
96      *
97      * @param other The other instance.
98      */
copyFrom(PermissionsState other)99     public void copyFrom(PermissionsState other) {
100         if (other == this) {
101             return;
102         }
103 
104         synchronized (mLock) {
105             if (mPermissions != null) {
106                 if (other.mPermissions == null) {
107                     mPermissions = null;
108                 } else {
109                     mPermissions.clear();
110                 }
111             }
112             if (other.mPermissions != null) {
113                 if (mPermissions == null) {
114                     mPermissions = new ArrayMap<>();
115                 }
116                 final int permissionCount = other.mPermissions.size();
117                 for (int i = 0; i < permissionCount; i++) {
118                     String name = other.mPermissions.keyAt(i);
119                     PermissionData permissionData = other.mPermissions.valueAt(i);
120                     mPermissions.put(name, new PermissionData(permissionData));
121                 }
122             }
123         }
124 
125         mGlobalGids = NO_GIDS;
126         if (other.mGlobalGids != NO_GIDS) {
127             mGlobalGids = Arrays.copyOf(other.mGlobalGids,
128                     other.mGlobalGids.length);
129         }
130 
131         if (mPermissionReviewRequired != null) {
132             if (other.mPermissionReviewRequired == null) {
133                 mPermissionReviewRequired = null;
134             } else {
135                 mPermissionReviewRequired.clear();
136             }
137         }
138         if (other.mPermissionReviewRequired != null) {
139             if (mPermissionReviewRequired == null) {
140                 mPermissionReviewRequired = new SparseBooleanArray();
141             }
142             final int userCount = other.mPermissionReviewRequired.size();
143             for (int i = 0; i < userCount; i++) {
144                 final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i);
145                 mPermissionReviewRequired.put(other.mPermissionReviewRequired.keyAt(i),
146                         reviewRequired);
147             }
148         }
149     }
150 
151     @Override
equals(Object obj)152     public boolean equals(Object obj) {
153         if (this == obj) {
154             return true;
155         }
156         if (obj == null) {
157             return false;
158         }
159         if (getClass() != obj.getClass()) {
160             return false;
161         }
162         final PermissionsState other = (PermissionsState) obj;
163 
164         synchronized (mLock) {
165             if (mPermissions == null) {
166                 if (other.mPermissions != null) {
167                     return false;
168                 }
169             } else if (!mPermissions.equals(other.mPermissions)) {
170                 return false;
171             }
172         }
173 
174         if (mPermissionReviewRequired == null) {
175             if (other.mPermissionReviewRequired != null) {
176                 return false;
177             }
178         } else if (!mPermissionReviewRequired.equals(other.mPermissionReviewRequired)) {
179             return false;
180         }
181         return Arrays.equals(mGlobalGids, other.mGlobalGids);
182     }
183 
isPermissionReviewRequired(int userId)184     public boolean isPermissionReviewRequired(int userId) {
185         return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId);
186     }
187 
188     /**
189      * Grant an install permission.
190      *
191      * @param permission The permission to grant.
192      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
193      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
194      *     #PERMISSION_OPERATION_FAILURE}.
195      */
grantInstallPermission(BasePermission permission)196     public int grantInstallPermission(BasePermission permission) {
197         return grantPermission(permission, UserHandle.USER_ALL);
198     }
199 
200     /**
201      * Revoke an install permission.
202      *
203      * @param permission The permission to revoke.
204      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
205      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
206      *     #PERMISSION_OPERATION_FAILURE}.
207      */
revokeInstallPermission(BasePermission permission)208     public int revokeInstallPermission(BasePermission permission) {
209         return revokePermission(permission, UserHandle.USER_ALL);
210     }
211 
212     /**
213      * Grant a runtime permission for a given device user.
214      *
215      * @param permission The permission to grant.
216      * @param userId The device user id.
217      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
218      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
219      *     #PERMISSION_OPERATION_FAILURE}.
220      */
grantRuntimePermission(BasePermission permission, int userId)221     public int grantRuntimePermission(BasePermission permission, int userId) {
222         enforceValidUserId(userId);
223         if (userId == UserHandle.USER_ALL) {
224             return PERMISSION_OPERATION_FAILURE;
225         }
226         return grantPermission(permission, userId);
227     }
228 
229     /**
230      *  Revoke a runtime permission for a given device user.
231      *
232      * @param permission The permission to revoke.
233      * @param userId The device user id.
234      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
235      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
236      *     #PERMISSION_OPERATION_FAILURE}.
237      */
revokeRuntimePermission(BasePermission permission, int userId)238     public int revokeRuntimePermission(BasePermission permission, int userId) {
239         enforceValidUserId(userId);
240         if (userId == UserHandle.USER_ALL) {
241             return PERMISSION_OPERATION_FAILURE;
242         }
243         return revokePermission(permission, userId);
244     }
245 
246     /**
247      * Gets whether this state has a given runtime permission for a
248      * given device user id.
249      *
250      * @param name The permission name.
251      * @param userId The device user id.
252      * @return Whether this state has the permission.
253      */
hasRuntimePermission(String name, int userId)254     public boolean hasRuntimePermission(String name, int userId) {
255         enforceValidUserId(userId);
256         return !hasInstallPermission(name) && hasPermission(name, userId);
257     }
258 
259     /**
260      * Gets whether this state has a given install permission.
261      *
262      * @param name The permission name.
263      * @return Whether this state has the permission.
264      */
hasInstallPermission(String name)265     public boolean hasInstallPermission(String name) {
266         return hasPermission(name, UserHandle.USER_ALL);
267     }
268 
269     /**
270      * Gets whether the state has a given permission for the specified
271      * user, regardless if this is an install or a runtime permission.
272      *
273      * @param name The permission name.
274      * @param userId The device user id.
275      * @return Whether the user has the permission.
276      */
hasPermission(String name, int userId)277     public boolean hasPermission(String name, int userId) {
278         enforceValidUserId(userId);
279 
280         synchronized (mLock) {
281             if (mPermissions == null) {
282                 return false;
283             }
284             PermissionData permissionData = mPermissions.get(name);
285 
286             return permissionData != null && permissionData.isGranted(userId);
287         }
288 
289     }
290 
291     /**
292      * Returns whether the state has any known request for the given permission name,
293      * whether or not it has been granted.
294      */
hasRequestedPermission(ArraySet<String> names)295     public boolean hasRequestedPermission(ArraySet<String> names) {
296         synchronized (mLock) {
297             if (mPermissions == null) {
298                 return false;
299             }
300             for (int i=names.size()-1; i>=0; i--) {
301                 if (mPermissions.get(names.valueAt(i)) != null) {
302                     return true;
303                 }
304             }
305         }
306 
307         return false;
308     }
309 
310     /**
311      * Returns whether the state has any known request for the given permission name,
312      * whether or not it has been granted.
313      */
hasRequestedPermission(String name)314     public boolean hasRequestedPermission(String name) {
315         return mPermissions != null && (mPermissions.get(name) != null);
316     }
317     /**
318      * Gets all permissions for a given device user id regardless if they
319      * are install time or runtime permissions.
320      *
321      * @param userId The device user id.
322      * @return The permissions or an empty set.
323      */
getPermissions(int userId)324     public Set<String> getPermissions(int userId) {
325         enforceValidUserId(userId);
326 
327         synchronized (mLock) {
328             if (mPermissions == null) {
329                 return Collections.emptySet();
330             }
331 
332             Set<String> permissions = new ArraySet<>(mPermissions.size());
333 
334             final int permissionCount = mPermissions.size();
335             for (int i = 0; i < permissionCount; i++) {
336                 String permission = mPermissions.keyAt(i);
337 
338                 if (hasInstallPermission(permission)) {
339                     permissions.add(permission);
340                     continue;
341                 }
342 
343                 if (userId != UserHandle.USER_ALL) {
344                     if (hasRuntimePermission(permission, userId)) {
345                         permissions.add(permission);
346                     }
347                 }
348             }
349 
350             return permissions;
351         }
352     }
353 
354     /**
355      * Gets the state for an install permission or null if no such.
356      *
357      * @param name The permission name.
358      * @return The permission state.
359      */
getInstallPermissionState(String name)360     public PermissionState getInstallPermissionState(String name) {
361         return getPermissionState(name, UserHandle.USER_ALL);
362     }
363 
364     /**
365      * Gets the state for a runtime permission or null if no such.
366      *
367      * @param name The permission name.
368      * @param userId The device user id.
369      * @return The permission state.
370      */
getRuntimePermissionState(String name, int userId)371     public PermissionState getRuntimePermissionState(String name, int userId) {
372         enforceValidUserId(userId);
373         return getPermissionState(name, userId);
374     }
375 
376     /**
377      * Gets all install permission states.
378      *
379      * @return The permission states or an empty set.
380      */
getInstallPermissionStates()381     public List<PermissionState> getInstallPermissionStates() {
382         return getPermissionStatesInternal(UserHandle.USER_ALL);
383     }
384 
385     /**
386      * Gets all runtime permission states.
387      *
388      * @return The permission states or an empty set.
389      */
getRuntimePermissionStates(int userId)390     public List<PermissionState> getRuntimePermissionStates(int userId) {
391         enforceValidUserId(userId);
392         return getPermissionStatesInternal(userId);
393     }
394 
395     /**
396      * Gets the flags for a permission regardless if it is install or
397      * runtime permission.
398      *
399      * @param name The permission name.
400      * @return The permission state or null if no such.
401      */
getPermissionFlags(String name, int userId)402     public int getPermissionFlags(String name, int userId) {
403         PermissionState installPermState = getInstallPermissionState(name);
404         if (installPermState != null) {
405             return installPermState.getFlags();
406         }
407         PermissionState runtimePermState = getRuntimePermissionState(name, userId);
408         if (runtimePermState != null) {
409             return runtimePermState.getFlags();
410         }
411         return 0;
412     }
413 
414     /**
415      * Update the flags associated with a given permission.
416      * @param permission The permission whose flags to update.
417      * @param userId The user for which to update.
418      * @param flagMask Mask for which flags to change.
419      * @param flagValues New values for the mask flags.
420      * @return Whether the permission flags changed.
421      */
updatePermissionFlags(BasePermission permission, int userId, int flagMask, int flagValues)422     public boolean updatePermissionFlags(BasePermission permission, int userId,
423             int flagMask, int flagValues) {
424         enforceValidUserId(userId);
425 
426         final boolean mayChangeFlags = flagValues != 0 || flagMask != 0;
427 
428         synchronized (mLock) {
429             if (mPermissions == null) {
430                 if (!mayChangeFlags) {
431                     return false;
432                 }
433                 ensurePermissionData(permission);
434             }
435         }
436 
437         PermissionData permissionData = null;
438         synchronized (mLock) {
439             permissionData = mPermissions.get(permission.getName());
440         }
441 
442         if (permissionData == null) {
443             if (!mayChangeFlags) {
444                 return false;
445             }
446             permissionData = ensurePermissionData(permission);
447         }
448 
449         final int oldFlags = permissionData.getFlags(userId);
450 
451         final boolean updated = permissionData.updateFlags(userId, flagMask, flagValues);
452         if (updated) {
453             final int newFlags = permissionData.getFlags(userId);
454             if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
455                     && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
456                 if (mPermissionReviewRequired == null) {
457                     mPermissionReviewRequired = new SparseBooleanArray();
458                 }
459                 mPermissionReviewRequired.put(userId, true);
460             } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
461                     && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
462                 if (mPermissionReviewRequired != null && !hasPermissionRequiringReview(userId)) {
463                     mPermissionReviewRequired.delete(userId);
464                     if (mPermissionReviewRequired.size() <= 0) {
465                         mPermissionReviewRequired = null;
466                     }
467                 }
468             }
469         }
470         return updated;
471     }
472 
hasPermissionRequiringReview(int userId)473     private boolean hasPermissionRequiringReview(int userId) {
474         synchronized (mLock) {
475             final int permissionCount = mPermissions.size();
476             for (int i = 0; i < permissionCount; i++) {
477                 final PermissionData permission = mPermissions.valueAt(i);
478                 if ((permission.getFlags(userId)
479                         & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
480                     return true;
481                 }
482             }
483         }
484 
485         return false;
486     }
487 
updatePermissionFlagsForAllPermissions( int userId, int flagMask, int flagValues)488     public boolean updatePermissionFlagsForAllPermissions(
489             int userId, int flagMask, int flagValues) {
490         enforceValidUserId(userId);
491 
492         synchronized (mLock) {
493             if (mPermissions == null) {
494                 return false;
495             }
496             boolean changed = false;
497             final int permissionCount = mPermissions.size();
498             for (int i = 0; i < permissionCount; i++) {
499                 PermissionData permissionData = mPermissions.valueAt(i);
500                 changed |= permissionData.updateFlags(userId, flagMask, flagValues);
501             }
502 
503             return changed;
504         }
505     }
506 
507     /**
508      * Compute the Linux gids for a given device user from the permissions
509      * granted to this user. Note that these are computed to avoid additional
510      * state as they are rarely accessed.
511      *
512      * @param userId The device user id.
513      * @return The gids for the device user.
514      */
computeGids(int userId)515     public int[] computeGids(int userId) {
516         enforceValidUserId(userId);
517 
518         int[] gids = mGlobalGids;
519 
520         synchronized (mLock) {
521             if (mPermissions != null) {
522                 final int permissionCount = mPermissions.size();
523                 for (int i = 0; i < permissionCount; i++) {
524                     String permission = mPermissions.keyAt(i);
525                     if (!hasPermission(permission, userId)) {
526                         continue;
527                     }
528                     PermissionData permissionData = mPermissions.valueAt(i);
529                     final int[] permGids = permissionData.computeGids(userId);
530                     if (permGids != NO_GIDS) {
531                         gids = appendInts(gids, permGids);
532                     }
533                 }
534             }
535         }
536 
537         return gids;
538     }
539 
540     /**
541      * Compute the Linux gids for all device users from the permissions
542      * granted to these users.
543      *
544      * @return The gids for all device users.
545      */
computeGids(int[] userIds)546     public int[] computeGids(int[] userIds) {
547         int[] gids = mGlobalGids;
548 
549         for (int userId : userIds) {
550             final int[] userGids = computeGids(userId);
551             gids = appendInts(gids, userGids);
552         }
553 
554         return gids;
555     }
556 
557     /**
558      * Resets the internal state of this object.
559      */
reset()560     public void reset() {
561         mGlobalGids = NO_GIDS;
562 
563         synchronized (mLock) {
564             mPermissions = null;
565         }
566 
567         mPermissionReviewRequired = null;
568     }
569 
getPermissionState(String name, int userId)570     private PermissionState getPermissionState(String name, int userId) {
571         synchronized (mLock) {
572             if (mPermissions == null) {
573                 return null;
574             }
575             PermissionData permissionData = mPermissions.get(name);
576             if (permissionData == null) {
577                 return null;
578             }
579 
580             return permissionData.getPermissionState(userId);
581         }
582     }
583 
getPermissionStatesInternal(int userId)584     private List<PermissionState> getPermissionStatesInternal(int userId) {
585         enforceValidUserId(userId);
586 
587         synchronized (mLock) {
588             if (mPermissions == null) {
589                 return Collections.emptyList();
590             }
591 
592             List<PermissionState> permissionStates = new ArrayList<>();
593 
594             final int permissionCount = mPermissions.size();
595             for (int i = 0; i < permissionCount; i++) {
596                 PermissionData permissionData = mPermissions.valueAt(i);
597 
598                 PermissionState permissionState = permissionData.getPermissionState(userId);
599                 if (permissionState != null) {
600                     permissionStates.add(permissionState);
601                 }
602             }
603 
604             return permissionStates;
605         }
606     }
607 
grantPermission(BasePermission permission, int userId)608     private int grantPermission(BasePermission permission, int userId) {
609         if (hasPermission(permission.getName(), userId)) {
610             return PERMISSION_OPERATION_SUCCESS;
611         }
612 
613         final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
614         final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
615 
616         PermissionData permissionData = ensurePermissionData(permission);
617 
618         if (!permissionData.grant(userId)) {
619             return PERMISSION_OPERATION_FAILURE;
620         }
621 
622         if (hasGids) {
623             final int[] newGids = computeGids(userId);
624             if (oldGids.length != newGids.length) {
625                 return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
626             }
627         }
628 
629         return PERMISSION_OPERATION_SUCCESS;
630     }
631 
revokePermission(BasePermission permission, int userId)632     private int revokePermission(BasePermission permission, int userId) {
633         final String permName = permission.getName();
634         if (!hasPermission(permName, userId)) {
635             return PERMISSION_OPERATION_SUCCESS;
636         }
637 
638         final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
639         final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
640 
641         PermissionData permissionData = null;
642         synchronized (mLock) {
643             permissionData = mPermissions.get(permName);
644         }
645 
646         if (!permissionData.revoke(userId)) {
647             return PERMISSION_OPERATION_FAILURE;
648         }
649 
650         if (permissionData.isDefault()) {
651             ensureNoPermissionData(permName);
652         }
653 
654         if (hasGids) {
655             final int[] newGids = computeGids(userId);
656             if (oldGids.length != newGids.length) {
657                 return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
658             }
659         }
660 
661         return PERMISSION_OPERATION_SUCCESS;
662     }
663 
664     // TODO: fix this to use arraycopy and append all ints in one go
appendInts(int[] current, int[] added)665     private static int[] appendInts(int[] current, int[] added) {
666         if (current != null && added != null) {
667             for (int guid : added) {
668                 current = ArrayUtils.appendInt(current, guid);
669             }
670         }
671         return current;
672     }
673 
enforceValidUserId(int userId)674     private static void enforceValidUserId(int userId) {
675         if (userId != UserHandle.USER_ALL && userId < 0) {
676             throw new IllegalArgumentException("Invalid userId:" + userId);
677         }
678     }
679 
ensurePermissionData(BasePermission permission)680     private PermissionData ensurePermissionData(BasePermission permission) {
681         final String permName = permission.getName();
682 
683         synchronized (mLock) {
684             if (mPermissions == null) {
685                 mPermissions = new ArrayMap<>();
686             }
687             PermissionData permissionData = mPermissions.get(permName);
688             if (permissionData == null) {
689                 permissionData = new PermissionData(permission);
690                 mPermissions.put(permName, permissionData);
691             }
692             return permissionData;
693         }
694 
695     }
696 
ensureNoPermissionData(String name)697     private void ensureNoPermissionData(String name) {
698         synchronized (mLock) {
699             if (mPermissions == null) {
700                 return;
701             }
702             mPermissions.remove(name);
703             if (mPermissions.isEmpty()) {
704                 mPermissions = null;
705             }
706         }
707 
708     }
709 
710     private static final class PermissionData {
711         private final BasePermission mPerm;
712         private SparseArray<PermissionState> mUserStates = new SparseArray<>();
713 
PermissionData(BasePermission perm)714         public PermissionData(BasePermission perm) {
715             mPerm = perm;
716         }
717 
PermissionData(PermissionData other)718         public PermissionData(PermissionData other) {
719             this(other.mPerm);
720             final int otherStateCount = other.mUserStates.size();
721             for (int i = 0; i < otherStateCount; i++) {
722                 final int otherUserId = other.mUserStates.keyAt(i);
723                 PermissionState otherState = other.mUserStates.valueAt(i);
724                 mUserStates.put(otherUserId, new PermissionState(otherState));
725             }
726         }
727 
computeGids(int userId)728         public int[] computeGids(int userId) {
729             return mPerm.computeGids(userId);
730         }
731 
isGranted(int userId)732         public boolean isGranted(int userId) {
733             if (isInstallPermission()) {
734                 userId = UserHandle.USER_ALL;
735             }
736 
737             PermissionState userState = mUserStates.get(userId);
738             if (userState == null) {
739                 return false;
740             }
741 
742             return userState.mGranted;
743         }
744 
grant(int userId)745         public boolean grant(int userId) {
746             if (!isCompatibleUserId(userId)) {
747                 return false;
748             }
749 
750             if (isGranted(userId)) {
751                 return false;
752             }
753 
754             PermissionState userState = mUserStates.get(userId);
755             if (userState == null) {
756                 userState = new PermissionState(mPerm.getName());
757                 mUserStates.put(userId, userState);
758             }
759 
760             userState.mGranted = true;
761 
762             return true;
763         }
764 
revoke(int userId)765         public boolean revoke(int userId) {
766             if (!isCompatibleUserId(userId)) {
767                 return false;
768             }
769 
770             if (!isGranted(userId)) {
771                 return false;
772             }
773 
774             PermissionState userState = mUserStates.get(userId);
775             userState.mGranted = false;
776 
777             if (userState.isDefault()) {
778                 mUserStates.remove(userId);
779             }
780 
781             return true;
782         }
783 
getPermissionState(int userId)784         public PermissionState getPermissionState(int userId) {
785             return mUserStates.get(userId);
786         }
787 
getFlags(int userId)788         public int getFlags(int userId) {
789             PermissionState userState = mUserStates.get(userId);
790             if (userState != null) {
791                 return userState.mFlags;
792             }
793             return 0;
794         }
795 
isDefault()796         public boolean isDefault() {
797             return mUserStates.size() <= 0;
798         }
799 
isInstallPermissionKey(int userId)800         public static boolean isInstallPermissionKey(int userId) {
801             return userId == UserHandle.USER_ALL;
802         }
803 
updateFlags(int userId, int flagMask, int flagValues)804         public boolean updateFlags(int userId, int flagMask, int flagValues) {
805             if (isInstallPermission()) {
806                 userId = UserHandle.USER_ALL;
807             }
808 
809             if (!isCompatibleUserId(userId)) {
810                 return false;
811             }
812 
813             final int newFlags = flagValues & flagMask;
814 
815             PermissionState userState = mUserStates.get(userId);
816             if (userState != null) {
817                 final int oldFlags = userState.mFlags;
818                 userState.mFlags = (userState.mFlags & ~flagMask) | newFlags;
819                 if (userState.isDefault()) {
820                     mUserStates.remove(userId);
821                 }
822                 return userState.mFlags != oldFlags;
823             } else if (newFlags != 0) {
824                 userState = new PermissionState(mPerm.getName());
825                 userState.mFlags = newFlags;
826                 mUserStates.put(userId, userState);
827                 return true;
828             }
829 
830             return false;
831         }
832 
isCompatibleUserId(int userId)833         private boolean isCompatibleUserId(int userId) {
834             return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId));
835         }
836 
isInstallPermission()837         private boolean isInstallPermission() {
838             return mUserStates.size() == 1
839                     && mUserStates.get(UserHandle.USER_ALL) != null;
840         }
841     }
842 
843     public static final class PermissionState {
844         private final String mName;
845         private boolean mGranted;
846         private int mFlags;
847 
PermissionState(String name)848         public PermissionState(String name) {
849             mName = name;
850         }
851 
PermissionState(PermissionState other)852         public PermissionState(PermissionState other) {
853             mName = other.mName;
854             mGranted = other.mGranted;
855             mFlags = other.mFlags;
856         }
857 
isDefault()858         public boolean isDefault() {
859             return !mGranted && mFlags == 0;
860         }
861 
getName()862         public String getName() {
863             return mName;
864         }
865 
isGranted()866         public boolean isGranted() {
867             return mGranted;
868         }
869 
getFlags()870         public int getFlags() {
871             return mFlags;
872         }
873     }
874 }
875