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 one-time permission flag
173      * @param oneTime true to set the flag, false to unset it
174      */
setOneTime(boolean oneTime)175     public void setOneTime(boolean oneTime) {
176         if (oneTime) {
177             mFlags |= PackageManager.FLAG_PERMISSION_ONE_TIME;
178         } else {
179             mFlags &= ~PackageManager.FLAG_PERMISSION_ONE_TIME;
180         }
181     }
182 
isSystemFixed()183     public boolean isSystemFixed() {
184         return (mFlags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0;
185     }
186 
isPolicyFixed()187     public boolean isPolicyFixed() {
188         return (mFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
189     }
190 
isUserSet()191     public boolean isUserSet() {
192         return (mFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
193     }
194 
isGrantedByDefault()195     public boolean isGrantedByDefault() {
196         return (mFlags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0;
197     }
198 
199     /**
200      * Is the permission user sensitive, i.e. should it always be shown to the user.
201      *
202      * <p>Non-sensitive permission are usually hidden behind a setting in an overflow menu or
203      * some other kind of flag.
204      *
205      * @return {@code true} if the permission is user sensitive.
206      */
isUserSensitive()207     public boolean isUserSensitive() {
208         if (isGrantedIncludingAppOp()) {
209             return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
210         } else {
211             return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0;
212         }
213     }
214 
215     /**
216      * If this permission is split into a foreground and background permission, this is the name
217      * of the background permission.
218      *
219      * @return The name of the background permission or {@code null} if the permission is not split
220      */
getBackgroundPermissionName()221     public String getBackgroundPermissionName() {
222         return mBackgroundPermissionName;
223     }
224 
225     /**
226      * @return If this permission is split into a foreground and background permission,
227      * returns the background permission
228      */
getBackgroundPermission()229     public Permission getBackgroundPermission() {
230         return mBackgroundPermission;
231     }
232 
233     /**
234      * @return If this permission is split into a foreground and background permission,
235      * returns the foreground permission
236      */
getForegroundPermissions()237     public ArrayList<Permission> getForegroundPermissions() {
238         return mForegroundPermissions;
239     }
240 
241     /**
242      * @return {@code true} iff this is the foreground permission of a background-foreground-split
243      * permission
244      */
hasBackgroundPermission()245     public boolean hasBackgroundPermission() {
246         return mBackgroundPermissionName != null;
247     }
248 
249     /**
250      * @return {@code true} iff this is the background permission of a background-foreground-split
251      * permission
252      */
isBackgroundPermission()253     public boolean isBackgroundPermission() {
254         return mForegroundPermissions != null;
255     }
256 
257     /**
258      * @see PackageManager#FLAG_PERMISSION_ONE_TIME
259      */
isOneTime()260     public boolean isOneTime() {
261         return (mFlags & PackageManager.FLAG_PERMISSION_ONE_TIME) != 0;
262     }
263 
setUserSet(boolean userSet)264     public void setUserSet(boolean userSet) {
265         if (userSet) {
266             mFlags |= PackageManager.FLAG_PERMISSION_USER_SET;
267         } else {
268             mFlags &= ~PackageManager.FLAG_PERMISSION_USER_SET;
269         }
270     }
271 
setPolicyFixed(boolean policyFixed)272     public void setPolicyFixed(boolean policyFixed) {
273         if (policyFixed) {
274             mFlags |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
275         } else {
276             mFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
277         }
278     }
279 
isRevokedCompat()280     public boolean isRevokedCompat() {
281         return (mFlags & PackageManager.FLAG_PERMISSION_REVOKED_COMPAT) != 0;
282     }
283 
setRevokedCompat(boolean revokedCompat)284     public void setRevokedCompat(boolean revokedCompat) {
285         if (revokedCompat) {
286             mFlags |= PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
287         } else {
288             mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
289         }
290     }
291 
setAppOpAllowed(boolean mAppOpAllowed)292     public void setAppOpAllowed(boolean mAppOpAllowed) {
293         this.mAppOpAllowed = mAppOpAllowed;
294     }
295 
isEphemeral()296     public boolean isEphemeral() {
297         return mIsEphemeral;
298     }
299 
isRuntimeOnly()300     public boolean isRuntimeOnly() {
301         return mIsRuntimeOnly;
302     }
303 
isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions)304     public boolean isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions) {
305         return (!isEphemeralApp || isEphemeral())
306                 && (supportsRuntimePermissions || !isRuntimeOnly());
307     }
308 
309     @Override
equals(Object o)310     public boolean equals(Object o) {
311         if (!(o instanceof Permission)) {
312             return false;
313         }
314 
315         Permission other = (Permission) o;
316 
317         if (!Objects.equals(getName(), other.getName()) || getFlags() != other.getFlags()
318                 || isGranted() != other.isGranted()) {
319             return false;
320         }
321 
322 
323         // Only compare permission names, in order to avoid recursion
324         if (getBackgroundPermission() != null && other.getBackgroundPermission() != null) {
325             if (!Objects.equals(getBackgroundPermissionName(),
326                     other.getBackgroundPermissionName())) {
327                 return false;
328             }
329         } else if (getBackgroundPermission() != other.getBackgroundPermission()) {
330             return false;
331         }
332 
333         if (getForegroundPermissions() != null && other.getForegroundPermissions() != null) {
334             ArrayList<Permission> others = other.getForegroundPermissions();
335             if (getForegroundPermissions().size() != others.size()) {
336                 return false;
337             }
338             for (int i = 0; i < others.size(); i++) {
339                 if (!getForegroundPermissions().get(i).getName().equals(others.get(i).getName())) {
340                     return false;
341                 }
342             }
343         } else if (getForegroundPermissions() != null || other.getForegroundPermissions() != null) {
344             return false;
345         }
346 
347         return Objects.equals(getAppOp(), other.getAppOp())
348                 && isAppOpAllowed() == other.isAppOpAllowed();
349     }
350 
351     @Override
hashCode()352     public int hashCode() {
353         ArrayList<String> linkedPermissionNames = new ArrayList<>();
354         if (mBackgroundPermission != null) {
355             linkedPermissionNames.add(mBackgroundPermission.getName());
356         }
357         if (mForegroundPermissions != null) {
358             for (Permission linkedPermission: mForegroundPermissions) {
359                 if (linkedPermission != null) {
360                     linkedPermissionNames.add(linkedPermission.getName());
361                 }
362             }
363         }
364         return Objects.hash(mName, mFlags, mGranted, mAppOp, mAppOpAllowed, linkedPermissionNames);
365     }
366 }
367