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.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UserIdInt;
22 import android.content.pm.PackageManager;
23 import android.util.ArrayMap;
24 import android.util.ArraySet;
25 import android.util.IntArray;
26 
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Set;
31 
32 /**
33  * Permission state for a UID.
34  */
35 public final class UidPermissionState {
36     private boolean mMissing;
37 
38     @Nullable
39     private ArrayMap<String, PermissionState> mPermissions;
40 
UidPermissionState()41     public UidPermissionState() {}
42 
UidPermissionState(@onNull UidPermissionState other)43     public UidPermissionState(@NonNull UidPermissionState other) {
44         mMissing = other.mMissing;
45 
46         if (other.mPermissions != null) {
47             mPermissions = new ArrayMap<>();
48             final int permissionsSize = other.mPermissions.size();
49             for (int i = 0; i < permissionsSize; i++) {
50                 final String name = other.mPermissions.keyAt(i);
51                 final PermissionState permissionState = other.mPermissions.valueAt(i);
52                 mPermissions.put(name, new PermissionState(permissionState));
53             }
54         }
55     }
56 
57     /**
58      * Reset the internal state of this object.
59      */
reset()60     public void reset() {
61         mMissing = false;
62         mPermissions = null;
63         invalidateCache();
64     }
65 
66     /**
67      * Check whether the permissions state is missing for a user. This can happen if permission
68      * state is rolled back and we'll need to generate a reasonable default state to keep the app
69      * usable.
70      */
isMissing()71     public boolean isMissing() {
72         return mMissing;
73     }
74 
75     /**
76      * Set whether the permissions state is missing for a user. This can happen if permission state
77      * is rolled back and we'll need to generate a reasonable default state to keep the app usable.
78      */
setMissing(boolean missing)79     public void setMissing(boolean missing) {
80         mMissing = missing;
81     }
82 
83     /**
84      * Get whether there is a permission state for a permission.
85      *
86      * @deprecated This used to be named hasRequestedPermission() and its usage is confusing
87      */
88     @Deprecated
hasPermissionState(@onNull String name)89     public boolean hasPermissionState(@NonNull String name) {
90         return mPermissions != null && mPermissions.containsKey(name);
91     }
92 
93     /**
94      * Get whether there is a permission state for any of the permissions.
95      *
96      * @deprecated This used to be named hasRequestedPermission() and its usage is confusing
97      */
98     @Deprecated
hasPermissionState(@onNull ArraySet<String> names)99     public boolean hasPermissionState(@NonNull ArraySet<String> names) {
100         if (mPermissions == null) {
101             return false;
102         }
103         final int namesSize = names.size();
104         for (int i = 0; i < namesSize; i++) {
105             final String name = names.valueAt(i);
106             if (mPermissions.containsKey(name)) {
107                 return true;
108             }
109         }
110         return false;
111     }
112 
113     /**
114      * Gets the state for a permission or null if none.
115      *
116      * @param name the permission name
117      * @return the permission state
118      */
119     @Nullable
getPermissionState(@onNull String name)120     public PermissionState getPermissionState(@NonNull String name) {
121         if (mPermissions == null) {
122             return null;
123         }
124         return mPermissions.get(name);
125     }
126 
127     @NonNull
getOrCreatePermissionState(@onNull Permission permission)128     private PermissionState getOrCreatePermissionState(@NonNull Permission permission) {
129         if (mPermissions == null) {
130             mPermissions = new ArrayMap<>();
131         }
132         final String name = permission.getName();
133         PermissionState permissionState = mPermissions.get(name);
134         if (permissionState == null) {
135             permissionState = new PermissionState(permission);
136             mPermissions.put(name, permissionState);
137         }
138         return permissionState;
139     }
140 
141     /**
142      * Get all permission states.
143      *
144      * @return the permission states
145      */
146     @NonNull
getPermissionStates()147     public List<PermissionState> getPermissionStates() {
148         if (mPermissions == null) {
149             return Collections.emptyList();
150         }
151         return new ArrayList<>(mPermissions.values());
152     }
153 
154     /**
155      * Put a permission state.
156      *
157      * @param permission the permission
158      * @param granted whether the permission is granted
159      * @param flags the permission flags
160      */
putPermissionState(@onNull Permission permission, boolean granted, int flags)161     public void putPermissionState(@NonNull Permission permission, boolean granted, int flags) {
162         final String name = permission.getName();
163         if (mPermissions == null) {
164             mPermissions = new ArrayMap<>();
165         } else {
166             mPermissions.remove(name);
167         }
168         final PermissionState permissionState = new PermissionState(permission);
169         if (granted) {
170             permissionState.grant();
171         }
172         permissionState.updateFlags(flags, flags);
173         mPermissions.put(name, permissionState);
174     }
175 
176     /**
177      * Remove a permission state.
178      *
179      * @param name the permission name
180      * @return whether the permission state changed
181      */
removePermissionState(@onNull String name)182     public boolean removePermissionState(@NonNull String name) {
183         if (mPermissions == null) {
184             return false;
185         }
186         final boolean changed = mPermissions.remove(name) != null;
187         if (changed && mPermissions.isEmpty()) {
188             mPermissions = null;
189         }
190         return changed;
191     }
192 
193     /**
194      * Get whether a permission is granted.
195      *
196      * @param name the permission name
197      * @return whether the permission is granted
198      */
isPermissionGranted(@onNull String name)199     public boolean isPermissionGranted(@NonNull String name) {
200         final PermissionState permissionState = getPermissionState(name);
201         return permissionState != null && permissionState.isGranted();
202     }
203 
204     /**
205      * Get all the granted permissions.
206      *
207      * @return the granted permissions
208      */
209     @NonNull
getGrantedPermissions()210     public Set<String> getGrantedPermissions() {
211         if (mPermissions == null) {
212             return Collections.emptySet();
213         }
214 
215         final Set<String> permissions = new ArraySet<>(mPermissions.size());
216         final int permissionsSize = mPermissions.size();
217         for (int i = 0; i < permissionsSize; i++) {
218             final PermissionState permissionState = mPermissions.valueAt(i);
219 
220             if (permissionState.isGranted()) {
221                 permissions.add(permissionState.getName());
222             }
223         }
224         return permissions;
225     }
226 
227     /**
228      * Grant a permission.
229      *
230      * @param permission the permission to grant
231      * @return whether the permission grant state changed
232      */
grantPermission(@onNull Permission permission)233     public boolean grantPermission(@NonNull Permission permission) {
234         final PermissionState permissionState = getOrCreatePermissionState(permission);
235         return permissionState.grant();
236     }
237 
238     /**
239      * Revoke a permission.
240      *
241      * @param permission the permission to revoke
242      * @return whether the permission grant state changed
243      */
revokePermission(@onNull Permission permission)244     public boolean revokePermission(@NonNull Permission permission) {
245         final String name = permission.getName();
246         final PermissionState permissionState = getPermissionState(name);
247         if (permissionState == null) {
248             return false;
249         }
250         final boolean changed = permissionState.revoke();
251         if (changed && permissionState.isDefault()) {
252             removePermissionState(name);
253         }
254         return changed;
255     }
256 
257     /**
258      * Get the flags for a permission.
259      *
260      * @param name the permission name
261      * @return the permission flags
262      */
getPermissionFlags(@onNull String name)263     public int getPermissionFlags(@NonNull String name) {
264         final PermissionState permissionState = getPermissionState(name);
265         if (permissionState == null) {
266             return 0;
267         }
268         return permissionState.getFlags();
269     }
270 
271     /**
272      * Update the flags for a permission.
273      *
274      * @param permission the permission name
275      * @param flagMask the mask for the flags
276      * @param flagValues the new values for the masked flags
277      * @return whether the permission flags changed
278      */
updatePermissionFlags(@onNull Permission permission, int flagMask, int flagValues)279     public boolean updatePermissionFlags(@NonNull Permission permission, int flagMask,
280             int flagValues) {
281         if (flagMask == 0) {
282             return false;
283         }
284         final PermissionState permissionState = getOrCreatePermissionState(permission);
285         final boolean changed = permissionState.updateFlags(flagMask, flagValues);
286         if (changed && permissionState.isDefault()) {
287             removePermissionState(permission.getName());
288         }
289         return changed;
290     }
291 
updatePermissionFlagsForAllPermissions(int flagMask, int flagValues)292     public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
293         if (flagMask == 0) {
294             return false;
295         }
296         if (mPermissions == null) {
297             return false;
298         }
299         boolean anyChanged = false;
300         for (int i = mPermissions.size() - 1; i >= 0; i--) {
301             final PermissionState permissionState = mPermissions.valueAt(i);
302             final boolean changed = permissionState.updateFlags(flagMask, flagValues);
303             if (changed && permissionState.isDefault()) {
304                 mPermissions.removeAt(i);
305             }
306             anyChanged |= changed;
307         }
308         return anyChanged;
309     }
310 
isPermissionsReviewRequired()311     public boolean isPermissionsReviewRequired() {
312         if (mPermissions == null) {
313             return false;
314         }
315         final int permissionsSize = mPermissions.size();
316         for (int i = 0; i < permissionsSize; i++) {
317             final PermissionState permission = mPermissions.valueAt(i);
318             if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
319                 return true;
320             }
321         }
322         return false;
323     }
324 
325     /**
326      * Compute the Linux GIDs from the permissions granted to a user.
327      *
328      * @param userId the user ID
329      * @return the GIDs for the user
330      */
331     @NonNull
computeGids(@onNull int[] globalGids, @UserIdInt int userId)332     public int[] computeGids(@NonNull int[] globalGids, @UserIdInt int userId) {
333         IntArray gids = IntArray.wrap(globalGids);
334         if (mPermissions == null) {
335             return gids.toArray();
336         }
337         final int permissionsSize = mPermissions.size();
338         for (int i = 0; i < permissionsSize; i++) {
339             PermissionState permissionState = mPermissions.valueAt(i);
340             if (!permissionState.isGranted()) {
341                 continue;
342             }
343             final int[] permissionGids = permissionState.computeGids(userId);
344             if (permissionGids.length != 0) {
345                 gids.addAll(permissionGids);
346             }
347         }
348         return gids.toArray();
349     }
350 
invalidateCache()351     static void invalidateCache() {
352         PackageManager.invalidatePackageInfoCache();
353     }
354 }
355