• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.media;
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