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; 18 19 import static com.android.providers.media.util.DatabaseUtils.bindList; 20 import static com.android.providers.media.util.Logging.TAG; 21 import static com.android.providers.media.util.PermissionUtils.checkAppOpRequestInstallPackagesForSharedUid; 22 import static com.android.providers.media.util.PermissionUtils.checkIsLegacyStorageGranted; 23 import static com.android.providers.media.util.PermissionUtils.checkPermissionAccessMediaLocation; 24 import static com.android.providers.media.util.PermissionUtils.checkPermissionAccessMediaOwnerPackageName; 25 import static com.android.providers.media.util.PermissionUtils.checkPermissionAccessMtp; 26 import static com.android.providers.media.util.PermissionUtils.checkPermissionDelegator; 27 import static com.android.providers.media.util.PermissionUtils.checkPermissionInstallPackages; 28 import static com.android.providers.media.util.PermissionUtils.checkPermissionManager; 29 import static com.android.providers.media.util.PermissionUtils.checkPermissionQueryAllPackages; 30 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadAudio; 31 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadImages; 32 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadStorage; 33 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadVideo; 34 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadVisualUserSelected; 35 import static com.android.providers.media.util.PermissionUtils.checkPermissionSelf; 36 import static com.android.providers.media.util.PermissionUtils.checkPermissionShell; 37 import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteAudio; 38 import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteImages; 39 import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteStorage; 40 import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteVideo; 41 import static com.android.providers.media.util.PermissionUtils.checkWriteImagesOrVideoAppOps; 42 43 import android.annotation.Nullable; 44 import android.app.AppOpsManager; 45 import android.app.compat.CompatChanges; 46 import android.compat.annotation.ChangeId; 47 import android.compat.annotation.EnabledAfter; 48 import android.compat.annotation.EnabledSince; 49 import android.content.ContentProvider; 50 import android.content.Context; 51 import android.content.pm.ApplicationInfo; 52 import android.content.pm.PackageManager.NameNotFoundException; 53 import android.os.Binder; 54 import android.os.Build; 55 import android.os.Process; 56 import android.os.SystemProperties; 57 import android.os.UserHandle; 58 import android.os.UserManager; 59 import android.provider.MediaStore.Files.FileColumns; 60 import android.util.ArrayMap; 61 import android.util.Log; 62 63 import androidx.annotation.GuardedBy; 64 import androidx.annotation.NonNull; 65 import androidx.annotation.VisibleForTesting; 66 67 import com.android.modules.utils.build.SdkLevel; 68 import com.android.providers.media.util.Logging; 69 import com.android.providers.media.util.LongArray; 70 import com.android.providers.media.util.UserCache; 71 72 import java.io.PrintWriter; 73 import java.util.Locale; 74 75 public class LocalCallingIdentity { 76 77 public final int pid; 78 public final int uid; 79 private final UserHandle user; 80 private final Context context; 81 private final String packageNameUnchecked; 82 // Info used for logging permission checks 83 private final @Nullable String attributionTag; 84 private final Object lock = new Object(); 85 86 @GuardedBy("lock") 87 private int[] mDeletedFileCountsBypassingDatabase = new int[FileColumns.MEDIA_TYPE_COUNT]; 88 LocalCallingIdentity(Context context, int pid, int uid, UserHandle user, String packageNameUnchecked, @Nullable String attributionTag)89 private LocalCallingIdentity(Context context, int pid, int uid, UserHandle user, 90 String packageNameUnchecked, @Nullable String attributionTag) { 91 this.context = context; 92 this.pid = pid; 93 this.uid = uid; 94 this.user = user; 95 this.packageNameUnchecked = packageNameUnchecked; 96 this.attributionTag = attributionTag; 97 } 98 99 /** 100 * See definition in {@link android.os.Environment} 101 */ 102 private static final long DEFAULT_SCOPED_STORAGE = 149924527L; 103 104 /** 105 * See definition in {@link android.os.Environment} 106 */ 107 private static final long FORCE_ENABLE_SCOPED_STORAGE = 132649864L; 108 109 private static final long UNKNOWN_ROW_ID = -1; 110 fromBinder(Context context, ContentProvider provider, UserCache userCache)111 public static LocalCallingIdentity fromBinder(Context context, ContentProvider provider, 112 UserCache userCache) { 113 String callingPackage = provider.getCallingPackageUnchecked(); 114 int binderUid = Binder.getCallingUid(); 115 if (callingPackage == null) { 116 if (binderUid == Process.SYSTEM_UID || binderUid == Process.myUid()) { 117 // If UID is system assume we are running as ourself and not handling IPC 118 // Otherwise, we'd crash when we attempt AppOpsManager#checkPackage 119 // in LocalCallingIdentity#getPackageName 120 return fromSelf(context); 121 } 122 // Package will be resolved during getPackageNameInternal() 123 callingPackage = null; 124 } 125 String callingAttributionTag = provider.getCallingAttributionTag(); 126 if (callingAttributionTag == null) { 127 callingAttributionTag = context.getAttributionTag(); 128 } 129 UserHandle user; 130 if (binderUid == Process.SHELL_UID || binderUid == Process.ROOT_UID) { 131 // For requests coming from the shell (eg `content query`), assume they are 132 // for the user we are running as. 133 user = Process.myUserHandle(); 134 } else { 135 user = UserHandle.getUserHandleForUid(binderUid); 136 } 137 // We need to use the cached variant here, because the uncached version may 138 // make a binder transaction, which would cause infinite recursion here. 139 // Using the cached variant is fine, because we shouldn't be getting any binder 140 // requests for this volume before it has been mounted anyway, at which point 141 // we must already know about the new user. 142 if (!userCache.userSharesMediaWithParentCached(user)) { 143 // It's possible that we got a cross-profile intent from a regular work profile; in 144 // that case, the request was explicitly targeted at the media database of the owner 145 // user; reflect that here. 146 user = Process.myUserHandle(); 147 } 148 return new LocalCallingIdentity(context, Binder.getCallingPid(), binderUid, 149 user, callingPackage, callingAttributionTag); 150 } 151 fromExternal(Context context, @Nullable UserCache userCache, int uid)152 public static LocalCallingIdentity fromExternal(Context context, @Nullable UserCache userCache, 153 int uid) { 154 final String[] sharedPackageNames = context.getPackageManager().getPackagesForUid(uid); 155 if (sharedPackageNames == null || sharedPackageNames.length == 0) { 156 throw new IllegalArgumentException("UID " + uid + " has no associated package"); 157 } 158 LocalCallingIdentity ident = fromExternal(context, userCache, uid, sharedPackageNames[0], 159 null); 160 ident.sharedPackageNames = sharedPackageNames; 161 ident.sharedPackageNamesResolved = true; 162 if (uid == Process.SHELL_UID) { 163 // This is useful for debugging/testing/development 164 if (SystemProperties.getBoolean("persist.sys.fuse.shell.redaction-needed", false)) { 165 ident.hasPermission |= PERMISSION_IS_REDACTION_NEEDED; 166 ident.hasPermissionResolved = PERMISSION_IS_REDACTION_NEEDED; 167 } 168 } 169 170 return ident; 171 } 172 fromExternal(Context context, @Nullable UserCache userCache, int uid, String packageName, @Nullable String attributionTag)173 public static LocalCallingIdentity fromExternal(Context context, @Nullable UserCache userCache, 174 int uid, String packageName, @Nullable String attributionTag) { 175 UserHandle user = UserHandle.getUserHandleForUid(uid); 176 if (userCache != null && !userCache.userSharesMediaWithParentCached(user)) { 177 // This can happen on some proprietary app clone solutions, where the owner 178 // and clone user each have their own MediaProvider instance, but refer to 179 // each other for cross-user file access through the use of bind mounts. 180 // In this case, assume the access is for the owner user, since that is 181 // the only user for which we manage volumes anyway. 182 user = Process.myUserHandle(); 183 } 184 return new LocalCallingIdentity(context, -1, uid, user, packageName, attributionTag); 185 } 186 fromSelf(Context context)187 public static LocalCallingIdentity fromSelf(Context context) { 188 return fromSelfAsUser(context, Process.myUserHandle()); 189 } 190 fromSelfAsUser(Context context, UserHandle user)191 public static LocalCallingIdentity fromSelfAsUser(Context context, UserHandle user) { 192 final LocalCallingIdentity ident = new LocalCallingIdentity( 193 context, 194 android.os.Process.myPid(), 195 android.os.Process.myUid(), 196 user, 197 context.getOpPackageName(), 198 context.getAttributionTag()); 199 200 ident.packageName = ident.packageNameUnchecked; 201 ident.packageNameResolved = true; 202 // Use ident.attributionTag from context, hence no change 203 ident.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; 204 ident.targetSdkVersionResolved = true; 205 ident.shouldBypass = false; 206 ident.shouldBypassResolved = true; 207 ident.hasPermission = ~(PERMISSION_IS_LEGACY_GRANTED | PERMISSION_IS_LEGACY_WRITE 208 | PERMISSION_IS_LEGACY_READ | PERMISSION_IS_REDACTION_NEEDED 209 | PERMISSION_IS_SHELL | PERMISSION_IS_DELEGATOR); 210 ident.hasPermissionResolved = ~0; 211 return ident; 212 } 213 214 /** 215 * Returns mocked {@link LocalCallingIdentity} for testing 216 */ 217 @VisibleForTesting forTest(Context context, int uid, int permission)218 public static LocalCallingIdentity forTest(Context context, int uid, int permission) { 219 final String[] sharedPackageNames = context.getPackageManager().getPackagesForUid(uid); 220 if (sharedPackageNames == null || sharedPackageNames.length == 0) { 221 throw new IllegalArgumentException("UID " + uid + " has no associated package"); 222 } 223 LocalCallingIdentity ident = new LocalCallingIdentity(context, -1, uid, 224 Process.myUserHandle(), sharedPackageNames[0], null); 225 ident.sharedPackageNames = sharedPackageNames; 226 ident.sharedPackageNamesResolved = true; 227 ident.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; 228 ident.targetSdkVersionResolved = true; 229 ident.shouldBypass = false; 230 ident.shouldBypassResolved = true; 231 ident.hasPermission = permission; 232 ident.hasPermissionResolved = ~0; 233 return ident; 234 } 235 236 private volatile String packageName; 237 private volatile boolean packageNameResolved; 238 getPackageName()239 public String getPackageName() { 240 if (!packageNameResolved) { 241 packageName = getPackageNameInternal(); 242 packageNameResolved = true; 243 } 244 return packageName; 245 } 246 isValidProviderOrFuseCallingIdentity()247 public boolean isValidProviderOrFuseCallingIdentity() { 248 return packageNameUnchecked != null; 249 } 250 getPackageNameInternal()251 private String getPackageNameInternal() { 252 // TODO(b/263480773): The packageNameUnchecked can be null when 253 // ContentProvider#getCallingPackageUnchecked returns null and the binder UID is not system 254 // or MediaProvider. In such scenarios, previously an exception was thrown in the 255 // checkPackage() call below. This was fixed for b/261444895 however, we still need to 256 // investigate if we should explicitly throw an exception in such cases. 257 if (packageNameUnchecked == null) { 258 return context.getPackageManager().getNameForUid(uid); 259 } 260 // Verify that package name is actually owned by UID 261 context.getSystemService(AppOpsManager.class) 262 .checkPackage(uid, packageNameUnchecked); 263 return packageNameUnchecked; 264 } 265 266 private volatile String[] sharedPackageNames; 267 private volatile boolean sharedPackageNamesResolved; 268 269 /** 270 * Returns an array of package names that share the {@code uid} 271 */ getSharedPackageNamesArray()272 public String[] getSharedPackageNamesArray() { 273 if (!sharedPackageNamesResolved) { 274 sharedPackageNames = getSharedPackageNamesListInternal(); 275 sharedPackageNamesResolved = true; 276 } 277 return sharedPackageNames; 278 } 279 280 /** 281 * Returns comma separated string of package names that share the {@code uid} 282 */ getSharedPackagesAsString()283 public String getSharedPackagesAsString() { 284 final String[] sharedPackageNames = getSharedPackageNamesArray(); 285 return bindList((Object[]) sharedPackageNames); 286 } 287 getSharedPackageNamesListInternal()288 private String[] getSharedPackageNamesListInternal() { 289 final String[] packageNames = context.getPackageManager().getPackagesForUid(uid); 290 return (packageNames != null) ? packageNames : new String[0]; 291 } 292 293 private volatile int targetSdkVersion; 294 private volatile boolean targetSdkVersionResolved; 295 getTargetSdkVersion()296 public int getTargetSdkVersion() { 297 if (!targetSdkVersionResolved) { 298 targetSdkVersion = getTargetSdkVersionInternal(); 299 targetSdkVersionResolved = true; 300 } 301 return targetSdkVersion; 302 } 303 getTargetSdkVersionInternal()304 private int getTargetSdkVersionInternal() { 305 try { 306 final ApplicationInfo ai = context.getPackageManager() 307 .getApplicationInfo(getPackageName(), 0); 308 if (ai != null) { 309 return ai.targetSdkVersion; 310 } 311 } catch (NameNotFoundException ignored) { 312 } 313 return Build.VERSION_CODES.CUR_DEVELOPMENT; 314 } 315 getUser()316 public UserHandle getUser() { 317 return user; 318 } 319 320 public static final int PERMISSION_IS_SELF = 1 << 0; 321 public static final int PERMISSION_IS_SHELL = 1 << 1; 322 public static final int PERMISSION_IS_MANAGER = 1 << 2; 323 public static final int PERMISSION_IS_DELEGATOR = 1 << 3; 324 325 public static final int PERMISSION_IS_REDACTION_NEEDED = 1 << 8; 326 public static final int PERMISSION_IS_LEGACY_GRANTED = 1 << 9; 327 public static final int PERMISSION_IS_LEGACY_READ = 1 << 10; 328 public static final int PERMISSION_IS_LEGACY_WRITE = 1 << 11; 329 330 public static final int PERMISSION_READ_AUDIO = 1 << 16; 331 public static final int PERMISSION_READ_VIDEO = 1 << 17; 332 public static final int PERMISSION_READ_IMAGES = 1 << 18; 333 public static final int PERMISSION_WRITE_AUDIO = 1 << 19; 334 public static final int PERMISSION_WRITE_VIDEO = 1 << 20; 335 public static final int PERMISSION_WRITE_IMAGES = 1 << 21; 336 337 public static final int PERMISSION_IS_SYSTEM_GALLERY = 1 << 22; 338 /** 339 * Explicitly checks **only** for INSTALL_PACKAGES runtime permission. 340 */ 341 public static final int PERMISSION_INSTALL_PACKAGES = 1 << 23; 342 public static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 1 << 24; 343 344 /** 345 * Checks if REQUEST_INSTALL_PACKAGES app-op is allowed for any package sharing this UID. 346 */ 347 public static final int APPOP_REQUEST_INSTALL_PACKAGES_FOR_SHARED_UID = 1 << 25; 348 public static final int PERMISSION_ACCESS_MTP = 1 << 26; 349 350 public static final int PERMISSION_READ_MEDIA_VISUAL_USER_SELECTED = 1 << 27; 351 352 public static final int PERMISSION_QUERY_ALL_PACKAGES = 1 << 28; 353 public static final int PERMISSION_ACCESS_MEDIA_OWNER_PACKAGE_NAME = 1 << 29; 354 355 private volatile int hasPermission; 356 private volatile int hasPermissionResolved; 357 hasPermission(int permission)358 public boolean hasPermission(int permission) { 359 if ((hasPermissionResolved & permission) == 0) { 360 if (hasPermissionInternal(permission)) { 361 hasPermission |= permission; 362 } 363 hasPermissionResolved |= permission; 364 } 365 return (hasPermission & permission) != 0; 366 } 367 hasPermissionInternal(int permission)368 private boolean hasPermissionInternal(int permission) { 369 boolean targetSdkIsAtLeastT = getTargetSdkVersion() > Build.VERSION_CODES.S_V2; 370 // While we're here, enforce any broad user-level restrictions 371 if ((uid == Process.SHELL_UID) && context.getSystemService(UserManager.class) 372 .hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) { 373 throw new SecurityException( 374 "Shell user cannot access files for user " + UserHandle.myUserId()); 375 } 376 377 switch (permission) { 378 case PERMISSION_IS_SELF: 379 return checkPermissionSelf(context, pid, uid); 380 case PERMISSION_IS_SHELL: 381 return checkPermissionShell(uid); 382 case PERMISSION_IS_MANAGER: 383 return checkPermissionManager(context, pid, uid, getPackageName(), attributionTag); 384 case PERMISSION_IS_DELEGATOR: 385 return checkPermissionDelegator(context, pid, uid); 386 387 case PERMISSION_IS_REDACTION_NEEDED: 388 return isRedactionNeededInternal(targetSdkIsAtLeastT); 389 case PERMISSION_IS_LEGACY_GRANTED: 390 return isLegacyStorageGranted(); 391 case PERMISSION_IS_LEGACY_READ: 392 return isLegacyReadInternal(); 393 case PERMISSION_IS_LEGACY_WRITE: 394 return isLegacyWriteInternal(); 395 396 case PERMISSION_WRITE_EXTERNAL_STORAGE: 397 return checkPermissionWriteStorage( 398 context, pid, uid, getPackageName(), attributionTag); 399 400 case PERMISSION_READ_AUDIO: 401 return checkPermissionReadAudio( 402 context, pid, uid, getPackageName(), attributionTag, targetSdkIsAtLeastT); 403 case PERMISSION_READ_VIDEO: 404 return checkPermissionReadVideo( 405 context, pid, uid, getPackageName(), attributionTag, targetSdkIsAtLeastT); 406 case PERMISSION_READ_IMAGES: 407 return checkPermissionReadImages( 408 context, pid, uid, getPackageName(), attributionTag, targetSdkIsAtLeastT); 409 case PERMISSION_WRITE_AUDIO: 410 return checkPermissionWriteAudio( 411 context, pid, uid, getPackageName(), attributionTag); 412 case PERMISSION_WRITE_VIDEO: 413 return checkPermissionWriteVideo( 414 context, pid, uid, getPackageName(), attributionTag); 415 case PERMISSION_WRITE_IMAGES: 416 return checkPermissionWriteImages( 417 context, pid, uid, getPackageName(), attributionTag); 418 case PERMISSION_IS_SYSTEM_GALLERY: 419 return checkWriteImagesOrVideoAppOps( 420 context, uid, getPackageName(), attributionTag); 421 case PERMISSION_INSTALL_PACKAGES: 422 return checkPermissionInstallPackages( 423 context, pid, uid, getPackageName(), attributionTag); 424 case APPOP_REQUEST_INSTALL_PACKAGES_FOR_SHARED_UID: 425 return checkAppOpRequestInstallPackagesForSharedUid( 426 context, uid, getSharedPackageNamesArray(), attributionTag); 427 case PERMISSION_ACCESS_MTP: 428 return checkPermissionAccessMtp( 429 context, pid, uid, getPackageName(), attributionTag); 430 case PERMISSION_READ_MEDIA_VISUAL_USER_SELECTED: 431 return checkPermissionReadVisualUserSelected(context, pid, uid, getPackageName(), 432 attributionTag, targetSdkIsAtLeastT); 433 case PERMISSION_QUERY_ALL_PACKAGES: 434 return checkPermissionQueryAllPackages( 435 context, pid, uid, getPackageName(), attributionTag); 436 case PERMISSION_ACCESS_MEDIA_OWNER_PACKAGE_NAME: 437 return checkPermissionAccessMediaOwnerPackageName( 438 context, pid, uid, getPackageName(), attributionTag); 439 default: 440 return false; 441 } 442 } 443 isLegacyStorageGranted()444 private boolean isLegacyStorageGranted() { 445 boolean defaultScopedStorage = CompatChanges.isChangeEnabled( 446 DEFAULT_SCOPED_STORAGE, getPackageName(), UserHandle.getUserHandleForUid(uid)); 447 boolean forceEnableScopedStorage = CompatChanges.isChangeEnabled( 448 FORCE_ENABLE_SCOPED_STORAGE, getPackageName(), UserHandle.getUserHandleForUid(uid)); 449 450 // if Scoped Storage is strictly enforced, the app does *not* have legacy storage access 451 if (isScopedStorageEnforced(defaultScopedStorage, forceEnableScopedStorage)) { 452 return false; 453 } 454 // if Scoped Storage is strictly disabled, the app has legacy storage access 455 if (isScopedStorageDisabled(defaultScopedStorage, forceEnableScopedStorage)) { 456 return true; 457 } 458 459 // To address b/338519249, we will check for sdk version V+ 460 boolean targetSdkIsAtLeastV = 461 getTargetSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM; 462 return checkIsLegacyStorageGranted(context, uid, getPackageName(), attributionTag, 463 targetSdkIsAtLeastV); 464 } 465 466 private volatile boolean shouldBypass; 467 private volatile boolean shouldBypassResolved; 468 469 /** 470 * Allow apps holding {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} 471 * permission to request raw external storage access. 472 */ 473 @ChangeId 474 @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) 475 static final long ENABLE_RAW_MANAGE_EXTERNAL_STORAGE_ACCESS = 178209446L; 476 477 /** 478 * Allow apps holding {@link android.app.role}#SYSTEM_GALLERY role to request raw external 479 * storage access. 480 */ 481 @ChangeId 482 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.R) 483 static final long ENABLE_RAW_SYSTEM_GALLERY_ACCESS = 183372781L; 484 485 /** 486 * Checks if app chooses to bypass database operations. 487 * 488 * <p> 489 * Note that this method doesn't check if app qualifies to bypass database operations. 490 * 491 * @return {@code true} if AndroidManifest.xml of this app has 492 * android:requestRawExternalStorageAccess=true 493 * {@code false} otherwise. 494 */ shouldBypassDatabase(boolean isSystemGallery)495 public boolean shouldBypassDatabase(boolean isSystemGallery) { 496 if (!shouldBypassResolved) { 497 shouldBypass = shouldBypassDatabaseInternal(isSystemGallery); 498 shouldBypassResolved = true; 499 } 500 return shouldBypass; 501 } 502 shouldBypassDatabaseInternal(boolean isSystemGallery)503 private boolean shouldBypassDatabaseInternal(boolean isSystemGallery) { 504 if (!SdkLevel.isAtLeastS()) { 505 // We need to parse the manifest flag ourselves here. 506 // TODO(b/178209446): Parse app manifest to get new flag values 507 return true; 508 } 509 510 final ApplicationInfo ai; 511 try { 512 ai = context.getPackageManager() 513 .getApplicationInfo(getPackageName(), 0); 514 if (ai != null) { 515 final int requestRawExternalStorageValue 516 = ai.getRequestRawExternalStorageAccess(); 517 if (requestRawExternalStorageValue 518 != ApplicationInfo.RAW_EXTERNAL_STORAGE_ACCESS_DEFAULT) { 519 return requestRawExternalStorageValue 520 == ApplicationInfo.RAW_EXTERNAL_STORAGE_ACCESS_REQUESTED; 521 } 522 // Manifest flag is not set, hence return default value based on the category of the 523 // app and targetSDK. 524 if (isSystemGallery) { 525 if (CompatChanges.isChangeEnabled( 526 ENABLE_RAW_SYSTEM_GALLERY_ACCESS, uid)) { 527 // If systemGallery, then the flag will default to false when they are 528 // targeting targetSDK>=30. 529 return false; 530 } 531 } else if (CompatChanges.isChangeEnabled( 532 ENABLE_RAW_MANAGE_EXTERNAL_STORAGE_ACCESS, uid)) { 533 // If app has MANAGE_EXTERNAL_STORAGE, the flag will default to false when they 534 // are targeting targetSDK>=31. 535 return false; 536 } 537 } 538 } catch (NameNotFoundException e) { 539 } 540 return true; 541 } 542 isScopedStorageEnforced(boolean defaultScopedStorage, boolean forceEnableScopedStorage)543 private boolean isScopedStorageEnforced(boolean defaultScopedStorage, 544 boolean forceEnableScopedStorage) { 545 return defaultScopedStorage && forceEnableScopedStorage; 546 } 547 isScopedStorageDisabled(boolean defaultScopedStorage, boolean forceEnableScopedStorage)548 private boolean isScopedStorageDisabled(boolean defaultScopedStorage, 549 boolean forceEnableScopedStorage) { 550 return !defaultScopedStorage && !forceEnableScopedStorage; 551 } 552 isLegacyWriteInternal()553 private boolean isLegacyWriteInternal() { 554 return hasPermission(PERMISSION_IS_LEGACY_GRANTED) 555 && checkPermissionWriteStorage(context, pid, uid, getPackageName(), attributionTag); 556 } 557 isLegacyReadInternal()558 private boolean isLegacyReadInternal() { 559 return hasPermission(PERMISSION_IS_LEGACY_GRANTED) 560 && checkPermissionReadStorage(context, pid, uid, getPackageName(), attributionTag); 561 } 562 563 /** System internals or callers holding permission have no redaction */ isRedactionNeededInternal(boolean isTargetSdkAtLeastT)564 private boolean isRedactionNeededInternal(boolean isTargetSdkAtLeastT) { 565 if (hasPermission(PERMISSION_IS_SELF) || hasPermission(PERMISSION_IS_SHELL)) { 566 return false; 567 } 568 569 return !checkPermissionAccessMediaLocation(context, pid, uid, getPackageName(), 570 attributionTag, isTargetSdkAtLeastT); 571 } 572 573 @GuardedBy("lock") 574 private final LongArray ownedIds = new LongArray(); 575 isOwned(long id)576 public boolean isOwned(long id) { 577 synchronized (lock) { 578 return ownedIds.indexOf(id) != -1; 579 } 580 } 581 setOwned(long id, boolean owned)582 public void setOwned(long id, boolean owned) { 583 synchronized (lock) { 584 final int index = ownedIds.indexOf(id); 585 if (owned) { 586 if (index == -1) { 587 ownedIds.add(id); 588 } 589 } else { 590 if (index != -1) { 591 ownedIds.remove(index); 592 } 593 } 594 } 595 } 596 597 @GuardedBy("lock") 598 private final ArrayMap<String, Long> rowIdOfDeletedPaths = new ArrayMap<>(); 599 addDeletedRowId(@onNull String path, long id)600 public void addDeletedRowId(@NonNull String path, long id) { 601 synchronized (lock) { 602 rowIdOfDeletedPaths.put(path.toLowerCase(Locale.ROOT), id); 603 } 604 } 605 removeDeletedRowId(long id)606 public boolean removeDeletedRowId(long id) { 607 synchronized (lock) { 608 int index = rowIdOfDeletedPaths.indexOfValue(id); 609 final boolean isDeleted = index > -1; 610 while (index > -1) { 611 rowIdOfDeletedPaths.removeAt(index); 612 index = rowIdOfDeletedPaths.indexOfValue(id); 613 } 614 return isDeleted; 615 } 616 } 617 getDeletedRowId(@onNull String path)618 public long getDeletedRowId(@NonNull String path) { 619 synchronized (lock) { 620 return rowIdOfDeletedPaths.getOrDefault(path.toLowerCase(Locale.ROOT), UNKNOWN_ROW_ID); 621 } 622 } 623 incrementDeletedFileCountBypassingDatabase(int mediaType)624 protected void incrementDeletedFileCountBypassingDatabase(int mediaType) { 625 synchronized (lock) { 626 mDeletedFileCountsBypassingDatabase[mediaType]++; 627 } 628 } 629 clearDeletedFileCountsBypassingDatabase()630 private void clearDeletedFileCountsBypassingDatabase() { 631 synchronized (lock) { 632 for (int i = 0; i < FileColumns.MEDIA_TYPE_COUNT; i++) { 633 mDeletedFileCountsBypassingDatabase[i] = 0; 634 } 635 } 636 } 637 getDeletedFileTotalCountBypassingDatabase()638 protected int getDeletedFileTotalCountBypassingDatabase() { 639 synchronized (lock) { 640 int totalCount = 0; 641 for (int i = 0; i < FileColumns.MEDIA_TYPE_COUNT; i++) { 642 totalCount += mDeletedFileCountsBypassingDatabase[i]; 643 } 644 return totalCount; 645 } 646 } 647 hasDeletedFileCount()648 protected boolean hasDeletedFileCount() { 649 synchronized (lock) { 650 for (int i = 0; i < FileColumns.MEDIA_TYPE_COUNT; i++) { 651 if (mDeletedFileCountsBypassingDatabase[i] > 0) return true; 652 } 653 return false; 654 } 655 } 656 getDeletedFileCountsBypassingDatabase()657 protected int[] getDeletedFileCountsBypassingDatabase() { 658 synchronized (lock) { 659 return mDeletedFileCountsBypassingDatabase; 660 } 661 } 662 663 private volatile int applicationMediaCapabilitiesSupportedFlags = -1; 664 private volatile int applicationMediaCapabilitiesUnsupportedFlags = -1; 665 getApplicationMediaCapabilitiesSupportedFlags()666 public int getApplicationMediaCapabilitiesSupportedFlags() { 667 return applicationMediaCapabilitiesSupportedFlags; 668 } 669 getApplicationMediaCapabilitiesUnsupportedFlags()670 public int getApplicationMediaCapabilitiesUnsupportedFlags() { 671 return applicationMediaCapabilitiesUnsupportedFlags; 672 } 673 setApplicationMediaCapabilitiesFlags(int supportedFlags, int unsupportedFlags)674 public void setApplicationMediaCapabilitiesFlags(int supportedFlags, int unsupportedFlags) { 675 applicationMediaCapabilitiesSupportedFlags = supportedFlags; 676 applicationMediaCapabilitiesUnsupportedFlags = unsupportedFlags; 677 } 678 679 /** 680 * Returns {@code true} if this package has Audio read/write permissions. 681 */ checkCallingPermissionAudio(boolean forWrite)682 public boolean checkCallingPermissionAudio(boolean forWrite) { 683 if (forWrite) { 684 return hasPermission(PERMISSION_WRITE_AUDIO); 685 } else { 686 // write permission should be enough for reading as well 687 return hasPermission(PERMISSION_READ_AUDIO) 688 || hasPermission(PERMISSION_WRITE_AUDIO); 689 } 690 } 691 692 /** 693 * Returns {@code true} if this package has Video read/write permissions. 694 */ checkCallingPermissionVideo(boolean forWrite)695 public boolean checkCallingPermissionVideo(boolean forWrite) { 696 if (forWrite) { 697 return hasPermission(PERMISSION_WRITE_VIDEO); 698 } else { 699 // write permission should be enough for reading as well 700 return hasPermission(PERMISSION_READ_VIDEO) || hasPermission(PERMISSION_WRITE_VIDEO); 701 } 702 } 703 704 /** 705 * Returns {@code true} if this package has Image read/write permissions. 706 */ checkCallingPermissionImages(boolean forWrite)707 public boolean checkCallingPermissionImages(boolean forWrite) { 708 if (forWrite) { 709 return hasPermission(PERMISSION_WRITE_IMAGES); 710 } else { 711 // write permission should be enough for reading as well 712 return hasPermission(PERMISSION_READ_IMAGES) || hasPermission(PERMISSION_WRITE_IMAGES); 713 } 714 } 715 716 /** 717 * Returns {@code true} if this package has permissions 718 * to access owner_package_name of any accessible file. 719 */ checkCallingPermissionsOwnerPackageName()720 public boolean checkCallingPermissionsOwnerPackageName() { 721 return hasPermission(PERMISSION_QUERY_ALL_PACKAGES) 722 || hasPermission(PERMISSION_ACCESS_MEDIA_OWNER_PACKAGE_NAME); 723 } 724 725 /** 726 * Returns {@code true} if this package is a legacy app and has read permission 727 */ isCallingPackageLegacyRead()728 public boolean isCallingPackageLegacyRead() { 729 return hasPermission(PERMISSION_IS_LEGACY_READ); 730 } 731 732 /** 733 * Returns {@code true} if this package is a legacy app and has write permission 734 */ isCallingPackageLegacyWrite()735 public boolean isCallingPackageLegacyWrite() { 736 return hasPermission(PERMISSION_IS_LEGACY_WRITE); 737 } 738 739 /** 740 * Return {@code true} if this package has user selected access on images/videos. 741 */ checkCallingPermissionUserSelected()742 public boolean checkCallingPermissionUserSelected() { 743 // For user select mode READ_MEDIA_VISUAL_USER_SELECTED == true && 744 // READ_MEDIA_IMAGES == false && READ_MEDIA_VIDEO == false 745 return hasPermission(PERMISSION_READ_MEDIA_VISUAL_USER_SELECTED) 746 && !hasPermission(PERMISSION_READ_IMAGES) && !hasPermission(PERMISSION_READ_VIDEO); 747 } 748 dump(PrintWriter writer)749 protected void dump(PrintWriter writer) { 750 if (!hasDeletedFileCount()) { 751 return; 752 } 753 754 writer.println(getDeletedFileCountsLogMessage(uid, getPackageName(), 755 getDeletedFileCountsBypassingDatabase())); 756 } 757 dump(String reason)758 protected void dump(String reason) { 759 Log.i(TAG, "Invalidating LocalCallingIdentity cache for package " + packageName 760 + ". Reason: " + reason); 761 if (hasDeletedFileCount()) { 762 Logging.logPersistent(getDeletedFileCountsLogMessage(uid, getPackageName(), 763 getDeletedFileCountsBypassingDatabase())); 764 } 765 } 766 dump()767 protected void dump() { 768 if (hasDeletedFileCount()) { 769 Logging.logPersistent(getDeletedFileCountsLogMessage(uid, getPackageName(), 770 getDeletedFileCountsBypassingDatabase())); 771 clearDeletedFileCountsBypassingDatabase(); 772 } 773 } 774 getDeletedFileCountsLogMessage(int uid, String packageName, int[] deletedFileCountsBypassingDatabase)775 private static String getDeletedFileCountsLogMessage(int uid, String packageName, 776 int[] deletedFileCountsBypassingDatabase) { 777 final StringBuilder builder = new StringBuilder("uid=" + uid 778 + " packageName=" + packageName 779 + " deletedFilesCountsBypassingDatabase="); 780 for (int count: deletedFileCountsBypassingDatabase) { 781 builder.append(count).append(' '); 782 } 783 return builder.toString(); 784 } 785 } 786