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