From f8af82046a52e588b56ca893aaf47de620f4c7ad Mon Sep 17 00:00:00 2001 From: Abhijeet Kaur Date: Sun, 1 Mar 2020 13:21:16 +0000 Subject: [PATCH] Use compat flags to reflect the current status of legacy storage. Scoped Storage status of an app can be changed by using compat flags, as apps can chose to opt-in/opt out of Scoped Storage using these flags. See ag/10406320 for more details. Update documentation of isExternalStorageLegacy() method. Remove checks around INSTALL_PACKAGES and OP_REQUEST_INSTALL_PACKAGES as that does not allow legacy external storage. Remove check for WRITE_MEDIA_STORAGE as its value is now reflected by OP_LEGACY_STORAGE (ag/10162772) Bug: 132649864 Test: atest ExternalStorageHostTest Test: atest com.android.providers.media Test: manually using StorageTest app Change-Id: Id53da8f8783455038bad101e6ac50ba66b595012 --- core/java/android/os/Environment.java | 64 ++++++++++++++++++--------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 21a1e0f0a1085..70f8dd946e6cb 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -16,13 +16,13 @@ package android.os; -import android.Manifest; import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; +import android.compat.Compatibility; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; @@ -87,6 +87,16 @@ public class Environment { private static final File DIR_APEX_ROOT = getDirectory(ENV_APEX_ROOT, "/apex"); + /** + * See definition in com.android.providers.media.LocalCallingIdentity + */ + private static final long DEFAULT_SCOPED_STORAGE = 149924527L; + + /** + * See definition in com.android.providers.media.LocalCallingIdentity + */ + private static final long FORCE_ENABLE_SCOPED_STORAGE = 132649864L; + @UnsupportedAppUsage private static UserEnvironment sCurrentUser; private static boolean sUserRequired; @@ -1190,12 +1200,13 @@ public class Environment { } /** - * Returns whether the primary shared/external storage media is a legacy - * view that includes files not owned by the app. + * Returns whether the shared/external storage media is a + * legacy view that includes files not owned by the app. *

* This value may be different from the value requested by * {@code requestLegacyExternalStorage} in the app's manifest, since an app - * may inherit its legacy state based on when it was first installed. + * may inherit its legacy state based on when it was first installed, target sdk and other + * factors. *

* Non-legacy apps can continue to discover and read media belonging to * other apps via {@link android.provider.MediaStore}. @@ -1206,18 +1217,19 @@ public class Environment { } /** - * Returns whether the shared/external storage media at the given path is a + * Returns whether the shared/external storage media is a * legacy view that includes files not owned by the app. *

* This value may be different from the value requested by * {@code requestLegacyExternalStorage} in the app's manifest, since an app - * may inherit its legacy state based on when it was first installed. + * may inherit its legacy state based on when it was first installed, target sdk and other + * factors. *

* Non-legacy apps can continue to discover and read media belonging to * other apps via {@link android.provider.MediaStore}. * * @throws IllegalArgumentException if the path is not a valid storage - * device. + * device. */ public static boolean isExternalStorageLegacy(@NonNull File path) { final Context context = AppGlobals.getInitialApplication(); @@ -1231,28 +1243,38 @@ public class Environment { return false; } - if (packageManager.checkPermission(Manifest.permission.WRITE_MEDIA_STORAGE, - context.getPackageName()) == PackageManager.PERMISSION_GRANTED) { + // TODO(b/150672994): Compat flags do not override instant app and isolated process's + // behavior. + boolean defaultScopedStorage = Compatibility.isChangeEnabled(DEFAULT_SCOPED_STORAGE); + boolean forceEnableScopedStorage = Compatibility.isChangeEnabled( + FORCE_ENABLE_SCOPED_STORAGE); + // if Scoped Storage is strictly enforced, the app does *not* have legacy storage access + // Note: does not require packagename/uid as this is directly called from an app process + if (isScopedStorageEnforced(defaultScopedStorage, forceEnableScopedStorage)) { + return false; + } + // if Scoped Storage is strictly disabled, the app has legacy storage access + // Note: does not require packagename/uid as this is directly called from an app process + if (isScopedStorageDisabled(defaultScopedStorage, forceEnableScopedStorage)) { return true; } - if (packageManager.checkPermission(Manifest.permission.INSTALL_PACKAGES, - context.getPackageName()) == PackageManager.PERMISSION_GRANTED) { - return true; - } final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); - final String[] packagesForUid = packageManager.getPackagesForUid(uid); - for (String packageName : packagesForUid) { - if (appOps.checkOpNoThrow(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, - uid, packageName) == AppOpsManager.MODE_ALLOWED) { - return true; - } - } - return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED; } + private static boolean isScopedStorageEnforced(boolean defaultScopedStorage, + boolean forceEnableScopedStorage) { + return defaultScopedStorage && forceEnableScopedStorage; + } + + private static boolean isScopedStorageDisabled(boolean defaultScopedStorage, + boolean forceEnableScopedStorage) { + return !defaultScopedStorage && !forceEnableScopedStorage; + } + + static File getDirectory(String variableName, String defaultPath) { String path = System.getenv(variableName); return path == null ? new File(defaultPath) : new File(path);