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