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.util.ArrayMap;
23 import android.util.SparseArray;
24 import android.util.SparseBooleanArray;
25 
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Objects;
29 
30 /**
31  * Legacy permission state that was associated with packages or shared users.
32  */
33 //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
34 public final class LegacyPermissionState {
35     // Maps from user IDs to user states.
36     @NonNull
37     private final SparseArray<UserState> mUserStates = new SparseArray<>();
38 
39     // Keyed by user IDs.
40     @NonNull
41     private final SparseBooleanArray mMissing = new SparseBooleanArray();
42 
43     /**
44      * Copy from another permission state.
45      *
46      * @param other the other permission state.
47      *
48      * @hide
49      */
copyFrom(@onNull LegacyPermissionState other)50     public void copyFrom(@NonNull LegacyPermissionState other) {
51         if (other == this) {
52             return;
53         }
54 
55         mUserStates.clear();
56         final int userStatesSize = other.mUserStates.size();
57         for (int i = 0; i < userStatesSize; i++) {
58             mUserStates.put(other.mUserStates.keyAt(i),
59                     new UserState(other.mUserStates.valueAt(i)));
60         }
61 
62         mMissing.clear();
63         final int missingSize = other.mMissing.size();
64         for (int i = 0; i < missingSize; i++) {
65             mMissing.put(other.mMissing.keyAt(i), other.mMissing.valueAt(i));
66         }
67     }
68 
69     /**
70      * Reset this permission state.
71      *
72      * @hide
73      */
reset()74     public void reset() {
75         mUserStates.clear();
76         mMissing.clear();
77     }
78 
79     @Override
equals(@ullable Object object)80     public boolean equals(@Nullable Object object) {
81         if (this == object) {
82             return true;
83         }
84         if (object == null) {
85             return false;
86         }
87         if (getClass() != object.getClass()) {
88             return false;
89         }
90         final LegacyPermissionState other = (LegacyPermissionState) object;
91         // Hand-code equals() for mUserStates, since SparseArray only has the
92         // default equals() method.
93         final int userStatesSize = mUserStates.size();
94         if (userStatesSize != other.mUserStates.size()) {
95             return false;
96         }
97         for (int i = 0; i < userStatesSize; i++) {
98             final int userId = mUserStates.keyAt(i);
99             if (!Objects.equals(mUserStates.get(userId), other.mUserStates.get(userId))) {
100                 return false;
101             }
102         }
103         return Objects.equals(mMissing, other.mMissing);
104     }
105 
106     /**
107      * Get the permission state for a permission and a user.
108      *
109      * @param permissionName the permission name
110      * @param userId the user ID
111      * @return the permission state
112      *
113      * @hide
114      */
115     @Nullable
getPermissionState(@onNull String permissionName, @UserIdInt int userId)116     public PermissionState getPermissionState(@NonNull String permissionName,
117             @UserIdInt int userId) {
118         checkUserId(userId);
119         UserState userState = mUserStates.get(userId);
120         if (userState == null) {
121             return null;
122         }
123         return userState.getPermissionState(permissionName);
124     }
125 
126     /**
127      * Put a permission state for a user.
128      *
129      * @param permissionState the permission state
130      * @param userId the user ID
131      */
putPermissionState(@onNull PermissionState permissionState, @UserIdInt int userId)132     public void putPermissionState(@NonNull PermissionState permissionState,
133             @UserIdInt int userId) {
134         checkUserId(userId);
135         UserState userState = mUserStates.get(userId);
136         if (userState == null) {
137             userState = new UserState();
138             mUserStates.put(userId, userState);
139         }
140         userState.putPermissionState(permissionState);
141     }
142 
143     /**
144      * Check whether there are any permission states for the given permissions.
145      *
146      * @param permissionNames the permission names
147      * @return whether there are any permission states
148      *
149      * @hide
150      */
hasPermissionState(@onNull Collection<String> permissionNames)151     public boolean hasPermissionState(@NonNull Collection<String> permissionNames) {
152         final int userStatesSize = mUserStates.size();
153         for (int i = 0; i < userStatesSize; i++) {
154             final UserState userState = mUserStates.valueAt(i);
155             for (final String permissionName : permissionNames) {
156                 if (userState.getPermissionState(permissionName) != null) {
157                     return true;
158                 }
159             }
160         }
161         return false;
162     }
163 
164     /**
165      * Get all the permission states for a user.
166      *
167      * @param userId the user ID
168      * @return the permission states
169      */
170     @NonNull
getPermissionStates(@serIdInt int userId)171     public Collection<PermissionState> getPermissionStates(@UserIdInt int userId) {
172         checkUserId(userId);
173         final UserState userState = mUserStates.get(userId);
174         if (userState == null) {
175             return Collections.emptyList();
176         }
177         return userState.getPermissionStates();
178     }
179 
180     /**
181      * Check whether the permission state is missing for a user.
182      * <p>
183      * This can happen if permission state is rolled back and we'll need to generate a reasonable
184      * default state to keep the app usable.
185      *
186      * @param userId the user ID
187      * @return whether the permission state is missing
188      */
isMissing(@serIdInt int userId)189     public boolean isMissing(@UserIdInt int userId) {
190         checkUserId(userId);
191         return mMissing.get(userId);
192     }
193 
194     /**
195      * Set whether the permission state is missing for a user.
196      * <p>
197      * This can happen if permission state is rolled back and we'll need to generate a reasonable
198      * default state to keep the app usable.
199      *
200      * @param missing whether the permission state is missing
201      * @param userId the user ID
202      */
setMissing(boolean missing, @UserIdInt int userId)203     public void setMissing(boolean missing, @UserIdInt int userId) {
204         checkUserId(userId);
205         if (missing) {
206             mMissing.put(userId, true);
207         } else {
208             mMissing.delete(userId);
209         }
210     }
211 
checkUserId(@serIdInt int userId)212     private static void checkUserId(@UserIdInt int userId) {
213         if (userId < 0) {
214             throw new IllegalArgumentException("Invalid user ID " + userId);
215         }
216     }
217 
218     /**
219      * Legacy state for permissions for a user.
220      */
221     private static final class UserState {
222         // Maps from permission names to permission states.
223         @NonNull
224         private final ArrayMap<String, PermissionState> mPermissionStates = new ArrayMap<>();
225 
UserState()226         public UserState() {}
227 
UserState(@onNull UserState other)228         public UserState(@NonNull UserState other) {
229             final int permissionStatesSize = other.mPermissionStates.size();
230             for (int i = 0; i < permissionStatesSize; i++) {
231                 mPermissionStates.put(other.mPermissionStates.keyAt(i),
232                         new PermissionState(other.mPermissionStates.valueAt(i)));
233             }
234         }
235 
236         @Nullable
getPermissionState(@onNull String permissionName)237         public PermissionState getPermissionState(@NonNull String permissionName) {
238             return mPermissionStates.get(permissionName);
239         }
240 
putPermissionState(@onNull PermissionState permissionState)241         public void putPermissionState(@NonNull PermissionState permissionState) {
242             mPermissionStates.put(permissionState.getName(), permissionState);
243         }
244 
245         @NonNull
getPermissionStates()246         public Collection<PermissionState> getPermissionStates() {
247             return Collections.unmodifiableCollection(mPermissionStates.values());
248         }
249     }
250 
251     /**
252      * Legacy state for a single permission.
253      */
254     public static final class PermissionState {
255         @NonNull
256         private final String mName;
257 
258         private final boolean mRuntime;
259 
260         private final boolean mGranted;
261 
262         private final int mFlags;
263 
264         /**
265          * Create a new instance of this class.
266          *
267          * @param name the name of the permission
268          * @param runtime whether the permission is runtime
269          * @param granted whether the permission is granted
270          * @param flags the permission flags
271          */
PermissionState(@onNull String name, boolean runtime, boolean granted, int flags)272         public PermissionState(@NonNull String name, boolean runtime, boolean granted, int flags) {
273             mName = name;
274             mRuntime = runtime;
275             mGranted = granted;
276             mFlags = flags;
277         }
278 
PermissionState(@onNull PermissionState other)279         private PermissionState(@NonNull PermissionState other) {
280             mName = other.mName;
281             mRuntime = other.mRuntime;
282             mGranted = other.mGranted;
283             mFlags = other.mFlags;
284         }
285 
286         /**
287          * Get the permission name.
288          *
289          * @return the permission name
290          */
291         @NonNull
getName()292         public String getName() {
293             return mName;
294         }
295 
296         /**
297          * Get whether the permission is a runtime permission.
298          *
299          * @return whether the permission is a runtime permission.
300          */
isRuntime()301         public boolean isRuntime() {
302             return mRuntime;
303         }
304 
305         /**
306          * Get whether the permission is granted.
307          *
308          * @return whether the permission is granted
309          */
310         @NonNull
isGranted()311         public boolean isGranted() {
312             return mGranted;
313         }
314 
315         /**
316          * Get the permission flags.
317          *
318          * @return the permission flags
319          */
320         @NonNull
getFlags()321         public int getFlags() {
322             return mFlags;
323         }
324 
325         @Override
equals(@ullable Object object)326         public boolean equals(@Nullable Object object) {
327             if (this == object) {
328                 return true;
329             }
330             if (object == null || getClass() != object.getClass()) {
331                 return false;
332             }
333             PermissionState that = (PermissionState) object;
334             return mRuntime == that.mRuntime
335                     && mGranted == that.mGranted
336                     && mFlags == that.mFlags
337                     && Objects.equals(mName, that.mName);
338         }
339 
340         @Override
hashCode()341         public int hashCode() {
342             return Objects.hash(mName, mRuntime, mGranted, mFlags);
343         }
344     }
345 }
346