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    @Override
144    public boolean equals(Object obj) {
145        if (this == obj) {
146            return true;
147        }
148        if (obj == null) {
149            return false;
150        }
151        if (getClass() != obj.getClass()) {
152            return false;
153        }
154        final PermissionsState other = (PermissionsState) obj;
155
156        if (mPermissions == null) {
157            if (other.mPermissions != null) {
158                return false;
159            }
160        } else if (!mPermissions.equals(other.mPermissions)) {
161            return false;
162        }
163        if (mPermissionReviewRequired == null) {
164            if (other.mPermissionReviewRequired != null) {
165                return false;
166            }
167        } else if (!mPermissionReviewRequired.equals(other.mPermissionReviewRequired)) {
168            return false;
169        }
170        return Arrays.equals(mGlobalGids, other.mGlobalGids);
171    }
172
173    public boolean isPermissionReviewRequired(int userId) {
174        return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId);
175    }
176
177    /**
178     * Grant an install permission.
179     *
180     * @param permission The permission to grant.
181     * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
182     *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
183     *     #PERMISSION_OPERATION_FAILURE}.
184     */
185    public int grantInstallPermission(BasePermission permission) {
186        return grantPermission(permission, UserHandle.USER_ALL);
187    }
188
189    /**
190     * Revoke an install permission.
191     *
192     * @param permission The permission to revoke.
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 revokeInstallPermission(BasePermission permission) {
198        return revokePermission(permission, UserHandle.USER_ALL);
199    }
200
201    /**
202     * Grant a runtime permission for a given device user.
203     *
204     * @param permission The permission to grant.
205     * @param userId The device user id.
206     * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
207     *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
208     *     #PERMISSION_OPERATION_FAILURE}.
209     */
210    public int grantRuntimePermission(BasePermission permission, int userId) {
211        enforceValidUserId(userId);
212        if (userId == UserHandle.USER_ALL) {
213            return PERMISSION_OPERATION_FAILURE;
214        }
215        return grantPermission(permission, userId);
216    }
217
218    /**
219     *  Revoke a runtime permission for a given device user.
220     *
221     * @param permission The permission to revoke.
222     * @param userId The device user id.
223     * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
224     *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
225     *     #PERMISSION_OPERATION_FAILURE}.
226     */
227    public int revokeRuntimePermission(BasePermission permission, int userId) {
228        enforceValidUserId(userId);
229        if (userId == UserHandle.USER_ALL) {
230            return PERMISSION_OPERATION_FAILURE;
231        }
232        return revokePermission(permission, userId);
233    }
234
235    /**
236     * Gets whether this state has a given runtime permission for a
237     * given device user id.
238     *
239     * @param name The permission name.
240     * @param userId The device user id.
241     * @return Whether this state has the permission.
242     */
243    public boolean hasRuntimePermission(String name, int userId) {
244        enforceValidUserId(userId);
245        return !hasInstallPermission(name) && hasPermission(name, userId);
246    }
247
248    /**
249     * Gets whether this state has a given install permission.
250     *
251     * @param name The permission name.
252     * @return Whether this state has the permission.
253     */
254    public boolean hasInstallPermission(String name) {
255        return hasPermission(name, UserHandle.USER_ALL);
256    }
257
258    /**
259     * Gets whether the state has a given permission for the specified
260     * user, regardless if this is an install or a runtime permission.
261     *
262     * @param name The permission name.
263     * @param userId The device user id.
264     * @return Whether the user has the permission.
265     */
266    public boolean hasPermission(String name, int userId) {
267        enforceValidUserId(userId);
268
269        if (mPermissions == null) {
270            return false;
271        }
272
273        PermissionData permissionData = mPermissions.get(name);
274        return permissionData != null && permissionData.isGranted(userId);
275    }
276
277    /**
278     * Returns whether the state has any known request for the given permission name,
279     * whether or not it has been granted.
280     */
281    public boolean hasRequestedPermission(ArraySet<String> names) {
282        if (mPermissions == null) {
283            return false;
284        }
285        for (int i=names.size()-1; i>=0; i--) {
286            if (mPermissions.get(names.valueAt(i)) != null) {
287                return true;
288            }
289        }
290        return false;
291    }
292
293    /**
294     * Gets all permissions for a given device user id regardless if they
295     * are install time or runtime permissions.
296     *
297     * @param userId The device user id.
298     * @return The permissions or an empty set.
299     */
300    public Set<String> getPermissions(int userId) {
301        enforceValidUserId(userId);
302
303        if (mPermissions == null) {
304            return Collections.emptySet();
305        }
306
307        Set<String> permissions = new ArraySet<>(mPermissions.size());
308
309        final int permissionCount = mPermissions.size();
310        for (int i = 0; i < permissionCount; i++) {
311            String permission = mPermissions.keyAt(i);
312
313            if (hasInstallPermission(permission)) {
314                permissions.add(permission);
315                continue;
316            }
317
318            if (userId != UserHandle.USER_ALL) {
319                if (hasRuntimePermission(permission, userId)) {
320                    permissions.add(permission);
321                }
322            }
323        }
324
325        return permissions;
326    }
327
328    /**
329     * Gets the state for an install permission or null if no such.
330     *
331     * @param name The permission name.
332     * @return The permission state.
333     */
334    public PermissionState getInstallPermissionState(String name) {
335        return getPermissionState(name, UserHandle.USER_ALL);
336    }
337
338    /**
339     * Gets the state for a runtime permission or null if no such.
340     *
341     * @param name The permission name.
342     * @param userId The device user id.
343     * @return The permission state.
344     */
345    public PermissionState getRuntimePermissionState(String name, int userId) {
346        enforceValidUserId(userId);
347        return getPermissionState(name, userId);
348    }
349
350    /**
351     * Gets all install permission states.
352     *
353     * @return The permission states or an empty set.
354     */
355    public List<PermissionState> getInstallPermissionStates() {
356        return getPermissionStatesInternal(UserHandle.USER_ALL);
357    }
358
359    /**
360     * Gets all runtime permission states.
361     *
362     * @return The permission states or an empty set.
363     */
364    public List<PermissionState> getRuntimePermissionStates(int userId) {
365        enforceValidUserId(userId);
366        return getPermissionStatesInternal(userId);
367    }
368
369    /**
370     * Gets the flags for a permission regardless if it is install or
371     * runtime permission.
372     *
373     * @param name The permission name.
374     * @return The permission state or null if no such.
375     */
376    public int getPermissionFlags(String name, int userId) {
377        PermissionState installPermState = getInstallPermissionState(name);
378        if (installPermState != null) {
379            return installPermState.getFlags();
380        }
381        PermissionState runtimePermState = getRuntimePermissionState(name, userId);
382        if (runtimePermState != null) {
383            return runtimePermState.getFlags();
384        }
385        return 0;
386    }
387
388    /**
389     * Update the flags associated with a given permission.
390     * @param permission The permission whose flags to update.
391     * @param userId The user for which to update.
392     * @param flagMask Mask for which flags to change.
393     * @param flagValues New values for the mask flags.
394     * @return Whether the permission flags changed.
395     */
396    public boolean updatePermissionFlags(BasePermission permission, int userId,
397            int flagMask, int flagValues) {
398        enforceValidUserId(userId);
399
400        final boolean mayChangeFlags = flagValues != 0 || flagMask != 0;
401
402        if (mPermissions == null) {
403            if (!mayChangeFlags) {
404                return false;
405            }
406            ensurePermissionData(permission);
407        }
408
409        PermissionData permissionData = mPermissions.get(permission.name);
410        if (permissionData == null) {
411            if (!mayChangeFlags) {
412                return false;
413            }
414            permissionData = ensurePermissionData(permission);
415        }
416
417        final int oldFlags = permissionData.getFlags(userId);
418
419        final boolean updated = permissionData.updateFlags(userId, flagMask, flagValues);
420        if (updated) {
421            final int newFlags = permissionData.getFlags(userId);
422            if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
423                    && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
424                if (mPermissionReviewRequired == null) {
425                    mPermissionReviewRequired = new SparseBooleanArray();
426                }
427                mPermissionReviewRequired.put(userId, true);
428            } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
429                    && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
430                if (mPermissionReviewRequired != null && !hasPermissionRequiringReview(userId)) {
431                    mPermissionReviewRequired.delete(userId);
432                    if (mPermissionReviewRequired.size() <= 0) {
433                        mPermissionReviewRequired = null;
434                    }
435                }
436            }
437        }
438        return updated;
439    }
440
441    private boolean hasPermissionRequiringReview(int userId) {
442        final int permissionCount = mPermissions.size();
443        for (int i = 0; i < permissionCount; i++) {
444            final PermissionData permission = mPermissions.valueAt(i);
445            if ((permission.getFlags(userId)
446                    & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
447                return true;
448            }
449        }
450        return false;
451    }
452
453    public boolean updatePermissionFlagsForAllPermissions(
454            int userId, int flagMask, int flagValues) {
455        enforceValidUserId(userId);
456
457        if (mPermissions == null) {
458            return false;
459        }
460        boolean changed = false;
461        final int permissionCount = mPermissions.size();
462        for (int i = 0; i < permissionCount; i++) {
463            PermissionData permissionData = mPermissions.valueAt(i);
464            changed |= permissionData.updateFlags(userId, flagMask, flagValues);
465        }
466        return changed;
467    }
468
469    /**
470     * Compute the Linux gids for a given device user from the permissions
471     * granted to this user. Note that these are computed to avoid additional
472     * state as they are rarely accessed.
473     *
474     * @param userId The device user id.
475     * @return The gids for the device user.
476     */
477    public int[] computeGids(int userId) {
478        enforceValidUserId(userId);
479
480        int[] gids = mGlobalGids;
481
482        if (mPermissions != null) {
483            final int permissionCount = mPermissions.size();
484            for (int i = 0; i < permissionCount; i++) {
485                String permission = mPermissions.keyAt(i);
486                if (!hasPermission(permission, userId)) {
487                    continue;
488                }
489                PermissionData permissionData = mPermissions.valueAt(i);
490                final int[] permGids = permissionData.computeGids(userId);
491                if (permGids != NO_GIDS) {
492                    gids = appendInts(gids, permGids);
493                }
494            }
495        }
496
497        return gids;
498    }
499
500    /**
501     * Compute the Linux gids for all device users from the permissions
502     * granted to these users.
503     *
504     * @return The gids for all device users.
505     */
506    public int[] computeGids(int[] userIds) {
507        int[] gids = mGlobalGids;
508
509        for (int userId : userIds) {
510            final int[] userGids = computeGids(userId);
511            gids = appendInts(gids, userGids);
512        }
513
514        return gids;
515    }
516
517    /**
518     * Resets the internal state of this object.
519     */
520    public void reset() {
521        mGlobalGids = NO_GIDS;
522        mPermissions = null;
523        mPermissionReviewRequired = null;
524    }
525
526    private PermissionState getPermissionState(String name, int userId) {
527        if (mPermissions == null) {
528            return null;
529        }
530        PermissionData permissionData = mPermissions.get(name);
531        if (permissionData == null) {
532            return null;
533        }
534        return permissionData.getPermissionState(userId);
535    }
536
537    private List<PermissionState> getPermissionStatesInternal(int userId) {
538        enforceValidUserId(userId);
539
540        if (mPermissions == null) {
541            return Collections.emptyList();
542        }
543
544        List<PermissionState> permissionStates = new ArrayList<>();
545
546        final int permissionCount = mPermissions.size();
547        for (int i = 0; i < permissionCount; i++) {
548            PermissionData permissionData = mPermissions.valueAt(i);
549
550            PermissionState permissionState = permissionData.getPermissionState(userId);
551            if (permissionState != null) {
552                permissionStates.add(permissionState);
553            }
554        }
555
556        return permissionStates;
557    }
558
559    private int grantPermission(BasePermission permission, int userId) {
560        if (hasPermission(permission.name, userId)) {
561            return PERMISSION_OPERATION_FAILURE;
562        }
563
564        final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
565        final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
566
567        PermissionData permissionData = ensurePermissionData(permission);
568
569        if (!permissionData.grant(userId)) {
570            return PERMISSION_OPERATION_FAILURE;
571        }
572
573        if (hasGids) {
574            final int[] newGids = computeGids(userId);
575            if (oldGids.length != newGids.length) {
576                return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
577            }
578        }
579
580        return PERMISSION_OPERATION_SUCCESS;
581    }
582
583    private int revokePermission(BasePermission permission, int userId) {
584        if (!hasPermission(permission.name, userId)) {
585            return PERMISSION_OPERATION_FAILURE;
586        }
587
588        final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
589        final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
590
591        PermissionData permissionData = mPermissions.get(permission.name);
592
593        if (!permissionData.revoke(userId)) {
594            return PERMISSION_OPERATION_FAILURE;
595        }
596
597        if (permissionData.isDefault()) {
598            ensureNoPermissionData(permission.name);
599        }
600
601        if (hasGids) {
602            final int[] newGids = computeGids(userId);
603            if (oldGids.length != newGids.length) {
604                return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
605            }
606        }
607
608        return PERMISSION_OPERATION_SUCCESS;
609    }
610
611    // TODO: fix this to use arraycopy and append all ints in one go
612    private static int[] appendInts(int[] current, int[] added) {
613        if (current != null && added != null) {
614            for (int guid : added) {
615                current = ArrayUtils.appendInt(current, guid);
616            }
617        }
618        return current;
619    }
620
621    private static void enforceValidUserId(int userId) {
622        if (userId != UserHandle.USER_ALL && userId < 0) {
623            throw new IllegalArgumentException("Invalid userId:" + userId);
624        }
625    }
626
627    private PermissionData ensurePermissionData(BasePermission permission) {
628        if (mPermissions == null) {
629            mPermissions = new ArrayMap<>();
630        }
631        PermissionData permissionData = mPermissions.get(permission.name);
632        if (permissionData == null) {
633            permissionData = new PermissionData(permission);
634            mPermissions.put(permission.name, permissionData);
635        }
636        return permissionData;
637    }
638
639    private void ensureNoPermissionData(String name) {
640        if (mPermissions == null) {
641            return;
642        }
643        mPermissions.remove(name);
644        if (mPermissions.isEmpty()) {
645            mPermissions = null;
646        }
647    }
648
649    private static final class PermissionData {
650        private final BasePermission mPerm;
651        private SparseArray<PermissionState> mUserStates = new SparseArray<>();
652
653        public PermissionData(BasePermission perm) {
654            mPerm = perm;
655        }
656
657        public PermissionData(PermissionData other) {
658            this(other.mPerm);
659            final int otherStateCount = other.mUserStates.size();
660            for (int i = 0; i < otherStateCount; i++) {
661                final int otherUserId = other.mUserStates.keyAt(i);
662                PermissionState otherState = other.mUserStates.valueAt(i);
663                mUserStates.put(otherUserId, new PermissionState(otherState));
664            }
665        }
666
667        public int[] computeGids(int userId) {
668            return mPerm.computeGids(userId);
669        }
670
671        public boolean isGranted(int userId) {
672            if (isInstallPermission()) {
673                userId = UserHandle.USER_ALL;
674            }
675
676            PermissionState userState = mUserStates.get(userId);
677            if (userState == null) {
678                return false;
679            }
680
681            return userState.mGranted;
682        }
683
684        public boolean grant(int userId) {
685            if (!isCompatibleUserId(userId)) {
686                return false;
687            }
688
689            if (isGranted(userId)) {
690                return false;
691            }
692
693            PermissionState userState = mUserStates.get(userId);
694            if (userState == null) {
695                userState = new PermissionState(mPerm.name);
696                mUserStates.put(userId, userState);
697            }
698
699            userState.mGranted = true;
700
701            return true;
702        }
703
704        public boolean revoke(int userId) {
705            if (!isCompatibleUserId(userId)) {
706                return false;
707            }
708
709            if (!isGranted(userId)) {
710                return false;
711            }
712
713            PermissionState userState = mUserStates.get(userId);
714            userState.mGranted = false;
715
716            if (userState.isDefault()) {
717                mUserStates.remove(userId);
718            }
719
720            return true;
721        }
722
723        public PermissionState getPermissionState(int userId) {
724            return mUserStates.get(userId);
725        }
726
727        public int getFlags(int userId) {
728            PermissionState userState = mUserStates.get(userId);
729            if (userState != null) {
730                return userState.mFlags;
731            }
732            return 0;
733        }
734
735        public boolean isDefault() {
736            return mUserStates.size() <= 0;
737        }
738
739        public static boolean isInstallPermissionKey(int userId) {
740            return userId == UserHandle.USER_ALL;
741        }
742
743        public boolean updateFlags(int userId, int flagMask, int flagValues) {
744            if (isInstallPermission()) {
745                userId = UserHandle.USER_ALL;
746            }
747
748            if (!isCompatibleUserId(userId)) {
749                return false;
750            }
751
752            final int newFlags = flagValues & flagMask;
753
754            PermissionState userState = mUserStates.get(userId);
755            if (userState != null) {
756                final int oldFlags = userState.mFlags;
757                userState.mFlags = (userState.mFlags & ~flagMask) | newFlags;
758                if (userState.isDefault()) {
759                    mUserStates.remove(userId);
760                }
761                return userState.mFlags != oldFlags;
762            } else if (newFlags != 0) {
763                userState = new PermissionState(mPerm.name);
764                userState.mFlags = newFlags;
765                mUserStates.put(userId, userState);
766                return true;
767            }
768
769            return false;
770        }
771
772        private boolean isCompatibleUserId(int userId) {
773            return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId));
774        }
775
776        private boolean isInstallPermission() {
777            return mUserStates.size() == 1
778                    && mUserStates.get(UserHandle.USER_ALL) != null;
779        }
780    }
781
782    public static final class PermissionState {
783        private final String mName;
784        private boolean mGranted;
785        private int mFlags;
786
787        public PermissionState(String name) {
788            mName = name;
789        }
790
791        public PermissionState(PermissionState other) {
792            mName = other.mName;
793            mGranted = other.mGranted;
794            mFlags = other.mFlags;
795        }
796
797        public boolean isDefault() {
798            return !mGranted && mFlags == 0;
799        }
800
801        public String getName() {
802            return mName;
803        }
804
805        public boolean isGranted() {
806            return mGranted;
807        }
808
809        public int getFlags() {
810            return mFlags;
811        }
812    }
813}
814