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
17package com.android.server.pm;
18
19import android.content.pm.PackageManager;
20import android.os.UserHandle;
21import android.util.ArrayMap;
22import android.util.ArraySet;
23
24import android.util.SparseArray;
25import android.util.SparseBooleanArray;
26import com.android.internal.util.ArrayUtils;
27
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Collections;
31import java.util.List;
32import java.util.Set;
33
34/**
35 * This class encapsulates the permissions for a package or a shared user.
36 * <p>
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
40 * specific users.
41 * </p>
42 * <p>
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.
46 * <p>
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.
50 * </p>
51 */
52public final class PermissionsState {
53
54    /** The permission operation failed. */
55    public static final int PERMISSION_OPERATION_FAILURE = -1;
56
57    /** The permission operation succeeded and no gids changed. */
58    public static final int PERMISSION_OPERATION_SUCCESS = 0;
59
60    /** The permission operation succeeded and gids changed. */
61    public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1;
62
63    private static final int[] NO_GIDS = {};
64
65    private ArrayMap<String, PermissionData> mPermissions;
66
67    private int[] mGlobalGids = NO_GIDS;
68
69    private SparseBooleanArray mPermissionReviewRequired;
70
71    public PermissionsState() {
72        /* do nothing */
73    }
74
75    public PermissionsState(PermissionsState prototype) {
76        copyFrom(prototype);
77    }
78
79    /**
80     * Sets the global gids, applicable to all users.
81     *
82     * @param globalGids The global gids.
83     */
84    public void setGlobalGids(int[] globalGids) {
85        if (!ArrayUtils.isEmpty(globalGids)) {
86            mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
87        }
88    }
89
90    /**
91     * Initialized this instance from another one.
92     *
93     * @param other The other instance.
94     */
95    public void copyFrom(PermissionsState other) {
96        if (other == this) {
97            return;
98        }
99        if (mPermissions != null) {
100            if (other.mPermissions == null) {
101                mPermissions = null;
102            } else {
103                mPermissions.clear();
104            }
105        }
106        if (other.mPermissions != null) {
107            if (mPermissions == null) {
108                mPermissions = new ArrayMap<>();
109            }
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));
115            }
116        }
117
118        mGlobalGids = NO_GIDS;
119        if (other.mGlobalGids != NO_GIDS) {
120            mGlobalGids = Arrays.copyOf(other.mGlobalGids,
121                    other.mGlobalGids.length);
122        }
123
124        if (mPermissionReviewRequired != null) {
125            if (other.mPermissionReviewRequired == null) {
126                mPermissionReviewRequired = null;
127            } else {
128                mPermissionReviewRequired.clear();
129            }
130        }
131        if (other.mPermissionReviewRequired != null) {
132            if (mPermissionReviewRequired == null) {
133                mPermissionReviewRequired = new SparseBooleanArray();
134            }
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);
139            }
140        }
141    }
142
143    public boolean isPermissionReviewRequired(int userId) {
144        return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId);
145    }
146
147    /**
148     * Grant an install permission.
149     *
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}.
154     */
155    public int grantInstallPermission(BasePermission permission) {
156        return grantPermission(permission, UserHandle.USER_ALL);
157    }
158
159    /**
160     * Revoke an install permission.
161     *
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}.
166     */
167    public int revokeInstallPermission(BasePermission permission) {
168        return revokePermission(permission, UserHandle.USER_ALL);
169    }
170
171    /**
172     * Grant a runtime permission for a given device user.
173     *
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}.
179     */
180    public int grantRuntimePermission(BasePermission permission, int userId) {
181        enforceValidUserId(userId);
182        if (userId == UserHandle.USER_ALL) {
183            return PERMISSION_OPERATION_FAILURE;
184        }
185        return grantPermission(permission, userId);
186    }
187
188    /**
189     *  Revoke a runtime permission for a given device user.
190     *
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}.
196     */
197    public int revokeRuntimePermission(BasePermission permission, int userId) {
198        enforceValidUserId(userId);
199        if (userId == UserHandle.USER_ALL) {
200            return PERMISSION_OPERATION_FAILURE;
201        }
202        return revokePermission(permission, userId);
203    }
204
205    /**
206     * Gets whether this state has a given runtime permission for a
207     * given device user id.
208     *
209     * @param name The permission name.
210     * @param userId The device user id.
211     * @return Whether this state has the permission.
212     */
213    public boolean hasRuntimePermission(String name, int userId) {
214        enforceValidUserId(userId);
215        return !hasInstallPermission(name) && hasPermission(name, userId);
216    }
217
218    /**
219     * Gets whether this state has a given install permission.
220     *
221     * @param name The permission name.
222     * @return Whether this state has the permission.
223     */
224    public boolean hasInstallPermission(String name) {
225        return hasPermission(name, UserHandle.USER_ALL);
226    }
227
228    /**
229     * Gets whether the state has a given permission for the specified
230     * user, regardless if this is an install or a runtime permission.
231     *
232     * @param name The permission name.
233     * @param userId The device user id.
234     * @return Whether the user has the permission.
235     */
236    public boolean hasPermission(String name, int userId) {
237        enforceValidUserId(userId);
238
239        if (mPermissions == null) {
240            return false;
241        }
242
243        PermissionData permissionData = mPermissions.get(name);
244        return permissionData != null && permissionData.isGranted(userId);
245    }
246
247    /**
248     * Returns whether the state has any known request for the given permission name,
249     * whether or not it has been granted.
250     */
251    public boolean hasRequestedPermission(ArraySet<String> names) {
252        if (mPermissions == null) {
253            return false;
254        }
255        for (int i=names.size()-1; i>=0; i--) {
256            if (mPermissions.get(names.valueAt(i)) != null) {
257                return true;
258            }
259        }
260        return false;
261    }
262
263    /**
264     * Gets all permissions for a given device user id regardless if they
265     * are install time or runtime permissions.
266     *
267     * @param userId The device user id.
268     * @return The permissions or an empty set.
269     */
270    public Set<String> getPermissions(int userId) {
271        enforceValidUserId(userId);
272
273        if (mPermissions == null) {
274            return Collections.emptySet();
275        }
276
277        Set<String> permissions = new ArraySet<>(mPermissions.size());
278
279        final int permissionCount = mPermissions.size();
280        for (int i = 0; i < permissionCount; i++) {
281            String permission = mPermissions.keyAt(i);
282
283            if (hasInstallPermission(permission)) {
284                permissions.add(permission);
285                continue;
286            }
287
288            if (userId != UserHandle.USER_ALL) {
289                if (hasRuntimePermission(permission, userId)) {
290                    permissions.add(permission);
291                }
292            }
293        }
294
295        return permissions;
296    }
297
298    /**
299     * Gets the state for an install permission or null if no such.
300     *
301     * @param name The permission name.
302     * @return The permission state.
303     */
304    public PermissionState getInstallPermissionState(String name) {
305        return getPermissionState(name, UserHandle.USER_ALL);
306    }
307
308    /**
309     * Gets the state for a runtime permission or null if no such.
310     *
311     * @param name The permission name.
312     * @param userId The device user id.
313     * @return The permission state.
314     */
315    public PermissionState getRuntimePermissionState(String name, int userId) {
316        enforceValidUserId(userId);
317        return getPermissionState(name, userId);
318    }
319
320    /**
321     * Gets all install permission states.
322     *
323     * @return The permission states or an empty set.
324     */
325    public List<PermissionState> getInstallPermissionStates() {
326        return getPermissionStatesInternal(UserHandle.USER_ALL);
327    }
328
329    /**
330     * Gets all runtime permission states.
331     *
332     * @return The permission states or an empty set.
333     */
334    public List<PermissionState> getRuntimePermissionStates(int userId) {
335        enforceValidUserId(userId);
336        return getPermissionStatesInternal(userId);
337    }
338
339    /**
340     * Gets the flags for a permission regardless if it is install or
341     * runtime permission.
342     *
343     * @param name The permission name.
344     * @return The permission state or null if no such.
345     */
346    public int getPermissionFlags(String name, int userId) {
347        PermissionState installPermState = getInstallPermissionState(name);
348        if (installPermState != null) {
349            return installPermState.getFlags();
350        }
351        PermissionState runtimePermState = getRuntimePermissionState(name, userId);
352        if (runtimePermState != null) {
353            return runtimePermState.getFlags();
354        }
355        return 0;
356    }
357
358    /**
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.
365     */
366    public boolean updatePermissionFlags(BasePermission permission, int userId,
367            int flagMask, int flagValues) {
368        enforceValidUserId(userId);
369
370        final boolean mayChangeFlags = flagValues != 0 || flagMask != 0;
371
372        if (mPermissions == null) {
373            if (!mayChangeFlags) {
374                return false;
375            }
376            ensurePermissionData(permission);
377        }
378
379        PermissionData permissionData = mPermissions.get(permission.name);
380        if (permissionData == null) {
381            if (!mayChangeFlags) {
382                return false;
383            }
384            permissionData = ensurePermissionData(permission);
385        }
386
387        final int oldFlags = permissionData.getFlags(userId);
388
389        final boolean updated = permissionData.updateFlags(userId, flagMask, flagValues);
390        if (updated) {
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();
396                }
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;
404                    }
405                }
406            }
407        }
408        return updated;
409    }
410
411    public boolean updatePermissionFlagsForAllPermissions(
412            int userId, int flagMask, int flagValues) {
413        enforceValidUserId(userId);
414
415        if (mPermissions == null) {
416            return false;
417        }
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);
423        }
424        return changed;
425    }
426
427    /**
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.
431     *
432     * @param userId The device user id.
433     * @return The gids for the device user.
434     */
435    public int[] computeGids(int userId) {
436        enforceValidUserId(userId);
437
438        int[] gids = mGlobalGids;
439
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)) {
445                    continue;
446                }
447                PermissionData permissionData = mPermissions.valueAt(i);
448                final int[] permGids = permissionData.computeGids(userId);
449                if (permGids != NO_GIDS) {
450                    gids = appendInts(gids, permGids);
451                }
452            }
453        }
454
455        return gids;
456    }
457
458    /**
459     * Compute the Linux gids for all device users from the permissions
460     * granted to these users.
461     *
462     * @return The gids for all device users.
463     */
464    public int[] computeGids(int[] userIds) {
465        int[] gids = mGlobalGids;
466
467        for (int userId : userIds) {
468            final int[] userGids = computeGids(userId);
469            gids = appendInts(gids, userGids);
470        }
471
472        return gids;
473    }
474
475    /**
476     * Resets the internal state of this object.
477     */
478    public void reset() {
479        mGlobalGids = NO_GIDS;
480        mPermissions = null;
481        mPermissionReviewRequired = null;
482    }
483
484    private PermissionState getPermissionState(String name, int userId) {
485        if (mPermissions == null) {
486            return null;
487        }
488        PermissionData permissionData = mPermissions.get(name);
489        if (permissionData == null) {
490            return null;
491        }
492        return permissionData.getPermissionState(userId);
493    }
494
495    private List<PermissionState> getPermissionStatesInternal(int userId) {
496        enforceValidUserId(userId);
497
498        if (mPermissions == null) {
499            return Collections.emptyList();
500        }
501
502        List<PermissionState> permissionStates = new ArrayList<>();
503
504        final int permissionCount = mPermissions.size();
505        for (int i = 0; i < permissionCount; i++) {
506            PermissionData permissionData = mPermissions.valueAt(i);
507
508            PermissionState permissionState = permissionData.getPermissionState(userId);
509            if (permissionState != null) {
510                permissionStates.add(permissionState);
511            }
512        }
513
514        return permissionStates;
515    }
516
517    private int grantPermission(BasePermission permission, int userId) {
518        if (hasPermission(permission.name, userId)) {
519            return PERMISSION_OPERATION_FAILURE;
520        }
521
522        final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
523        final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
524
525        PermissionData permissionData = ensurePermissionData(permission);
526
527        if (!permissionData.grant(userId)) {
528            return PERMISSION_OPERATION_FAILURE;
529        }
530
531        if (hasGids) {
532            final int[] newGids = computeGids(userId);
533            if (oldGids.length != newGids.length) {
534                return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
535            }
536        }
537
538        return PERMISSION_OPERATION_SUCCESS;
539    }
540
541    private int revokePermission(BasePermission permission, int userId) {
542        if (!hasPermission(permission.name, userId)) {
543            return PERMISSION_OPERATION_FAILURE;
544        }
545
546        final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
547        final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
548
549        PermissionData permissionData = mPermissions.get(permission.name);
550
551        if (!permissionData.revoke(userId)) {
552            return PERMISSION_OPERATION_FAILURE;
553        }
554
555        if (permissionData.isDefault()) {
556            ensureNoPermissionData(permission.name);
557        }
558
559        if (hasGids) {
560            final int[] newGids = computeGids(userId);
561            if (oldGids.length != newGids.length) {
562                return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
563            }
564        }
565
566        return PERMISSION_OPERATION_SUCCESS;
567    }
568
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);
573            }
574        }
575        return current;
576    }
577
578    private static void enforceValidUserId(int userId) {
579        if (userId != UserHandle.USER_ALL && userId < 0) {
580            throw new IllegalArgumentException("Invalid userId:" + userId);
581        }
582    }
583
584    private PermissionData ensurePermissionData(BasePermission permission) {
585        if (mPermissions == null) {
586            mPermissions = new ArrayMap<>();
587        }
588        PermissionData permissionData = mPermissions.get(permission.name);
589        if (permissionData == null) {
590            permissionData = new PermissionData(permission);
591            mPermissions.put(permission.name, permissionData);
592        }
593        return permissionData;
594    }
595
596    private void ensureNoPermissionData(String name) {
597        if (mPermissions == null) {
598            return;
599        }
600        mPermissions.remove(name);
601        if (mPermissions.isEmpty()) {
602            mPermissions = null;
603        }
604    }
605
606    private static final class PermissionData {
607        private final BasePermission mPerm;
608        private SparseArray<PermissionState> mUserStates = new SparseArray<>();
609
610        public PermissionData(BasePermission perm) {
611            mPerm = perm;
612        }
613
614        public PermissionData(PermissionData other) {
615            this(other.mPerm);
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));
621            }
622        }
623
624        public int[] computeGids(int userId) {
625            return mPerm.computeGids(userId);
626        }
627
628        public boolean isGranted(int userId) {
629            if (isInstallPermission()) {
630                userId = UserHandle.USER_ALL;
631            }
632
633            PermissionState userState = mUserStates.get(userId);
634            if (userState == null) {
635                return false;
636            }
637
638            return userState.mGranted;
639        }
640
641        public boolean grant(int userId) {
642            if (!isCompatibleUserId(userId)) {
643                return false;
644            }
645
646            if (isGranted(userId)) {
647                return false;
648            }
649
650            PermissionState userState = mUserStates.get(userId);
651            if (userState == null) {
652                userState = new PermissionState(mPerm.name);
653                mUserStates.put(userId, userState);
654            }
655
656            userState.mGranted = true;
657
658            return true;
659        }
660
661        public boolean revoke(int userId) {
662            if (!isCompatibleUserId(userId)) {
663                return false;
664            }
665
666            if (!isGranted(userId)) {
667                return false;
668            }
669
670            PermissionState userState = mUserStates.get(userId);
671            userState.mGranted = false;
672
673            if (userState.isDefault()) {
674                mUserStates.remove(userId);
675            }
676
677            return true;
678        }
679
680        public PermissionState getPermissionState(int userId) {
681            return mUserStates.get(userId);
682        }
683
684        public int getFlags(int userId) {
685            PermissionState userState = mUserStates.get(userId);
686            if (userState != null) {
687                return userState.mFlags;
688            }
689            return 0;
690        }
691
692        public boolean isDefault() {
693            return mUserStates.size() <= 0;
694        }
695
696        public static boolean isInstallPermissionKey(int userId) {
697            return userId == UserHandle.USER_ALL;
698        }
699
700        public boolean updateFlags(int userId, int flagMask, int flagValues) {
701            if (isInstallPermission()) {
702                userId = UserHandle.USER_ALL;
703            }
704
705            if (!isCompatibleUserId(userId)) {
706                return false;
707            }
708
709            final int newFlags = flagValues & flagMask;
710
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);
717                }
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);
723                return true;
724            }
725
726            return false;
727        }
728
729        private boolean isCompatibleUserId(int userId) {
730            return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId));
731        }
732
733        private boolean isInstallPermission() {
734            return mUserStates.size() == 1
735                    && mUserStates.get(UserHandle.USER_ALL) != null;
736        }
737    }
738
739    public static final class PermissionState {
740        private final String mName;
741        private boolean mGranted;
742        private int mFlags;
743
744        public PermissionState(String name) {
745            mName = name;
746        }
747
748        public PermissionState(PermissionState other) {
749            mName = other.mName;
750            mGranted = other.mGranted;
751            mFlags = other.mFlags;
752        }
753
754        public boolean isDefault() {
755            return !mGranted && mFlags == 0;
756        }
757
758        public String getName() {
759            return mName;
760        }
761
762        public boolean isGranted() {
763            return mGranted;
764        }
765
766        public int getFlags() {
767            return mFlags;
768        }
769    }
770}
771