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