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