summaryrefslogtreecommitdiff
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java21
-rw-r--r--core/java/android/content/pm/PackageParser.java6
-rw-r--r--core/java/android/os/Environment.java44
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);