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