• 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.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 for read permission when legacy storage is granted.
153      * There is a bug in AppOpsManager that keeps legacy storage granted even
154      * when an app updates its targetSdkVersion from value <30 to >=30.
155      * If an app upgrades from targetSdk 29 to targetSdk 33, legacy storage
156      * remains granted and in targetSdk 33, app are required to replace R_E_S
157      * with R_M_*. If an app updates its manifest with R_M_*, permission check
158      * in MediaProvider will look for R_E_S and will not grant read access as
159      * the app would be still treated as legacy. Ensure that legacy app either has
160      * R_E_S or all of R_M_* to get read permission. Since this is a fix for legacy
161      * app op bug, we are avoiding granular permission checks based on media type.
162      */
checkPermissionReadForLegacyStorage(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean isTargetSdkAtleastT)163     public static boolean checkPermissionReadForLegacyStorage(@NonNull Context context,
164             int pid, int uid, @NonNull String packageName, @Nullable String attributionTag,
165             boolean isTargetSdkAtleastT) {
166         if (isTargetSdkAtleastT) {
167             return checkPermissionForDataDelivery(context, READ_EXTERNAL_STORAGE, pid, uid,
168                     packageName, attributionTag,
169                     generateAppOpMessage(packageName, sOpDescription.get())) || (
170                     checkPermissionForDataDelivery(context, READ_MEDIA_IMAGES, pid, uid,
171                             packageName, attributionTag,
172                             generateAppOpMessage(packageName, sOpDescription.get()))
173                             && checkPermissionForDataDelivery(context, READ_MEDIA_VIDEO, pid, uid,
174                             packageName, attributionTag,
175                             generateAppOpMessage(packageName, sOpDescription.get()))
176                             && checkPermissionForDataDelivery(context, READ_MEDIA_AUDIO, pid, uid,
177                             packageName, attributionTag,
178                             generateAppOpMessage(packageName, sOpDescription.get())));
179         } else {
180             return checkPermissionForDataDelivery(context, READ_EXTERNAL_STORAGE, pid, uid,
181                     packageName, attributionTag,
182                     generateAppOpMessage(packageName, sOpDescription.get()));
183         }
184     }
185 
186     /**
187      * Check if the given package has been granted the
188      * android.Manifest.permission#ACCESS_MEDIA_LOCATION permission.
189      */
checkPermissionAccessMediaLocation(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean isTargetSdkAtLeastT)190     public static boolean checkPermissionAccessMediaLocation(@NonNull Context context, int pid,
191             int uid, @NonNull String packageName, @Nullable String attributionTag,
192             boolean isTargetSdkAtLeastT) {
193         return checkPermissionForDataDelivery(context, ACCESS_MEDIA_LOCATION, pid, uid, packageName,
194                 attributionTag, generateAppOpMessage(packageName, sOpDescription.get()))
195                 || checkPermissionAccessMediaCompatGrant(context, pid, uid, packageName,
196                 attributionTag, isTargetSdkAtLeastT);
197     }
198 
199     /**
200      *  Check if ACCESS_MEDIA_LOCATION is requested, and that READ_MEDIA_VISUAL_USER_SELECTED is
201      *  implicitly requested and fully granted
202      */
checkPermissionAccessMediaCompatGrant(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean isTargetSdkAtLeastT)203     private static boolean checkPermissionAccessMediaCompatGrant(@NonNull Context context, int pid,
204             int uid, @NonNull String packageName, @Nullable String attributionTag,
205             boolean isTargetSdkAtLeastT) {
206         if (!SdkLevel.isAtLeastU() || !isTargetSdkAtLeastT) {
207             return false;
208         }
209         try {
210             PackageInfo pi = context.getPackageManager().getPackageInfo(packageName,
211                     PackageManager.GET_PERMISSIONS);
212             if (pi.requestedPermissions == null) {
213                 return false;
214             }
215 
216             boolean amlRequested = false;
217             boolean userSelectedImplicit = false;
218             for (int i = 0; i < pi.requestedPermissions.length; i++) {
219                 if (ACCESS_MEDIA_LOCATION.equals(pi.requestedPermissions[i])) {
220                     amlRequested = true;
221                 }
222                 if (READ_MEDIA_VISUAL_USER_SELECTED.equals(pi.requestedPermissions[i])) {
223                     userSelectedImplicit = (pi.requestedPermissionsFlags[i]
224                             & PackageInfo.REQUESTED_PERMISSION_IMPLICIT) != 0;
225                 }
226             }
227 
228             return amlRequested && userSelectedImplicit && checkPermissionReadVisualUserSelected(
229                     context, pid, uid, packageName, attributionTag, isTargetSdkAtLeastT,
230                     /* forDataDelivery */ true);
231         } catch (PackageManager.NameNotFoundException e) {
232             return false;
233         }
234     }
235 
236     /**
237      * Check if the given package has been granted the
238      * android.Manifest.permission#MANAGE_MEDIA permission.
239      */
checkPermissionManageMedia(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)240     public static boolean checkPermissionManageMedia(@NonNull Context context, int pid, int uid,
241             @NonNull String packageName, @Nullable String attributionTag) {
242         return checkPermissionForDataDelivery(context, MANAGE_MEDIA, pid, uid, packageName,
243                 attributionTag, generateAppOpMessage(packageName, sOpDescription.get()));
244     }
245 
checkIsLegacyStorageGranted(@onNull Context context, int uid, String packageName, boolean isTargetSdkAtLeastV)246     public static boolean checkIsLegacyStorageGranted(@NonNull Context context, int uid,
247             String packageName, boolean isTargetSdkAtLeastV) {
248         if (!isTargetSdkAtLeastV && context.getSystemService(AppOpsManager.class)
249                 .unsafeCheckOp(OPSTR_LEGACY_STORAGE, uid, packageName) == MODE_ALLOWED) {
250             return true;
251         }
252         // Check OPSTR_NO_ISOLATED_STORAGE app op.
253         return checkNoIsolatedStorageGranted(context, uid, packageName);
254     }
255 
checkPermissionReadAudio( @onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean targetSdkIsAtLeastT, boolean forDataDelivery)256     public static boolean checkPermissionReadAudio(
257             @NonNull Context context,
258             int pid,
259             int uid,
260             @NonNull String packageName,
261             @Nullable String attributionTag,
262             boolean targetSdkIsAtLeastT,
263             boolean forDataDelivery) {
264 
265         String permission = targetSdkIsAtLeastT && SdkLevel.isAtLeastT()
266                 ? READ_MEDIA_AUDIO : READ_EXTERNAL_STORAGE;
267 
268         if (!checkPermissionForPreflight(context, permission, pid, uid, packageName)) {
269             return false;
270         }
271         return checkAppOpAllowingLegacy(context, OPSTR_READ_MEDIA_AUDIO, pid,
272                 uid, packageName, attributionTag,
273                 generateAppOpMessage(packageName, sOpDescription.get()), forDataDelivery);
274     }
275 
checkPermissionWriteAudio(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean forDataDelivery)276     public static boolean checkPermissionWriteAudio(@NonNull Context context, int pid, int uid,
277             @NonNull String packageName, @Nullable String attributionTag, boolean forDataDelivery) {
278         if (!checkPermissionAllowingNonLegacy(
279                     context, WRITE_EXTERNAL_STORAGE, pid, uid, packageName)) {
280             return false;
281         }
282         return checkAppOpAllowingLegacy(context, OPSTR_WRITE_MEDIA_AUDIO, pid,
283                 uid, packageName, attributionTag,
284                 generateAppOpMessage(packageName, sOpDescription.get()), forDataDelivery);
285     }
286 
checkPermissionReadVideo( @onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean targetSdkIsAtLeastT, boolean forDataDelivery)287     public static boolean checkPermissionReadVideo(
288             @NonNull Context context,
289             int pid,
290             int uid,
291             @NonNull String packageName,
292             @Nullable String attributionTag,
293             boolean targetSdkIsAtLeastT,
294             boolean forDataDelivery) {
295         String permission = targetSdkIsAtLeastT && SdkLevel.isAtLeastT()
296                 ? READ_MEDIA_VIDEO : READ_EXTERNAL_STORAGE;
297 
298         if (!checkPermissionForPreflight(context, permission, pid, uid, packageName)) {
299             return false;
300         }
301 
302         return checkAppOpAllowingLegacy(context, OPSTR_READ_MEDIA_VIDEO, pid,
303                 uid, packageName, attributionTag,
304                 generateAppOpMessage(packageName, sOpDescription.get()), forDataDelivery);
305     }
306 
checkPermissionWriteVideo(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean forDataDelivery)307     public static boolean checkPermissionWriteVideo(@NonNull Context context, int pid, int uid,
308             @NonNull String packageName, @Nullable String attributionTag, boolean forDataDelivery) {
309         if (!checkPermissionAllowingNonLegacy(
310                 context, WRITE_EXTERNAL_STORAGE, pid, uid, packageName)) {
311             return false;
312         }
313         return checkAppOpAllowingLegacy(context, OPSTR_WRITE_MEDIA_VIDEO, pid,
314                 uid, packageName, attributionTag,
315                 generateAppOpMessage(packageName, sOpDescription.get()), forDataDelivery);
316     }
317 
checkPermissionReadImages( @onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean targetSdkIsAtLeastT, boolean forDataDelivery)318     public static boolean checkPermissionReadImages(
319             @NonNull Context context,
320             int pid,
321             int uid,
322             @NonNull String packageName,
323             @Nullable String attributionTag,
324             boolean targetSdkIsAtLeastT, boolean forDataDelivery) {
325         String permission = targetSdkIsAtLeastT && SdkLevel.isAtLeastT()
326                 ? READ_MEDIA_IMAGES : READ_EXTERNAL_STORAGE;
327 
328         if (!checkPermissionForPreflight(context, permission, pid, uid, packageName)) {
329             return false;
330         }
331 
332         return checkAppOpAllowingLegacy(context, OPSTR_READ_MEDIA_IMAGES, pid,
333                 uid, packageName, attributionTag,
334                 generateAppOpMessage(packageName, sOpDescription.get()), forDataDelivery);
335     }
336 
checkPermissionWriteImages(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean forDataDelivery)337     public static boolean checkPermissionWriteImages(@NonNull Context context, int pid, int uid,
338             @NonNull String packageName, @Nullable String attributionTag, boolean forDataDelivery) {
339         if (!checkPermissionAllowingNonLegacy(
340                 context, WRITE_EXTERNAL_STORAGE, pid, uid, packageName)) {
341             return false;
342         }
343         return checkAppOpAllowingLegacy(context, OPSTR_WRITE_MEDIA_IMAGES, pid,
344                 uid, packageName, attributionTag,
345                 generateAppOpMessage(packageName, sOpDescription.get()), forDataDelivery);
346     }
347 
348     /**
349      * Check if the given package has been granted the
350      * android.Manifest.permission#READ_MEDIA_VISUAL_USER_SELECTED permission.
351      */
checkPermissionReadVisualUserSelected( @onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean targetSdkIsAtLeastT, boolean forDataDelivery)352     public static boolean checkPermissionReadVisualUserSelected(
353             @NonNull Context context,
354             int pid,
355             int uid,
356             @NonNull String packageName,
357             @Nullable String attributionTag,
358             boolean targetSdkIsAtLeastT,
359             boolean forDataDelivery) {
360         if (!SdkLevel.isAtLeastU() || !targetSdkIsAtLeastT) {
361             return false;
362         }
363         if (forDataDelivery) {
364             return checkPermissionForDataDelivery(context, READ_MEDIA_VISUAL_USER_SELECTED, pid,
365                     uid,
366                     packageName, attributionTag,
367                     generateAppOpMessage(packageName, sOpDescription.get()));
368         } else {
369             return checkPermissionForPreflight(context, READ_MEDIA_VISUAL_USER_SELECTED, pid,
370                     uid,
371                     packageName);
372         }
373     }
374 
375     /**
376      * Check if the given package has been granted the
377      * android.Manifest.permission#QUERY_ALL_PACKAGES permission.
378      */
checkPermissionQueryAllPackages(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)379     public static boolean checkPermissionQueryAllPackages(@NonNull Context context, int pid,
380             int uid, @NonNull String packageName, @Nullable String attributionTag) {
381         return checkPermissionForDataDelivery(context, QUERY_ALL_PACKAGES, pid,
382                 uid, packageName, attributionTag, null);
383     }
384 
385     /**
386      * Check if the given package has been granted the
387      * android.provider.MediaStore.#ACCESS_MEDIA_OWNER_PACKAGE_NAME_PERMISSION permission.
388      */
checkPermissionAccessMediaOwnerPackageName(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)389     public static boolean checkPermissionAccessMediaOwnerPackageName(@NonNull Context context,
390             int pid, int uid, @NonNull String packageName, @Nullable String attributionTag) {
391         return checkPermissionForDataDelivery(context,
392                 MediaStore.ACCESS_MEDIA_OWNER_PACKAGE_NAME_PERMISSION,
393                 pid, uid, packageName, attributionTag, null);
394     }
395 
396     /**
397      * Check if the given package has been granted the
398      * {@link android.provider.MediaStore#ACCESS_OEM_METADATA_PERMISSION} permission.
399      */
checkPermissionAccessOemMetadata(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)400     public static boolean checkPermissionAccessOemMetadata(@NonNull Context context,
401             int pid, int uid, @NonNull String packageName, @Nullable String attributionTag) {
402         return checkPermissionForDataDelivery(context, MediaStore.ACCESS_OEM_METADATA_PERMISSION,
403                 pid, uid, packageName, attributionTag, null);
404     }
405 
406     /**
407      * Check if the given package has been granted the
408      * {@link android.provider.MediaStore#UPDATE_OEM_METADATA_PERMISSION} permission.
409      */
checkPermissionUpdateOemMetadata(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)410     public static boolean checkPermissionUpdateOemMetadata(@NonNull Context context,
411             int pid, int uid, @NonNull String packageName, @Nullable String attributionTag) {
412         return checkPermissionForPreflight(context, MediaStore.UPDATE_OEM_METADATA_PERMISSION,
413                 pid, uid, packageName);
414     }
415 
checkPermissionInstallPackages(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)416     public static boolean checkPermissionInstallPackages(@NonNull Context context, int pid, int uid,
417         @NonNull String packageName, @Nullable String attributionTag) {
418         return checkPermissionForDataDelivery(context, INSTALL_PACKAGES, pid,
419                 uid, packageName, attributionTag, null);
420     }
421 
checkPermissionAccessMtp(@onNull Context context, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag)422     public static boolean checkPermissionAccessMtp(@NonNull Context context, int pid, int uid,
423         @NonNull String packageName, @Nullable String attributionTag) {
424         return checkPermissionForDataDelivery(context, ACCESS_MTP, pid,
425                 uid, packageName, attributionTag, null);
426     }
427 
428     /**
429      * Returns {@code true} if the given package has write images or write video app op, which
430      * indicates the package is a system gallery.
431      */
checkWriteImagesOrVideoAppOps(@onNull Context context, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean forDataDelivery)432     public static boolean checkWriteImagesOrVideoAppOps(@NonNull Context context, int uid,
433             @NonNull String packageName, @Nullable String attributionTag, boolean forDataDelivery) {
434         return checkAppOp(
435                 context, OPSTR_WRITE_MEDIA_IMAGES, uid, packageName, attributionTag,
436                 generateAppOpMessage(packageName, sOpDescription.get()), forDataDelivery)
437                 || checkAppOp(
438                         context, OPSTR_WRITE_MEDIA_VIDEO, uid, packageName, attributionTag,
439                 generateAppOpMessage(packageName, sOpDescription.get()), forDataDelivery);
440     }
441 
442     /**
443      * Returns {@code true} if any package for the given uid has request_install_packages app op.
444      */
checkAppOpRequestInstallPackagesForSharedUid(@onNull Context context, int uid, @NonNull String[] sharedPackageNames, @Nullable String attributionTag)445     public static boolean checkAppOpRequestInstallPackagesForSharedUid(@NonNull Context context,
446             int uid, @NonNull String[] sharedPackageNames, @Nullable String attributionTag) {
447         for (String packageName : sharedPackageNames) {
448             if (checkAppOp(context, OPSTR_REQUEST_INSTALL_PACKAGES, uid, packageName,
449                     attributionTag, generateAppOpMessage(packageName, sOpDescription.get()),
450                     /*forDataDelivery*/ false)) {
451                 return true;
452             }
453         }
454         return false;
455     }
456 
457     /**
458      * @param context Application context
459      * @param pid calling process ID
460      * @param uid callers UID
461      * @return true if the given uid has MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION permission,
462      * otherwise returns false.
463      */
checkManageCloudMediaProvidersPermission(@onNull Context context, int pid, @UserIdInt int uid)464     public static boolean checkManageCloudMediaProvidersPermission(@NonNull Context context,
465             int pid, @UserIdInt int uid) {
466         return context.checkPermission(
467                 MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION,
468                 pid,
469                 uid
470         ) == PERMISSION_GRANTED;
471     }
472 
473     @VisibleForTesting
checkNoIsolatedStorageGranted(@onNull Context context, int uid, @NonNull String packageName)474     static boolean checkNoIsolatedStorageGranted(@NonNull Context context, int uid,
475             @NonNull String packageName) {
476         final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
477         int ret = appOps.unsafeCheckOpNoThrow(OPSTR_NO_ISOLATED_STORAGE, uid, packageName);
478         return ret == AppOpsManager.MODE_ALLOWED;
479     }
480 
481     /**
482      * Generates a message to be used with the different {@link AppOpsManager#noteOp} variations.
483      * If the supplied description is {@code null}, the returned message will be {@code null}.
484      */
generateAppOpMessage( @onNull String packageName, @Nullable String description)485     private static String generateAppOpMessage(
486             @NonNull String packageName, @Nullable String description) {
487         if (description == null) {
488             return null;
489         }
490         return "Package: " + packageName + ". Description: " + description + ".";
491     }
492 
493     /**
494      * Similar to {@link #checkPermissionForPreflight(Context, String, int, int, String)},
495      * but also returns true for non-legacy apps.
496      */
checkPermissionAllowingNonLegacy(@onNull Context context, @NonNull String permission, int pid, int uid, @NonNull String packageName)497     private static boolean checkPermissionAllowingNonLegacy(@NonNull Context context,
498             @NonNull String permission, int pid, int uid, @NonNull String packageName) {
499         final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
500 
501         // Allowing non legacy apps to bypass this check
502         if (appOps.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, uid,
503                 packageName) != AppOpsManager.MODE_ALLOWED) return true;
504 
505         // Seems like it's a legacy app, so it has to pass the permission check
506         return checkPermissionForPreflight(context, permission, pid, uid, packageName);
507     }
508 
509     /**
510      * Checks *only* App Ops.
511      */
checkAppOp(@onNull Context context, @NonNull String op, int uid, @NonNull String packageName, @Nullable String attributionTag, @Nullable String opMessage, boolean forDataDelivery)512     private static boolean checkAppOp(@NonNull Context context,
513             @NonNull String op, int uid, @NonNull String packageName,
514             @Nullable String attributionTag, @Nullable String opMessage, boolean forDataDelivery) {
515         final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
516         final int mode = forDataDelivery ? appOps.noteOpNoThrow(op, uid, packageName,
517                 attributionTag, opMessage) : appOps.unsafeCheckOpNoThrow(op, uid, packageName);
518         switch (mode) {
519             case AppOpsManager.MODE_ALLOWED:
520                 return true;
521             case AppOpsManager.MODE_DEFAULT:
522             case AppOpsManager.MODE_IGNORED:
523             case AppOpsManager.MODE_ERRORED:
524                 return false;
525             default:
526                 throw new IllegalStateException(op + " has unknown mode " + mode);
527         }
528     }
529 
530 
531     /**
532      * Checks *only* App Ops, also returns true for legacy apps.
533      */
checkAppOpAllowingLegacy(@onNull Context context, @NonNull String op, int pid, int uid, @NonNull String packageName, @Nullable String attributionTag, @Nullable String opMessage, boolean forDataDelivery)534     private static boolean checkAppOpAllowingLegacy(@NonNull Context context,
535             @NonNull String op, int pid, int uid, @NonNull String packageName,
536             @Nullable String attributionTag, @Nullable String opMessage, boolean forDataDelivery) {
537         final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
538         final int mode = forDataDelivery
539                 ? appOps.noteOpNoThrow(op, uid, packageName, attributionTag, opMessage)
540                 : appOps.unsafeCheckOpNoThrow(op, uid, packageName);
541         switch (mode) {
542             case AppOpsManager.MODE_ALLOWED:
543                 return true;
544             case AppOpsManager.MODE_DEFAULT:
545             case AppOpsManager.MODE_IGNORED:
546             case AppOpsManager.MODE_ERRORED:
547                 // Legacy apps technically have the access granted by this op,
548                 // even when the op is denied
549                 if ((appOps.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, uid,
550                         packageName) == AppOpsManager.MODE_ALLOWED)) return true;
551 
552                 return false;
553             default:
554                 throw new IllegalStateException(op + " has unknown mode " + mode);
555         }
556     }
557 
558     /**
559      * Checks whether a given package in a UID and PID has a given permission
560      * and whether the app op that corresponds to this permission is allowed.
561      *
562      * <strong>NOTE:</strong> Use this method only for permission checks at the
563      * preflight point where you will not deliver the permission protected data
564      * to clients but schedule permission data delivery, apps register listeners,
565      * etc.
566      *
567      * <p>For example, if an app registers a location listener it should have the location
568      * permission but no data is actually sent to the app at the moment of registration
569      * and you should use this method to determine if the app has or may have location
570      * permission (if app has only foreground location the grant state depends on the app's
571      * fg/gb state) and this check will not leave a trace that permission protected data
572      * was delivered. When you are about to deliver the location data to a registered
573      * listener you should use {@link #checkPermissionForDataDelivery(Context, String,
574      * int, int, String, String, String)} which will evaluate the permission access based on the
575      * current fg/bg state of the app and leave a record that the data was accessed.
576      *
577      * @param context Context for accessing resources.
578      * @param permission The permission to check.
579      * @param pid The process id for which to check.
580      * @param uid The uid for which to check.
581      * @param packageName The package name for which to check. If null the
582      *     the first package for the calling UID will be used.
583      * @return boolean if permission is {@link #PERMISSION_GRANTED}
584      *
585      * @see #checkPermissionForDataDelivery(Context, String, int, int, String, String, String)
586      */
checkPermissionForPreflight(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName)587     private static boolean checkPermissionForPreflight(@NonNull Context context,
588             @NonNull String permission, int pid, int uid, @Nullable String packageName) {
589         return checkPermissionCommon(context, permission, pid, uid, packageName,
590                 null /*attributionTag*/, null /*message*/,
591                 false /*forDataDelivery*/);
592     }
593 
594     /**
595      * Checks whether a given package in a UID and PID has a given permission
596      * and whether the app op that corresponds to this permission is allowed.
597      *
598      * <strong>NOTE:</strong> Use this method only for permission checks at the
599      * point where you will deliver the permission protected data to clients.
600      *
601      * <p>For example, if an app registers a location listener it should have the location
602      * permission but no data is actually sent to the app at the moment of registration
603      * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)}
604      * to determine if the app has or may have location permission (if app has only foreground
605      * location the grant state depends on the app's fg/gb state) and this check will not
606      * leave a trace that permission protected data was delivered. When you are about to
607      * deliver the location data to a registered listener you should use this method which
608      * will evaluate the permission access based on the current fg/bg state of the app and
609      * leave a record that the data was accessed.
610      *
611      * @param context Context for accessing resources.
612      * @param permission The permission to check.
613      * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
614      *    is not known.
615      * @param uid The uid for which to check.
616      * @param packageName The package name for which to check. If null the
617      *     the first package for the calling UID will be used.
618      * @param attributionTag attribution tag
619      * @return boolean true if {@link #PERMISSION_GRANTED}
620      * @param message A message describing the reason the permission was checked
621      *
622      * @see #checkPermissionForPreflight(Context, String, int, int, String)
623      */
checkPermissionForDataDelivery(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message)624     private static boolean checkPermissionForDataDelivery(@NonNull Context context,
625             @NonNull String permission, int pid, int uid, @Nullable String packageName,
626             @Nullable String attributionTag, @Nullable String message) {
627         return checkPermissionCommon(context, permission, pid, uid, packageName, attributionTag,
628                 message, true /*forDataDelivery*/);
629     }
630 
checkPermissionCommon(@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 checkPermissionCommon(@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 (packageName == null) {
635             String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
636             if (packageNames != null && packageNames.length > 0) {
637                 packageName = packageNames[0];
638             }
639         }
640 
641         if (isAppOpPermission(permission)) {
642             return checkAppOpPermission(context, permission, pid, uid, packageName, attributionTag,
643                     message, forDataDelivery);
644         }
645         if (isRuntimePermission(permission)) {
646             return checkRuntimePermission(context, permission, pid, uid, packageName,
647                     attributionTag, message, forDataDelivery);
648         }
649 
650         return context.checkPermission(permission, pid, uid) == PERMISSION_GRANTED;
651     }
652 
isAppOpPermission(String permission)653     private static boolean isAppOpPermission(String permission) {
654         switch (permission) {
655             case MANAGE_EXTERNAL_STORAGE:
656             case MANAGE_MEDIA:
657                 return true;
658         }
659         return false;
660     }
661 
isRuntimePermission(String permission)662     private static boolean isRuntimePermission(String permission) {
663         switch (permission) {
664             case ACCESS_MEDIA_LOCATION:
665             case READ_EXTERNAL_STORAGE:
666             case WRITE_EXTERNAL_STORAGE:
667             case READ_MEDIA_AUDIO:
668             case READ_MEDIA_VIDEO:
669             case READ_MEDIA_IMAGES:
670             case READ_MEDIA_VISUAL_USER_SELECTED:
671                 return true;
672         }
673         return false;
674     }
675 
checkAppOpPermission(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery)676     private static boolean checkAppOpPermission(@NonNull Context context,
677             @NonNull String permission, int pid, int uid, @Nullable String packageName,
678             @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery) {
679         final String op = AppOpsManager.permissionToOp(permission);
680         if (op == null || packageName == null) {
681             return false;
682         }
683 
684         final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
685         final int opMode = (forDataDelivery)
686                 ? appOpsManager.noteOpNoThrow(op, uid, packageName, attributionTag, message)
687                 : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
688 
689         switch (opMode) {
690             case AppOpsManager.MODE_ALLOWED:
691             case AppOpsManager.MODE_FOREGROUND:
692                 return true;
693             case AppOpsManager.MODE_DEFAULT:
694                 return context.checkPermission(permission, pid, uid) == PERMISSION_GRANTED;
695             default:
696                 return false;
697         }
698     }
699 
checkRuntimePermission(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery)700     private static boolean checkRuntimePermission(@NonNull Context context,
701             @NonNull String permission, int pid, int uid, @Nullable String packageName,
702             @Nullable String attributionTag, @Nullable String message, boolean forDataDelivery) {
703         if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
704             return false;
705         }
706 
707         final String op = AppOpsManager.permissionToOp(permission);
708         if (op == null || packageName == null) {
709             return true;
710         }
711 
712         final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
713         final int opMode = (forDataDelivery)
714                 ? appOpsManager.noteOpNoThrow(op, uid, packageName, attributionTag, message)
715                 : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
716 
717         switch (opMode) {
718             case AppOpsManager.MODE_ALLOWED:
719             case AppOpsManager.MODE_FOREGROUND:
720                 return true;
721             default:
722                 return false;
723         }
724     }
725 }
726