• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 android.content;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.AppOpsManager;
23 import android.content.pm.PackageManager;
24 import android.content.pm.PermissionInfo;
25 import android.os.Binder;
26 import android.os.Process;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 
31 /**
32  * This class provides permission check APIs that verify both the
33  * permission and the associated app op for this permission if
34  * such is defined.
35  * <p>
36  * In the new permission model permissions with protection level
37  * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M}
38  * and above the user may not grant such permissions or revoke
39  * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M}
40  * these permissions are always granted as such apps do not expect
41  * permission revocations and would crash. Therefore, when the
42  * user disables a permission for a legacy app in the UI the
43  * platform disables the APIs guarded by this permission making
44  * them a no-op which is doing nothing or returning an empty
45  * result or default error.
46  * </p>
47  * <p>
48  * It is important that when you perform an operation on behalf of
49  * another app you use these APIs to check for permissions as the
50  * app may be a legacy app that does not participate in the new
51  * permission model for which the user had disabled the "permission"
52  * which is achieved by disallowing the corresponding app op.
53  * </p>
54  * <p>
55  * This class has two types of methods and you should be careful which
56  * type to call based on whether permission protected data is being
57  * passed to the app or you are just checking whether the app holds a
58  * permission. The reason is that a permission check requires checking
59  * the runtime permission and if it is granted checking the corresponding
60  * app op as for apps not supporting the runtime mode we never revoke
61  * permissions but disable app ops. Since there are two types of app op
62  * checks, one that does not leave a record an action was performed and
63  * another the does, one needs to call the preflight flavor of the checks
64  * named xxxForPreflight only if no private data is being delivered but
65  * a permission check is what is needed and the xxxForDataDelivery where
66  * the permission check is right before private data delivery.
67  *
68  * @hide
69  */
70 public final class PermissionChecker {
71     /** The permission is granted. */
72     public static final int PERMISSION_GRANTED =  PackageManager.PERMISSION_GRANTED;
73 
74     /** Returned when:
75      * <ul>
76      * <li>For non app op permissions, returned when the permission is denied.</li>
77      * <li>For app op permissions, returned when the app op is denied or app op is
78      * {@link AppOpsManager#MODE_DEFAULT} and permission is denied.</li>
79      * </ul>
80      *
81      */
82     public static final int PERMISSION_HARD_DENIED =  PackageManager.PERMISSION_DENIED;
83 
84     /** Only for runtime permissions, its returned when the runtime permission
85      * is granted, but the corresponding app op is denied. */
86     public static final int PERMISSION_SOFT_DENIED =  PackageManager.PERMISSION_DENIED - 1;
87 
88     /** Constant when the PID for which we check permissions is unknown. */
89     public static final int PID_UNKNOWN = -1;
90 
91     /** @hide */
92     @IntDef({PERMISSION_GRANTED,
93             PERMISSION_SOFT_DENIED,
94             PERMISSION_HARD_DENIED})
95     @Retention(RetentionPolicy.SOURCE)
96     public @interface PermissionResult {}
97 
PermissionChecker()98     private PermissionChecker() {
99         /* do nothing */
100     }
101 
102     /**
103      * Checks whether a given package in a UID and PID has a given permission
104      * and whether the app op that corresponds to this permission is allowed.
105      *
106      * <strong>NOTE:</strong> Use this method only for permission checks at the
107      * point where you will deliver the permission protected data to clients.
108      *
109      * <p>For example, if an app registers a location listener it should have the location
110      * permission but no data is actually sent to the app at the moment of registration
111      * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)}
112      * to determine if the app has or may have location permission (if app has only foreground
113      * location the grant state depends on the app's fg/gb state) and this check will not
114      * leave a trace that permission protected data was delivered. When you are about to
115      * deliver the location data to a registered listener you should use this method which
116      * will evaluate the permission access based on the current fg/bg state of the app and
117      * leave a record that the data was accessed.
118      *
119      * @param context Context for accessing resources.
120      * @param permission The permission to check.
121      * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
122      *    is not known.
123      * @param uid The uid for which to check.
124      * @param packageName The package name for which to check. If null the
125      *     the first package for the calling UID will be used.
126      * @param attributionTag attribution tag
127      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
128      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
129      * @param message A message describing the reason the permission was checked
130      *
131      * @see #checkPermissionForPreflight(Context, String, int, int, String)
132      */
133     @PermissionResult
checkPermissionForDataDelivery(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message)134     public static int checkPermissionForDataDelivery(@NonNull Context context,
135             @NonNull String permission, int pid, int uid, @Nullable String packageName,
136             @Nullable String attributionTag, @Nullable String message) {
137         return checkPermissionCommon(context, permission, pid, uid, packageName, attributionTag,
138                 message, true /*forDataDelivery*/);
139     }
140 
141     /**
142      * Checks whether a given package in a UID and PID has a given permission
143      * and whether the app op that corresponds to this permission is allowed.
144      *
145      * <strong>NOTE:</strong> Use this method only for permission checks at the
146      * preflight point where you will not deliver the permission protected data
147      * to clients but schedule permission data delivery, apps register listeners,
148      * etc.
149      *
150      * <p>For example, if an app registers a location listener it should have the location
151      * permission but no data is actually sent to the app at the moment of registration
152      * and you should use this method to determine if the app has or may have location
153      * permission (if app has only foreground location the grant state depends on the app's
154      * fg/gb state) and this check will not leave a trace that permission protected data
155      * was delivered. When you are about to deliver the location data to a registered
156      * listener you should use {@link #checkPermissionForDataDelivery(Context, String,
157      * int, int, String, String)} which will evaluate the permission access based on the current
158      * fg/bg state of the app and leave a record that the data was accessed.
159      *
160      * @param context Context for accessing resources.
161      * @param permission The permission to check.
162      * @param pid The process id for which to check.
163      * @param uid The uid for which to check.
164      * @param packageName The package name for which to check. If null the
165      *     the first package for the calling UID will be used.
166      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
167      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
168      *
169      * @see #checkPermissionForDataDelivery(Context, String, int, int, String, String)
170      */
171     @PermissionResult
checkPermissionForPreflight(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName)172     public static int checkPermissionForPreflight(@NonNull Context context,
173             @NonNull String permission, int pid, int uid, @Nullable String packageName) {
174         return checkPermissionCommon(context, permission, pid, uid, packageName,
175                 null /*attributionTag*/, null /*message*/, false /*forDataDelivery*/);
176     }
177 
178     /**
179      * Checks whether your app has a given permission and whether the app op
180      * that corresponds to this permission is allowed.
181      *
182      * <strong>NOTE:</strong> Use this method only for permission checks at the
183      * point where you will deliver the permission protected data to clients.
184      *
185      * <p>For example, if an app registers a location listener it should have the location
186      * permission but no data is actually sent to the app at the moment of registration
187      * and you should use {@link #checkSelfPermissionForPreflight(Context, String)}
188      * to determine if the app has or may have location permission (if app has only foreground
189      * location the grant state depends on the app's fg/gb state) and this check will not
190      * leave a trace that permission protected data was delivered. When you are about to
191      * deliver the location data to a registered listener you should use this method
192      * which will evaluate the permission access based on the current fg/bg state of the
193      * app and leave a record that the data was accessed.
194      *
195      * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
196      * {@link Process#myUid()}.
197      *
198      * @param context Context for accessing resources.
199      * @param permission The permission to check.
200      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
201      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
202      * @param message A message describing the reason the permission was checked
203      *
204      * @see #checkSelfPermissionForPreflight(Context, String)
205      */
206     @PermissionResult
checkSelfPermissionForDataDelivery(@onNull Context context, @NonNull String permission, @Nullable String message)207     public static int checkSelfPermissionForDataDelivery(@NonNull Context context,
208             @NonNull String permission, @Nullable String message) {
209         return checkPermissionForDataDelivery(context, permission, Process.myPid(),
210                 Process.myUid(), context.getPackageName(), context.getAttributionTag(), message);
211     }
212 
213     /**
214      * Checks whether your app has a given permission and whether the app op
215      * that corresponds to this permission is allowed.
216      *
217      * <strong>NOTE:</strong> Use this method only for permission checks at the
218      * preflight point where you will not deliver the permission protected data
219      * to clients but schedule permission data delivery, apps register listeners,
220      * etc.
221      *
222      * <p>For example, if an app registers a location listener it should have the location
223      * permission but no data is actually sent to the app at the moment of registration
224      * and you should use this method to determine if the app has or may have location
225      * permission (if app has only foreground location the grant state depends on the
226      * app's fg/gb state) and this check will not leave a trace that permission protected
227      * data was delivered. When you are about to deliver the location data to a registered
228      * listener you should use this method which will evaluate the permission access based
229      * on the current fg/bg state of the app and leave a record that the data was accessed.
230      *
231      * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
232      * {@link Process#myUid()}.
233      *
234      * @param context Context for accessing resources.
235      * @param permission The permission to check.
236      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
237      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
238      *
239      * @see #checkSelfPermissionForDataDelivery(Context, String, String)
240      */
241     @PermissionResult
checkSelfPermissionForPreflight(@onNull Context context, @NonNull String permission)242     public static int checkSelfPermissionForPreflight(@NonNull Context context,
243             @NonNull String permission) {
244         return checkPermissionForPreflight(context, permission, Process.myPid(),
245                 Process.myUid(), context.getPackageName());
246     }
247 
248     /**
249      * Checks whether the IPC you are handling has a given permission and whether
250      * the app op that corresponds to this permission is allowed.
251      *
252      * <strong>NOTE:</strong> Use this method only for permission checks at the
253      * point where you will deliver the permission protected data to clients.
254      *
255      * <p>For example, if an app registers a location listener it should have the location
256      * permission but no data is actually sent to the app at the moment of registration
257      * and you should use {@link #checkCallingPermissionForPreflight(Context, String, String)}
258      * to determine if the app has or may have location permission (if app has only foreground
259      * location the grant state depends on the app's fg/gb state) and this check will not
260      * leave a trace that permission protected data was delivered. When you are about to
261      * deliver the location data to a registered listener you should use this method which
262      * will evaluate the permission access based on the current fg/bg state of the app and
263      * leave a record that the data was accessed.
264      *
265      * @param context Context for accessing resources.
266      * @param permission The permission to check.
267      * @param packageName The package name making the IPC. If null the
268      *     the first package for the calling UID will be used.
269      * @param attributionTag attribution tag
270      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
271      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
272      * @param message A message describing the reason the permission was checked
273      *
274      * @see #checkCallingPermissionForPreflight(Context, String, String)
275      */
276     @PermissionResult
checkCallingPermissionForDataDelivery(@onNull Context context, @NonNull String permission, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message)277     public static int checkCallingPermissionForDataDelivery(@NonNull Context context,
278             @NonNull String permission, @Nullable String packageName,
279             @Nullable String attributionTag, @Nullable String message) {
280         if (Binder.getCallingPid() == Process.myPid()) {
281             return PERMISSION_HARD_DENIED;
282         }
283         return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
284                 Binder.getCallingUid(), packageName, attributionTag, message);
285     }
286 
287     /**
288      * Checks whether the IPC you are handling has a given permission and whether
289      * the app op that corresponds to this permission is allowed.
290      *
291      * <strong>NOTE:</strong> Use this method only for permission checks at the
292      * preflight point where you will not deliver the permission protected data
293      * to clients but schedule permission data delivery, apps register listeners,
294      * etc.
295      *
296      * <p>For example, if an app registers a location listener it should have the location
297      * permission but no data is actually sent to the app at the moment of registration
298      * and you should use this method to determine if the app has or may have location
299      * permission (if app has only foreground location the grant state depends on the app's
300      * fg/gb state) and this check will not leave a trace that permission protected data
301      * was delivered. When you are about to deliver the location data to a registered
302      * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
303      * String, String)} which will evaluate the permission access based on the current fg/bg state
304      * of the app and leave a record that the data was accessed.
305      *
306      * @param context Context for accessing resources.
307      * @param permission The permission to check.
308      * @param packageName The package name making the IPC. If null the
309      *     the first package for the calling UID will be used.
310      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
311      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
312      *
313      * @see #checkCallingPermissionForDataDelivery(Context, String, String, String)
314      */
315     @PermissionResult
checkCallingPermissionForPreflight(@onNull Context context, @NonNull String permission, @Nullable String packageName)316     public static int checkCallingPermissionForPreflight(@NonNull Context context,
317             @NonNull String permission, @Nullable String packageName) {
318         if (Binder.getCallingPid() == Process.myPid()) {
319             return PERMISSION_HARD_DENIED;
320         }
321         return checkPermissionForPreflight(context, permission, Binder.getCallingPid(),
322                 Binder.getCallingUid(), packageName);
323     }
324 
325     /**
326      * Checks whether the IPC you are handling or your app has a given permission
327      * and whether the app op that corresponds to this permission is allowed.
328      *
329      * <strong>NOTE:</strong> Use this method only for permission checks at the
330      * point where you will deliver the permission protected data to clients.
331      *
332      * <p>For example, if an app registers a location listener it should have the location
333      * permission but no data is actually sent to the app at the moment of registration
334      * and you should use {@link #checkCallingOrSelfPermissionForPreflight(Context, String)}
335      * to determine if the app has or may have location permission (if app has only foreground
336      * location the grant state depends on the app's fg/gb state) and this check will not
337      * leave a trace that permission protected data was delivered. When you are about to
338      * deliver the location data to a registered listener you should use this method which
339      * will evaluate the permission access based on the current fg/bg state of the app and
340      * leave a record that the data was accessed.
341      *
342      * @param context Context for accessing resources.
343      * @param permission The permission to check.
344      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
345      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
346      * @param attributionTag attribution tag of caller (if not self)
347      * @param message A message describing the reason the permission was checked
348      *
349      * @see #checkCallingOrSelfPermissionForPreflight(Context, String)
350      */
351     @PermissionResult
checkCallingOrSelfPermissionForDataDelivery(@onNull Context context, @NonNull String permission, @Nullable String attributionTag, @Nullable String message)352     public static int checkCallingOrSelfPermissionForDataDelivery(@NonNull Context context,
353             @NonNull String permission, @Nullable String attributionTag, @Nullable String message) {
354         String packageName = (Binder.getCallingPid() == Process.myPid())
355                 ? context.getPackageName() : null;
356         attributionTag = (Binder.getCallingPid() == Process.myPid())
357                 ? context.getAttributionTag() : attributionTag;
358         return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
359                 Binder.getCallingUid(), packageName, attributionTag, message);
360     }
361 
362     /**
363      * Checks whether the IPC you are handling or your app has a given permission
364      * and whether the app op that corresponds to this permission is allowed.
365      *
366      * <strong>NOTE:</strong> Use this method only for permission checks at the
367      * preflight point where you will not deliver the permission protected data
368      * to clients but schedule permission data delivery, apps register listeners,
369      * etc.
370      *
371      * <p>For example, if an app registers a location listener it should have the location
372      * permission but no data is actually sent to the app at the moment of registration
373      * and you should use this method to determine if the app has or may have location
374      * permission (if app has only foreground location the grant state depends on the
375      * app's fg/gb state) and this check will not leave a trace that permission protected
376      * data was delivered. When you are about to deliver the location data to a registered
377      * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
378      * String, String, String)} which will evaluate the permission access based on the current
379      * fg/bg state of the app and leave a record that the data was accessed.
380      *
381      * @param context Context for accessing resources.
382      * @param permission The permission to check.
383      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
384      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
385      *
386      * @see #checkCallingOrSelfPermissionForDataDelivery(Context, String, String, String)
387      */
388     @PermissionResult
checkCallingOrSelfPermissionForPreflight(@onNull Context context, @NonNull String permission)389     public static int checkCallingOrSelfPermissionForPreflight(@NonNull Context context,
390             @NonNull String permission) {
391         String packageName = (Binder.getCallingPid() == Process.myPid())
392                 ? context.getPackageName() : null;
393         return checkPermissionForPreflight(context, permission, Binder.getCallingPid(),
394                 Binder.getCallingUid(), packageName);
395     }
396 
checkPermissionCommon(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery)397     static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
398             int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
399             @Nullable String message, boolean forDataDelivery) {
400         final PermissionInfo permissionInfo;
401         try {
402             // TODO(b/147869157): Cache platform defined app op and runtime permissions to avoid
403             // calling into the package manager every time.
404             permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0);
405         } catch (PackageManager.NameNotFoundException ignored) {
406             return PERMISSION_HARD_DENIED;
407         }
408 
409         if (packageName == null) {
410             String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
411             if (packageNames != null && packageNames.length > 0) {
412                 packageName = packageNames[0];
413             }
414         }
415 
416         if (permissionInfo.isAppOp()) {
417             return checkAppOpPermission(context, permission, pid, uid, packageName, attributionTag,
418                     message, forDataDelivery);
419         }
420         if (permissionInfo.isRuntime()) {
421             return checkRuntimePermission(context, permission, pid, uid, packageName,
422                     attributionTag, message, forDataDelivery);
423         }
424         return context.checkPermission(permission, pid, uid);
425     }
426 
checkAppOpPermission(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery)427     private static int checkAppOpPermission(@NonNull Context context, @NonNull String permission,
428             int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
429             @Nullable String message, boolean forDataDelivery) {
430         final String op = AppOpsManager.permissionToOp(permission);
431         if (op == null || packageName == null) {
432             return PERMISSION_HARD_DENIED;
433         }
434 
435         final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
436         final int opMode = (forDataDelivery)
437                 ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
438                 : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
439 
440         switch (opMode) {
441             case AppOpsManager.MODE_ALLOWED:
442             case AppOpsManager.MODE_FOREGROUND: {
443                 return PERMISSION_GRANTED;
444             }
445             case AppOpsManager.MODE_DEFAULT: {
446                 return context.checkPermission(permission, pid, uid)
447                             == PackageManager.PERMISSION_GRANTED
448                         ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
449             }
450             default: {
451                 return PERMISSION_HARD_DENIED;
452             }
453         }
454     }
455 
checkRuntimePermission(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery)456     private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission,
457             int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
458             @Nullable String message, boolean forDataDelivery) {
459         if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
460             return PERMISSION_HARD_DENIED;
461         }
462 
463         final String op = AppOpsManager.permissionToOp(permission);
464         if (op == null || packageName == null) {
465             return PERMISSION_GRANTED;
466         }
467 
468         final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
469         final int opMode = (forDataDelivery)
470                 ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
471                 : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
472 
473         switch (opMode) {
474             case AppOpsManager.MODE_ALLOWED:
475             case AppOpsManager.MODE_FOREGROUND:
476                 return PERMISSION_GRANTED;
477             default:
478                 return PERMISSION_SOFT_DENIED;
479         }
480     }
481 }
482