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