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