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.permissioncontroller.permission.model;
18 
19 import android.content.pm.PackageManager;
20 import android.content.pm.PermissionInfo;
21 
22 import androidx.annotation.NonNull;
23 
24 import java.util.ArrayList;
25 import java.util.Objects;
26 
27 /**
28  * A permission and it's properties.
29  *
30  * @see AppPermissionGroup
31  */
32 public final class Permission {
33     private final @NonNull PermissionInfo mPermissionInfo;
34     private final String mName;
35     private final String mBackgroundPermissionName;
36     private final String mAppOp;
37 
38     private boolean mGranted;
39     private boolean mAppOpAllowed;
40     private int mFlags;
41     private boolean mIsEphemeral;
42     private boolean mIsRuntimeOnly;
43     private Permission mBackgroundPermission;
44     private ArrayList<Permission> mForegroundPermissions;
45     private boolean mWhitelisted;
46 
Permission(String name, @NonNull PermissionInfo permissionInfo, boolean granted, String appOp, boolean appOpAllowed, int flags)47     public Permission(String name, @NonNull PermissionInfo permissionInfo, boolean granted,
48             String appOp, boolean appOpAllowed, int flags) {
49         mPermissionInfo = permissionInfo;
50         mName = name;
51         mBackgroundPermissionName = permissionInfo.backgroundPermission;
52         mGranted = granted;
53         mAppOp = appOp;
54         mAppOpAllowed = appOpAllowed;
55         mFlags = flags;
56         mIsEphemeral =
57                 (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
58         mIsRuntimeOnly =
59                 (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
60     }
61 
62     /**
63      * Mark this permission as background permission for {@code foregroundPermissions}.
64      *
65      * @param foregroundPermission The foreground permission
66      */
addForegroundPermissions(Permission foregroundPermission)67     public void addForegroundPermissions(Permission foregroundPermission) {
68         if (mForegroundPermissions == null) {
69             mForegroundPermissions = new ArrayList<>(1);
70         }
71         mForegroundPermissions.add(foregroundPermission);
72     }
73 
74     /**
75      * Mark this permission as foreground permission for {@code backgroundPermission}.
76      *
77      * @param backgroundPermission The background permission
78      */
setBackgroundPermission(Permission backgroundPermission)79     public void setBackgroundPermission(Permission backgroundPermission) {
80         mBackgroundPermission = backgroundPermission;
81     }
82 
getPermissionInfo()83     public PermissionInfo getPermissionInfo() {
84         return mPermissionInfo;
85     }
86 
getName()87     public String getName() {
88         return mName;
89     }
90 
getAppOp()91     public String getAppOp() {
92         return mAppOp;
93     }
94 
getFlags()95     public int getFlags() {
96         return mFlags;
97     }
98 
isHardRestricted()99     boolean isHardRestricted() {
100         return (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
101     }
102 
isSoftRestricted()103     boolean isSoftRestricted() {
104         return (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
105     }
106 
107     /**
108      * Does this permission affect app ops.
109      *
110      * <p>I.e. does this permission have a matching app op or is this a background permission. All
111      * background permissions affect the app op of it's assigned foreground permission.
112      *
113      * @return {@code true} if this permission affects app ops
114      */
affectsAppOp()115     public boolean affectsAppOp() {
116         return mAppOp != null || isBackgroundPermission();
117     }
118 
119     /**
120      * Check if the permission is granted.
121      *
122      * <p>This ignores the state of the app-op. I.e. for apps not handling runtime permissions, this
123      * always returns {@code true}.
124      *
125      * @return If the permission is granted
126      */
isGranted()127     public boolean isGranted() {
128         return mGranted;
129     }
130 
131     /**
132      * Check if the permission is granted, also considering the state of the app-op.
133      *
134      * <p>For the UI, check the grant state of the whole group via
135      * {@link AppPermissionGroup#areRuntimePermissionsGranted}.
136      *
137      * @return {@code true} if the permission (and the app-op) is granted.
138      */
isGrantedIncludingAppOp()139     public boolean isGrantedIncludingAppOp() {
140         return mGranted && (!affectsAppOp() || isAppOpAllowed()) && !isReviewRequired();
141     }
142 
isReviewRequired()143     public boolean isReviewRequired() {
144         return (mFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
145     }
146 
unsetReviewRequired()147     public void unsetReviewRequired() {
148         mFlags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
149     }
150 
setGranted(boolean mGranted)151     public void setGranted(boolean mGranted) {
152         this.mGranted = mGranted;
153     }
154 
isAppOpAllowed()155     public boolean isAppOpAllowed() {
156         return mAppOpAllowed;
157     }
158 
isUserFixed()159     public boolean isUserFixed() {
160         return (mFlags & PackageManager.FLAG_PERMISSION_USER_FIXED) != 0;
161     }
162 
setUserFixed(boolean userFixed)163     public void setUserFixed(boolean userFixed) {
164         if (userFixed) {
165             mFlags |= PackageManager.FLAG_PERMISSION_USER_FIXED;
166         } else {
167             mFlags &= ~PackageManager.FLAG_PERMISSION_USER_FIXED;
168         }
169     }
170 
171     /**
172      * Sets the REVOKE_WHEN_REQUESTED permission flag
173      * @param revokeWhenRequested true to set the flag, false to unset it
174      */
setRevokeWhenRequested(boolean revokeWhenRequested)175     public void setRevokeWhenRequested(boolean revokeWhenRequested) {
176         if (revokeWhenRequested) {
177             mFlags |= PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
178         } else {
179             mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
180         }
181     }
182 
183     /**
184      * Sets the one-time permission flag
185      * @param oneTime true to set the flag, false to unset it
186      */
setOneTime(boolean oneTime)187     public void setOneTime(boolean oneTime) {
188         if (oneTime) {
189             mFlags |= PackageManager.FLAG_PERMISSION_ONE_TIME;
190         } else {
191             mFlags &= ~PackageManager.FLAG_PERMISSION_ONE_TIME;
192         }
193     }
194 
isSelectedLocationAccuracy()195     public boolean isSelectedLocationAccuracy() {
196         return (mFlags & PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY) != 0;
197     }
198 
199    /**
200      * Sets the selected-location-accuracy permission flag
201      * @param selectedLocationAccuracy true to set the flag, false to unset it
202      */
setSelectedLocationAccuracy(boolean selectedLocationAccuracy)203     public void setSelectedLocationAccuracy(boolean selectedLocationAccuracy) {
204         if (selectedLocationAccuracy) {
205             mFlags |= PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY;
206         } else {
207             mFlags &= ~PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY;
208         }
209     }
210 
isSystemFixed()211     public boolean isSystemFixed() {
212         return (mFlags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0;
213     }
214 
isPolicyFixed()215     public boolean isPolicyFixed() {
216         return (mFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
217     }
218 
isUserSet()219     public boolean isUserSet() {
220         return (mFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
221     }
222 
isGrantedByDefault()223     public boolean isGrantedByDefault() {
224         return (mFlags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0;
225     }
226 
227     /**
228      * Is the permission user sensitive, i.e. should it always be shown to the user.
229      *
230      * <p>Non-sensitive permission are usually hidden behind a setting in an overflow menu or
231      * some other kind of flag.
232      *
233      * @return {@code true} if the permission is user sensitive.
234      */
isUserSensitive()235     public boolean isUserSensitive() {
236         if (isGrantedIncludingAppOp()) {
237             return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
238         } else {
239             return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0;
240         }
241     }
242 
243     /**
244      * If this permission is split into a foreground and background permission, this is the name
245      * of the background permission.
246      *
247      * @return The name of the background permission or {@code null} if the permission is not split
248      */
getBackgroundPermissionName()249     public String getBackgroundPermissionName() {
250         return mBackgroundPermissionName;
251     }
252 
253     /**
254      * @return If this permission is split into a foreground and background permission,
255      * returns the background permission
256      */
getBackgroundPermission()257     public Permission getBackgroundPermission() {
258         return mBackgroundPermission;
259     }
260 
261     /**
262      * @return If this permission is split into a foreground and background permission,
263      * returns the foreground permission
264      */
getForegroundPermissions()265     public ArrayList<Permission> getForegroundPermissions() {
266         return mForegroundPermissions;
267     }
268 
269     /**
270      * @return {@code true} iff this is the foreground permission of a background-foreground-split
271      * permission
272      */
hasBackgroundPermission()273     public boolean hasBackgroundPermission() {
274         return mBackgroundPermissionName != null;
275     }
276 
277     /**
278      * @return {@code true} iff this is the background permission of a background-foreground-split
279      * permission
280      */
isBackgroundPermission()281     public boolean isBackgroundPermission() {
282         return mForegroundPermissions != null;
283     }
284 
285     /**
286      * @see PackageManager#FLAG_PERMISSION_ONE_TIME
287      */
isOneTime()288     public boolean isOneTime() {
289         return (mFlags & PackageManager.FLAG_PERMISSION_ONE_TIME) != 0;
290     }
291 
setUserSet(boolean userSet)292     public void setUserSet(boolean userSet) {
293         if (userSet) {
294             mFlags |= PackageManager.FLAG_PERMISSION_USER_SET;
295         } else {
296             mFlags &= ~PackageManager.FLAG_PERMISSION_USER_SET;
297         }
298     }
299 
setPolicyFixed(boolean policyFixed)300     public void setPolicyFixed(boolean policyFixed) {
301         if (policyFixed) {
302             mFlags |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
303         } else {
304             mFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
305         }
306     }
307 
isRevokedCompat()308     public boolean isRevokedCompat() {
309         return (mFlags & PackageManager.FLAG_PERMISSION_REVOKED_COMPAT) != 0;
310     }
311 
setRevokedCompat(boolean revokedCompat)312     public void setRevokedCompat(boolean revokedCompat) {
313         if (revokedCompat) {
314             mFlags |= PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
315         } else {
316             mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
317         }
318     }
319 
setAppOpAllowed(boolean mAppOpAllowed)320     public void setAppOpAllowed(boolean mAppOpAllowed) {
321         this.mAppOpAllowed = mAppOpAllowed;
322     }
323 
isEphemeral()324     public boolean isEphemeral() {
325         return mIsEphemeral;
326     }
327 
isRuntimeOnly()328     public boolean isRuntimeOnly() {
329         return mIsRuntimeOnly;
330     }
331 
isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions)332     public boolean isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions) {
333         return (!isEphemeralApp || isEphemeral())
334                 && (supportsRuntimePermissions || !isRuntimeOnly());
335     }
336 
337     @Override
equals(Object o)338     public boolean equals(Object o) {
339         if (!(o instanceof Permission)) {
340             return false;
341         }
342 
343         Permission other = (Permission) o;
344 
345         if (!Objects.equals(getName(), other.getName()) || getFlags() != other.getFlags()
346                 || isGranted() != other.isGranted()) {
347             return false;
348         }
349 
350 
351         // Only compare permission names, in order to avoid recursion
352         if (getBackgroundPermission() != null && other.getBackgroundPermission() != null) {
353             if (!Objects.equals(getBackgroundPermissionName(),
354                     other.getBackgroundPermissionName())) {
355                 return false;
356             }
357         } else if (getBackgroundPermission() != other.getBackgroundPermission()) {
358             return false;
359         }
360 
361         if (getForegroundPermissions() != null && other.getForegroundPermissions() != null) {
362             ArrayList<Permission> others = other.getForegroundPermissions();
363             if (getForegroundPermissions().size() != others.size()) {
364                 return false;
365             }
366             for (int i = 0; i < others.size(); i++) {
367                 if (!getForegroundPermissions().get(i).getName().equals(others.get(i).getName())) {
368                     return false;
369                 }
370             }
371         } else if (getForegroundPermissions() != null || other.getForegroundPermissions() != null) {
372             return false;
373         }
374 
375         return Objects.equals(getAppOp(), other.getAppOp())
376                 && isAppOpAllowed() == other.isAppOpAllowed();
377     }
378 
379     @Override
hashCode()380     public int hashCode() {
381         ArrayList<String> linkedPermissionNames = new ArrayList<>();
382         if (mBackgroundPermission != null) {
383             linkedPermissionNames.add(mBackgroundPermission.getName());
384         }
385         if (mForegroundPermissions != null) {
386             for (Permission linkedPermission: mForegroundPermissions) {
387                 if (linkedPermission != null) {
388                     linkedPermissionNames.add(linkedPermission.getName());
389                 }
390             }
391         }
392         return Objects.hash(mName, mFlags, mGranted, mAppOp, mAppOpAllowed, linkedPermissionNames);
393     }
394 }
395