• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.providers.media.util;
18 
19 import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
20 import static android.Manifest.permission.ACCESS_MTP;
21 import static android.Manifest.permission.BACKUP;
22 import static android.Manifest.permission.INSTALL_PACKAGES;
23 import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
24 import static android.Manifest.permission.MANAGE_MEDIA;
25 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
26 import static android.Manifest.permission.READ_MEDIA_AUDIO;
27 import static android.Manifest.permission.READ_MEDIA_IMAGES;
28 import static android.Manifest.permission.READ_MEDIA_VIDEO;
29 import static android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED;
30 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
31 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
32 import static android.app.AppOpsManager.MODE_ALLOWED;
33 import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE;
34 import static android.app.AppOpsManager.OPSTR_NO_ISOLATED_STORAGE;
35 import static android.app.AppOpsManager.OPSTR_READ_MEDIA_AUDIO;
36 import static android.app.AppOpsManager.OPSTR_READ_MEDIA_IMAGES;
37 import static android.app.AppOpsManager.OPSTR_READ_MEDIA_VIDEO;
38 import static android.app.AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES;
39 import static android.app.AppOpsManager.OPSTR_WRITE_MEDIA_AUDIO;
40 import static android.app.AppOpsManager.OPSTR_WRITE_MEDIA_IMAGES;
41 import static android.app.AppOpsManager.OPSTR_WRITE_MEDIA_VIDEO;
42 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
43 
44 import android.annotation.UserIdInt;
45 import android.app.AppOpsManager;
46 import android.app.DownloadManager;
47 import android.content.Context;
48 import android.content.pm.PackageInfo;
49 import android.content.pm.PackageManager;
50 import android.os.UserHandle;
51 
52 import androidx.annotation.NonNull;
53 import androidx.annotation.Nullable;
54 import androidx.annotation.VisibleForTesting;
55 
56 import com.android.modules.utils.build.SdkLevel;
57 
58 public class PermissionUtils {
59 
60     // Callers must hold both the old and new permissions, so that we can
61     // handle obscure cases like when an app targets Q but was installed on
62     // a device that was originally running on P before being upgraded to Q.
63 
64     private static ThreadLocal<String> sOpDescription = new ThreadLocal<>();
65 
setOpDescription(@ullable String description)66     public static void setOpDescription(@Nullable String description) {
67         sOpDescription.set(description);
68     }
69 
clearOpDescription()70     public static void clearOpDescription() { sOpDescription.set(null); }
71 
checkPermissionSelf(@onNull Context context, int pid, int uid)72     public static boolean checkPermissionSelf(@NonNull Context context, int pid, int uid) {
73         return UserHandle.getAppId(android.os.Process.myUid()) == UserHandle.getAppId(uid);
74     }
75 
76     /**
77      * Return {@code true} when the given user id's corresponsing app id is the same as current
78      * process's app id, else return {@code false}.
79      */
checkPermissionSelf(@serIdInt int uid)80     public static boolean checkPermissionSelf(@UserIdInt int uid) {
81         return UserHandle.getAppId(android.os.Process.myUid()) == UserHandle.getAppId(uid);
82     }
83 
84     /**
85      * Returns {@code true} if the given {@code uid} is a {@link android.os.Process.ROOT_UID} or
86      * {@link android.os.Process.SHELL_UID}. {@code false} otherwise.
87      */
checkPermissionShell(int uid)88     public static boolean checkPermissionShell(int uid) {
89         switch (uid) {
90             case android.os.Process.ROOT_UID:
91             case android.os.Process.SHELL_UID:
92                 return true;
93             default:
94                 return false;
95         }
96     }
97 
98     /**
99      * Check if the given package has been granted the "file manager" role on
100      * the device, which should grant them certain broader access.
101      */
checkPermissionManager(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)102     public static boolean checkPermissionManager(@NonNull Context context, int pid,
103             int uid, @NonNull String packageName, @Nullable String attributionTag) {
104         return checkPermissionForDataDelivery(context, MANAGE_EXTERNAL_STORAGE, pid, uid,
105                 packageName, attributionTag,
106                 generateAppOpMessage(packageName,sOpDescription.get()));
107     }
108 
109     /**
110      * Check if the given package has the ability to "delegate" the ownership of
111      * media items that they own to other apps, typically when they've finished
112      * performing operations on behalf of those apps.
113      * <p>
114      * One use-case for this is backup/restore apps, where the app restoring the
115      * content needs to shift the ownership back to the app that originally
116      * owned that media.
117      * <p>
118      * Another use-case is {@link DownloadManager}, which shifts ownership of
119      * finished downloads to the app that originally requested them.
120      */
checkPermissionDelegator(@onNull Context context, int pid, int uid)121     public static boolean checkPermissionDelegator(@NonNull Context context, int pid, int uid) {
122         return (context.checkPermission(BACKUP, pid, uid) == PERMISSION_GRANTED)
123                 || (context.checkPermission(UPDATE_DEVICE_STATS, pid, uid) == PERMISSION_GRANTED);
124     }
125 
checkPermissionWriteStorage(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)126     public static boolean checkPermissionWriteStorage(@NonNull Context context, int pid, int uid,
127             @NonNull String packageName, @Nullable String attributionTag) {
128         return checkPermissionForDataDelivery(context, WRITE_EXTERNAL_STORAGE, pid, uid,
129                 packageName, attributionTag,
130                 generateAppOpMessage(packageName,sOpDescription.get()));
131     }
132 
checkPermissionReadStorage(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)133     public static boolean checkPermissionReadStorage(@NonNull Context context, int pid, int uid,
134             @NonNull String packageName, @Nullable String attributionTag) {
135         return checkPermissionForDataDelivery(context, READ_EXTERNAL_STORAGE, pid, uid,
136                 packageName, attributionTag,
137                 generateAppOpMessage(packageName,sOpDescription.get()));
138     }
139 
140     /**
141      * Check if the given package has been granted the
142      * android.Manifest.permission#ACCESS_MEDIA_LOCATION permission.
143      */
checkPermissionAccessMediaLocation(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean isTargetSdkAtLeastT)144     public static boolean checkPermissionAccessMediaLocation(@NonNull Context context, int pid,
145             int uid, @NonNull String packageName, @Nullable String attributionTag,
146             boolean isTargetSdkAtLeastT) {
147         return checkPermissionForDataDelivery(context, ACCESS_MEDIA_LOCATION, pid, uid, packageName,
148                 attributionTag, generateAppOpMessage(packageName, sOpDescription.get()))
149                 || checkPermissionAccessMediaCompatGrant(context, pid, uid, packageName,
150                 attributionTag, isTargetSdkAtLeastT);
151     }
152 
153     /**
154      *  Check if ACCESS_MEDIA_LOCATION is requested, and that READ_MEDIA_VISUAL_USER_SELECTED is
155      *  implicitly requested and fully granted
156      */
checkPermissionAccessMediaCompatGrant(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean isTargetSdkAtLeastT)157     private static boolean checkPermissionAccessMediaCompatGrant(@NonNull Context context, int pid,
158             int uid, @NonNull String packageName, @Nullable String attributionTag,
159             boolean isTargetSdkAtLeastT) {
160         if (!SdkLevel.isAtLeastU() || !isTargetSdkAtLeastT) {
161             return false;
162         }
163         try {
164             PackageInfo pi = context.getPackageManager().getPackageInfo(packageName,
165                     PackageManager.GET_PERMISSIONS);
166             if (pi.requestedPermissions == null) {
167                 return false;
168             }
169 
170             boolean amlRequested = false;
171             boolean userSelectedImplicit = false;
172             for (int i = 0; i < pi.requestedPermissions.length; i++) {
173                 if (ACCESS_MEDIA_LOCATION.equals(pi.requestedPermissions[i])) {
174                     amlRequested = true;
175                 }
176                 if (READ_MEDIA_VISUAL_USER_SELECTED.equals(pi.requestedPermissions[i])) {
177                     userSelectedImplicit = (pi.requestedPermissionsFlags[i]
178                             & PackageInfo.REQUESTED_PERMISSION_IMPLICIT) != 0;
179                 }
180             }
181 
182             return amlRequested && userSelectedImplicit && checkPermissionReadVisualUserSelected(
183                     context, pid, uid, packageName, attributionTag, isTargetSdkAtLeastT);
184         } catch (PackageManager.NameNotFoundException e) {
185             return false;
186         }
187     }
188 
189     /**
190      * Check if the given package has been granted the
191      * android.Manifest.permission#MANAGE_MEDIA permission.
192      */
checkPermissionManageMedia(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)193     public static boolean checkPermissionManageMedia(@NonNull Context context, int pid, int uid,
194             @NonNull String packageName, @Nullable String attributionTag) {
195         return checkPermissionForDataDelivery(context, MANAGE_MEDIA, pid, uid, packageName,
196                 attributionTag, generateAppOpMessage(packageName, sOpDescription.get()));
197     }
198 
checkIsLegacyStorageGranted(@onNull Context context, int uid, String packageName, @Nullable String attributionTag)199     public static boolean checkIsLegacyStorageGranted(@NonNull Context context, int uid,
200             String packageName, @Nullable String attributionTag) {
201         if (context.getSystemService(AppOpsManager.class)
202                 .unsafeCheckOp(OPSTR_LEGACY_STORAGE, uid, packageName) == MODE_ALLOWED) {
203             return true;
204         }
205         // Check OPSTR_NO_ISOLATED_STORAGE app op.
206         return checkNoIsolatedStorageGranted(context, uid, packageName, attributionTag);
207     }
208 
checkPermissionReadAudio( @onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean targetSdkIsAtLeastT)209     public static boolean checkPermissionReadAudio(
210             @NonNull Context context,
211             int pid,
212             int uid,
213             @NonNull String packageName,
214             @Nullable String attributionTag,
215             boolean targetSdkIsAtLeastT) {
216 
217         String permission = targetSdkIsAtLeastT && SdkLevel.isAtLeastT()
218                 ? READ_MEDIA_AUDIO : READ_EXTERNAL_STORAGE;
219 
220         if (!checkPermissionForPreflight(context, permission, pid, uid, packageName)) {
221             return false;
222         }
223         return checkAppOpAllowingLegacy(context, OPSTR_READ_MEDIA_AUDIO, pid,
224                 uid, packageName, attributionTag,
225                 generateAppOpMessage(packageName, sOpDescription.get()));
226     }
227 
checkPermissionWriteAudio(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)228     public static boolean checkPermissionWriteAudio(@NonNull Context context, int pid, int uid,
229             @NonNull String packageName, @Nullable String attributionTag) {
230         if (!checkPermissionAllowingNonLegacy(
231                     context, WRITE_EXTERNAL_STORAGE, pid, uid, packageName)) {
232             return false;
233         }
234         return checkAppOpAllowingLegacy(context, OPSTR_WRITE_MEDIA_AUDIO, pid,
235                 uid, packageName, attributionTag,
236                 generateAppOpMessage(packageName, sOpDescription.get()));
237     }
238 
checkPermissionReadVideo( @onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean targetSdkIsAtLeastT)239     public static boolean checkPermissionReadVideo(
240             @NonNull Context context,
241             int pid,
242             int uid,
243             @NonNull String packageName,
244             @Nullable String attributionTag,
245             boolean targetSdkIsAtLeastT) {
246         String permission = targetSdkIsAtLeastT && SdkLevel.isAtLeastT()
247                 ? READ_MEDIA_VIDEO : READ_EXTERNAL_STORAGE;
248 
249         if (!checkPermissionForPreflight(context, permission, pid, uid, packageName)) {
250             return false;
251         }
252 
253         return checkAppOpAllowingLegacy(context, OPSTR_READ_MEDIA_VIDEO, pid,
254                 uid, packageName, attributionTag,
255                 generateAppOpMessage(packageName, sOpDescription.get()));
256     }
257 
checkPermissionWriteVideo(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)258     public static boolean checkPermissionWriteVideo(@NonNull Context context, int pid, int uid,
259             @NonNull String packageName, @Nullable String attributionTag) {
260         if (!checkPermissionAllowingNonLegacy(
261                 context, WRITE_EXTERNAL_STORAGE, pid, uid, packageName)) {
262             return false;
263         }
264         return checkAppOpAllowingLegacy(context, OPSTR_WRITE_MEDIA_VIDEO, pid,
265                 uid, packageName, attributionTag,
266                 generateAppOpMessage(packageName, sOpDescription.get()));
267     }
268 
checkPermissionReadImages( @onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean targetSdkIsAtLeastT)269     public static boolean checkPermissionReadImages(
270             @NonNull Context context,
271             int pid,
272             int uid,
273             @NonNull String packageName,
274             @Nullable String attributionTag,
275             boolean targetSdkIsAtLeastT) {
276         String permission = targetSdkIsAtLeastT && SdkLevel.isAtLeastT()
277                 ? READ_MEDIA_IMAGES : READ_EXTERNAL_STORAGE;
278 
279         if (!checkPermissionForPreflight(context, permission, pid, uid, packageName)) {
280             return false;
281         }
282 
283         return checkAppOpAllowingLegacy(context, OPSTR_READ_MEDIA_IMAGES, pid,
284                 uid, packageName, attributionTag,
285                 generateAppOpMessage(packageName, sOpDescription.get()));
286     }
287 
checkPermissionWriteImages(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)288     public static boolean checkPermissionWriteImages(@NonNull Context context, int pid, int uid,
289             @NonNull String packageName, @Nullable String attributionTag) {
290         if (!checkPermissionAllowingNonLegacy(
291                 context, WRITE_EXTERNAL_STORAGE, pid, uid, packageName)) {
292             return false;
293         }
294         return checkAppOpAllowingLegacy(context, OPSTR_WRITE_MEDIA_IMAGES, pid,
295                 uid, packageName, attributionTag,
296                 generateAppOpMessage(packageName, sOpDescription.get()));
297     }
298 
299     /**
300      * Check if the given package has been granted the
301      * android.Manifest.permission#READ_MEDIA_VISUAL_USER_SELECTED permission.
302      */
checkPermissionReadVisualUserSelected( @onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean targetSdkIsAtLeastT)303     public static boolean checkPermissionReadVisualUserSelected(
304             @NonNull Context context,
305             int pid,
306             int uid,
307             @NonNull String packageName,
308             @Nullable String attributionTag,
309             boolean targetSdkIsAtLeastT) {
310         if (!SdkLevel.isAtLeastU() || !targetSdkIsAtLeastT) {
311             return false;
312         }
313         return checkPermissionForDataDelivery(context, READ_MEDIA_VISUAL_USER_SELECTED, pid, uid,
314                 packageName, attributionTag,
315                 generateAppOpMessage(packageName, sOpDescription.get()));
316     }
317 
checkPermissionInstallPackages(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)318     public static boolean checkPermissionInstallPackages(@NonNull Context context, int pid, int uid,
319         @NonNull String packageName, @Nullable String attributionTag) {
320         return checkPermissionForDataDelivery(context, INSTALL_PACKAGES, pid,
321                 uid, packageName, attributionTag, null);
322     }
323 
checkPermissionAccessMtp(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)324     public static boolean checkPermissionAccessMtp(@NonNull Context context, int pid, int uid,
325         @NonNull String packageName, @Nullable String attributionTag) {
326         return checkPermissionForDataDelivery(context, ACCESS_MTP, pid,
327                 uid, packageName, attributionTag, null);
328     }
329 
330     /**
331      * Returns {@code true} if the given package has write images or write video app op, which
332      * indicates the package is a system gallery.
333      */
checkWriteImagesOrVideoAppOps(@onNull Context context, int uid, @NonNull String packageName, @Nullable String attributionTag)334     public static boolean checkWriteImagesOrVideoAppOps(@NonNull Context context, int uid,
335             @NonNull String packageName, @Nullable String attributionTag) {
336         return checkAppOp(
337                 context, OPSTR_WRITE_MEDIA_IMAGES, uid, packageName, attributionTag,
338                 generateAppOpMessage(packageName, sOpDescription.get()))
339                 || checkAppOp(
340                         context, OPSTR_WRITE_MEDIA_VIDEO, uid, packageName, attributionTag,
341                 generateAppOpMessage(packageName, sOpDescription.get()));
342     }
343 
344     /**
345      * Returns {@code true} if any package for the given uid has request_install_packages app op.
346      */
checkAppOpRequestInstallPackagesForSharedUid(@onNull Context context, int uid, @NonNull String[] sharedPackageNames, @Nullable String attributionTag)347     public static boolean checkAppOpRequestInstallPackagesForSharedUid(@NonNull Context context,
348             int uid, @NonNull String[] sharedPackageNames, @Nullable String attributionTag) {
349         for (String packageName : sharedPackageNames) {
350             if (checkAppOp(context, OPSTR_REQUEST_INSTALL_PACKAGES, uid, packageName,
351                     attributionTag, generateAppOpMessage(packageName, sOpDescription.get()))) {
352                 return true;
353             }
354         }
355         return false;
356     }
357 
358     @VisibleForTesting
checkNoIsolatedStorageGranted(@onNull Context context, int uid, @NonNull String packageName, @Nullable String attributionTag)359     static boolean checkNoIsolatedStorageGranted(@NonNull Context context, int uid,
360             @NonNull String packageName, @Nullable String attributionTag) {
361         final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
362         int ret = appOps.noteOpNoThrow(OPSTR_NO_ISOLATED_STORAGE, uid, packageName, attributionTag,
363                 generateAppOpMessage(packageName, "am instrument --no-isolated-storage"));
364         return ret == AppOpsManager.MODE_ALLOWED;
365     }
366 
367     /**
368      * Generates a message to be used with the different {@link AppOpsManager#noteOp} variations.
369      * If the supplied description is {@code null}, the returned message will be {@code null}.
370      */
generateAppOpMessage( @onNull String packageName, @Nullable String description)371     private static String generateAppOpMessage(
372             @NonNull String packageName, @Nullable String description) {
373         if (description == null) {
374             return null;
375         }
376         return "Package: " + packageName + ". Description: " + description + ".";
377     }
378 
379     /**
380      * Similar to {@link #checkPermissionForPreflight(Context, String, int, int, String)},
381      * but also returns true for non-legacy apps.
382      */
checkPermissionAllowingNonLegacy(@onNull Context context, @NonNull String permission, int pid, int uid, @NonNull String packageName)383     private static boolean checkPermissionAllowingNonLegacy(@NonNull Context context,
384             @NonNull String permission, int pid, int uid, @NonNull String packageName) {
385         final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
386 
387         // Allowing non legacy apps to bypass this check
388         if (appOps.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, uid,
389                 packageName) != AppOpsManager.MODE_ALLOWED) return true;
390 
391         // Seems like it's a legacy app, so it has to pass the permission check
392         return checkPermissionForPreflight(context, permission, pid, uid, packageName);
393     }
394 
395     /**
396      * Checks *only* App Ops.
397      */
checkAppOp(@onNull Context context, @NonNull String op, int uid, @NonNull String packageName, @Nullable String attributionTag, @Nullable String opMessage)398     private static boolean checkAppOp(@NonNull Context context,
399             @NonNull String op, int uid, @NonNull String packageName,
400             @Nullable String attributionTag, @Nullable String opMessage) {
401         final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
402         final int mode = appOps.noteOpNoThrow(op, uid, packageName, attributionTag, opMessage);
403         switch (mode) {
404             case AppOpsManager.MODE_ALLOWED:
405                 return true;
406             case AppOpsManager.MODE_DEFAULT:
407             case AppOpsManager.MODE_IGNORED:
408             case AppOpsManager.MODE_ERRORED:
409                 return false;
410             default:
411                 throw new IllegalStateException(op + " has unknown mode " + mode);
412         }
413     }
414 
415 
416     /**
417      * Checks *only* App Ops, also returns true for legacy apps.
418      */
checkAppOpAllowingLegacy(@onNull Context context, @NonNull String op, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, @Nullable String opMessage)419     private static boolean checkAppOpAllowingLegacy(@NonNull Context context,
420             @NonNull String op, int pid, int uid, @NonNull String packageName,
421             @Nullable String attributionTag, @Nullable String opMessage) {
422         final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
423         final int mode = appOps.noteOpNoThrow(op, uid, packageName, attributionTag, opMessage);
424         switch (mode) {
425             case AppOpsManager.MODE_ALLOWED:
426                 return true;
427             case AppOpsManager.MODE_DEFAULT:
428             case AppOpsManager.MODE_IGNORED:
429             case AppOpsManager.MODE_ERRORED:
430                 // Legacy apps technically have the access granted by this op,
431                 // even when the op is denied
432                 if ((appOps.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, uid,
433                         packageName) == AppOpsManager.MODE_ALLOWED)) return true;
434 
435                 return false;
436             default:
437                 throw new IllegalStateException(op + " has unknown mode " + mode);
438         }
439     }
440 
441     /**
442      * Checks whether a given package in a UID and PID has a given permission
443      * and whether the app op that corresponds to this permission is allowed.
444      *
445      * <strong>NOTE:</strong> Use this method only for permission checks at the
446      * preflight point where you will not deliver the permission protected data
447      * to clients but schedule permission data delivery, apps register listeners,
448      * etc.
449      *
450      * <p>For example, if an app registers a location listener it should have the location
451      * permission but no data is actually sent to the app at the moment of registration
452      * and you should use this method to determine if the app has or may have location
453      * permission (if app has only foreground location the grant state depends on the app's
454      * fg/gb state) and this check will not leave a trace that permission protected data
455      * was delivered. When you are about to deliver the location data to a registered
456      * listener you should use {@link #checkPermissionForDataDelivery(Context, String,
457      * int, int, String, String, String)} which will evaluate the permission access based on the
458      * current fg/bg state of the app and leave a record that the data was accessed.
459      *
460      * @param context Context for accessing resources.
461      * @param permission The permission to check.
462      * @param pid The process id for which to check.
463      * @param uid The uid for which to check.
464      * @param packageName The package name for which to check. If null the
465      *     the first package for the calling UID will be used.
466      * @return boolean if permission is {@link #PERMISSION_GRANTED}
467      *
468      * @see #checkPermissionForDataDelivery(Context, String, int, int, String, String, String)
469      */
checkPermissionForPreflight(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName)470     private static boolean checkPermissionForPreflight(@NonNull Context context,
471             @NonNull String permission, int pid, int uid, @Nullable String packageName) {
472         return checkPermissionCommon(context, permission, pid, uid, packageName,
473                 null /*attributionTag*/, null /*message*/,
474                 false /*forDataDelivery*/);
475     }
476 
477     /**
478      * Checks whether a given package in a UID and PID has a given permission
479      * and whether the app op that corresponds to this permission is allowed.
480      *
481      * <strong>NOTE:</strong> Use this method only for permission checks at the
482      * point where you will deliver the permission protected data to clients.
483      *
484      * <p>For example, if an app registers a location listener it should have the location
485      * permission but no data is actually sent to the app at the moment of registration
486      * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)}
487      * to determine if the app has or may have location permission (if app has only foreground
488      * location the grant state depends on the app's fg/gb state) and this check will not
489      * leave a trace that permission protected data was delivered. When you are about to
490      * deliver the location data to a registered listener you should use this method which
491      * will evaluate the permission access based on the current fg/bg state of the app and
492      * leave a record that the data was accessed.
493      *
494      * @param context Context for accessing resources.
495      * @param permission The permission to check.
496      * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
497      *    is not known.
498      * @param uid The uid for which to check.
499      * @param packageName The package name for which to check. If null the
500      *     the first package for the calling UID will be used.
501      * @param attributionTag attribution tag
502      * @return boolean true if {@link #PERMISSION_GRANTED}
503      * @param message A message describing the reason the permission was checked
504      *
505      * @see #checkPermissionForPreflight(Context, String, int, int, String)
506      */
checkPermissionForDataDelivery(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message)507     private static boolean checkPermissionForDataDelivery(@NonNull Context context,
508             @NonNull String permission, int pid, int uid, @Nullable String packageName,
509             @Nullable String attributionTag, @Nullable String message) {
510         return checkPermissionCommon(context, permission, pid, uid, packageName, attributionTag,
511                 message, true /*forDataDelivery*/);
512     }
513 
checkPermissionCommon(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery)514     private static boolean checkPermissionCommon(@NonNull Context context,
515             @NonNull String permission, int pid, int uid, @Nullable String packageName,
516             @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery) {
517         if (packageName == null) {
518             String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
519             if (packageNames != null && packageNames.length > 0) {
520                 packageName = packageNames[0];
521             }
522         }
523 
524         if (isAppOpPermission(permission)) {
525             return checkAppOpPermission(context, permission, pid, uid, packageName, attributionTag,
526                     message, forDataDelivery);
527         }
528         if (isRuntimePermission(permission)) {
529             return checkRuntimePermission(context, permission, pid, uid, packageName,
530                     attributionTag, message, forDataDelivery);
531         }
532 
533         return context.checkPermission(permission, pid, uid) == PERMISSION_GRANTED;
534     }
535 
isAppOpPermission(String permission)536     private static boolean isAppOpPermission(String permission) {
537         switch (permission) {
538             case MANAGE_EXTERNAL_STORAGE:
539             case MANAGE_MEDIA:
540                 return true;
541         }
542         return false;
543     }
544 
isRuntimePermission(String permission)545     private static boolean isRuntimePermission(String permission) {
546         switch (permission) {
547             case ACCESS_MEDIA_LOCATION:
548             case READ_EXTERNAL_STORAGE:
549             case WRITE_EXTERNAL_STORAGE:
550             case READ_MEDIA_AUDIO:
551             case READ_MEDIA_VIDEO:
552             case READ_MEDIA_IMAGES:
553             case READ_MEDIA_VISUAL_USER_SELECTED:
554                 return true;
555         }
556         return false;
557     }
558 
checkAppOpPermission(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery)559     private static boolean checkAppOpPermission(@NonNull Context context,
560             @NonNull String permission, int pid, int uid, @Nullable String packageName,
561             @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery) {
562         final String op = AppOpsManager.permissionToOp(permission);
563         if (op == null || packageName == null) {
564             return false;
565         }
566 
567         final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
568         final int opMode = (forDataDelivery)
569                 ? appOpsManager.noteOpNoThrow(op, uid, packageName, attributionTag, message)
570                 : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
571 
572         switch (opMode) {
573             case AppOpsManager.MODE_ALLOWED:
574             case AppOpsManager.MODE_FOREGROUND:
575                 return true;
576             case AppOpsManager.MODE_DEFAULT:
577                 return context.checkPermission(permission, pid, uid) == PERMISSION_GRANTED;
578             default:
579                 return false;
580         }
581     }
582 
checkRuntimePermission(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery)583     private static boolean checkRuntimePermission(@NonNull Context context,
584             @NonNull String permission, int pid, int uid, @Nullable String packageName,
585             @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery) {
586         if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
587             return false;
588         }
589 
590         final String op = AppOpsManager.permissionToOp(permission);
591         if (op == null || packageName == null) {
592             return true;
593         }
594 
595         final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
596         final int opMode = (forDataDelivery)
597                 ? appOpsManager.noteOpNoThrow(op, uid, packageName, attributionTag, message)
598                 : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
599 
600         switch (opMode) {
601             case AppOpsManager.MODE_ALLOWED:
602             case AppOpsManager.MODE_FOREGROUND:
603                 return true;
604             default:
605                 return false;
606         }
607     }
608 }
609