diff options
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/content/pm/ApplicationInfo.java | 21 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageParser.java | 6 | ||||
| -rw-r--r-- | core/java/android/os/Environment.java | 44 |
3 files changed, 68 insertions, 3 deletions
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 068a93a253ff..5328dda03893 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -678,6 +678,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final int PRIVATE_FLAG_IS_RESOURCE_OVERLAY = 1 << 28; + /** + * Value for {@link #privateFlags}: If {@code true} this app allows + * shared/external storage media to be a sandboxed view that only contains + * files owned by the app. + * + * @hide + */ + public static final int PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX = 1 << 29; /** @hide */ @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = { @@ -707,7 +715,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { PRIVATE_FLAG_VIRTUAL_PRELOAD, PRIVATE_FLAG_HAS_FRAGILE_USER_DATA, PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, - PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE + PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, + PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX, }) @Retention(RetentionPolicy.SOURCE) public @interface ApplicationInfoPrivateFlags {} @@ -1822,6 +1831,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (privateFlags & PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) != 0; } + /** + * If {@code true} this app allows shared/external storage media to be a + * sandboxed view that only contains files owned by the app. + * + * @hide + */ + public boolean isExternalStorageSandboxAllowed() { + return (privateFlags & PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX) != 0; + } + private boolean isAllowedToUseHiddenApis() { if (isSignedWithPlatformKey()) { return true; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 35d1eac5c0ad..0a01dcda8bbb 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3689,6 +3689,12 @@ public class PackageParser { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE; } + if (sa.getBoolean( + R.styleable.AndroidManifestApplication_allowExternalStorageSandbox, + owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) { + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX; + } + ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0); ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0); diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index cceb6edc4c0a..f7e927e48863 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.app.AppGlobals; +import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.storage.StorageManager; @@ -1060,7 +1062,7 @@ public class Environment { * @throws IllegalArgumentException if the path is not a valid storage * device. */ - public static boolean isExternalStorageRemovable(File path) { + public static boolean isExternalStorageRemovable(@NonNull File path) { final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId()); if (volume != null) { return volume.isRemovable(); @@ -1103,7 +1105,7 @@ public class Environment { * @throws IllegalArgumentException if the path is not a valid storage * device. */ - public static boolean isExternalStorageEmulated(File path) { + public static boolean isExternalStorageEmulated(@NonNull File path) { final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId()); if (volume != null) { return volume.isEmulated(); @@ -1112,6 +1114,44 @@ public class Environment { } } + /** + * Returns whether the shared/external storage media at the given path is a + * sandboxed view that only contains files owned by the app. + * <p> + * This value may be different from the value requested by + * {@code allowExternalStorageSandbox} in the app's manifest, since an app + * may inherit its sandboxed state based on when it was first installed. + * <p> + * Sandboxed apps can continue to discover and read media belonging to other + * apps via {@link android.provider.MediaStore}. + */ + public static boolean isExternalStorageSandboxed() { + final File externalDir = sCurrentUser.getExternalDirs()[0]; + return isExternalStorageSandboxed(externalDir); + } + + /** + * Returns whether the shared/external storage media at the given path is a + * sandboxed view that only contains files owned by the app. + * <p> + * This value may be different from the value requested by + * {@code allowExternalStorageSandbox} in the app's manifest, since an app + * may inherit its sandboxed state based on when it was first installed. + * <p> + * Sandboxed 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. + */ + public static boolean isExternalStorageSandboxed(@NonNull File path) { + final Context context = AppGlobals.getInitialApplication(); + final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); + return appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, + context.getApplicationInfo().uid, + context.getPackageName()) != AppOpsManager.MODE_ALLOWED; + } + static File getDirectory(String variableName, String defaultPath) { String path = System.getenv(variableName); return path == null ? new File(defaultPath) : new File(path); |
