Include user identifier in external storage paths.
When building external storage paths, always include user in path
to enable cross-user paths and aid debugging.
Each Zygote process continues to only have access to the appropriate
user-specific emulated storage through bind mounts. A second set of
mounts continue supporting legacy /sdcard-style paths. For example,
a process running as owner has these mount points:
/storage/emulated_legacy
/storage/emulated_legacy/Android/obb
/storage/emulated/0
/storage/emulated/obb
Since Environment is created before Zygote forks, we need to update
its internal paths after each process launches.
Bug: 7131382
Change-Id: I6f8c6971f2a8edfb415c14cb4ed05ff97e587a21
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 2fbcf3f..6667a41f 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -16,9 +16,10 @@
package android.os;
-import android.content.res.Resources;
import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
+import android.text.TextUtils;
import android.util.Log;
import java.io.File;
@@ -29,31 +30,125 @@
public class Environment {
private static final String TAG = "Environment";
+ private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
+ private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
+
private static final File ROOT_DIRECTORY
= getDirectory("ANDROID_ROOT", "/system");
private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
- private static final Object mLock = new Object();
+ private static UserEnvironment sCurrentUser;
- private volatile static StorageVolume mPrimaryVolume = null;
+ private static final Object sLock = new Object();
+
+ // @GuardedBy("sLock")
+ private static volatile StorageVolume sPrimaryVolume;
private static StorageVolume getPrimaryVolume() {
- if (mPrimaryVolume == null) {
- synchronized (mLock) {
- if (mPrimaryVolume == null) {
+ if (sPrimaryVolume == null) {
+ synchronized (sLock) {
+ if (sPrimaryVolume == null) {
try {
IMountService mountService = IMountService.Stub.asInterface(ServiceManager
.getService("mount"));
- Parcelable[] volumes = mountService.getVolumeList();
- mPrimaryVolume = (StorageVolume)volumes[0];
+ final StorageVolume[] volumes = mountService.getVolumeList();
+ sPrimaryVolume = StorageManager.getPrimaryVolume(volumes);
} catch (Exception e) {
Log.e(TAG, "couldn't talk to MountService", e);
}
}
}
}
- return mPrimaryVolume;
+ return sPrimaryVolume;
+ }
+
+ static {
+ initForCurrentUser();
+ }
+
+ /** {@hide} */
+ public static void initForCurrentUser() {
+ final int userId = UserHandle.myUserId();
+ sCurrentUser = new UserEnvironment(userId);
+
+ synchronized (sLock) {
+ sPrimaryVolume = null;
+ }
+ }
+
+ /** {@hide} */
+ public static class UserEnvironment {
+ // TODO: generalize further to create package-specific environment
+
+ private final File mExternalStorage;
+ private final File mExternalStorageAndroidData;
+ private final File mExternalStorageAndroidMedia;
+ private final File mExternalStorageAndroidObb;
+
+ public UserEnvironment(int userId) {
+ // See storage config details at http://source.android.com/tech/storage/
+ String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
+ String rawEmulatedStorageTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
+
+ if (!TextUtils.isEmpty(rawEmulatedStorageTarget)) {
+ // Device has emulated storage; external storage paths should have
+ // userId burned into them.
+ final File emulatedBase = new File(rawEmulatedStorageTarget);
+
+ // /storage/emulated/0
+ mExternalStorage = buildPath(emulatedBase, Integer.toString(userId));
+ // /storage/emulated/obb
+ mExternalStorageAndroidObb = buildPath(emulatedBase, "obb");
+
+ } else {
+ // Device has physical external storage; use plain paths.
+ if (TextUtils.isEmpty(rawExternalStorage)) {
+ Log.w(TAG, "EXTERNAL_STORAGE undefined; falling back to default");
+ rawExternalStorage = "/storage/sdcard0";
+ }
+
+ // /storage/sdcard0
+ mExternalStorage = new File(rawExternalStorage);
+ // /storage/sdcard0/Android/obb
+ mExternalStorageAndroidObb = buildPath(mExternalStorage, "Android", "obb");
+ }
+
+ mExternalStorageAndroidData = buildPath(mExternalStorage, "Android", "data");
+ mExternalStorageAndroidMedia = buildPath(mExternalStorage, "Android", "media");
+ }
+
+ public File getExternalStorageDirectory() {
+ return mExternalStorage;
+ }
+
+ public File getExternalStoragePublicDirectory(String type) {
+ return new File(mExternalStorage, type);
+ }
+
+ public File getExternalStorageAndroidDataDir() {
+ return mExternalStorageAndroidData;
+ }
+
+ public File getExternalStorageAppDataDirectory(String packageName) {
+ return new File(mExternalStorageAndroidData, packageName);
+ }
+
+ public File getExternalStorageAppMediaDirectory(String packageName) {
+ return new File(mExternalStorageAndroidMedia, packageName);
+ }
+
+ public File getExternalStorageAppObbDirectory(String packageName) {
+ return new File(mExternalStorageAndroidObb, packageName);
+ }
+
+ public File getExternalStorageAppFilesDirectory(String packageName) {
+ return new File(new File(mExternalStorageAndroidData, packageName), "files");
+ }
+
+ public File getExternalStorageAppCacheDirectory(String packageName) {
+ return new File(new File(mExternalStorageAndroidData, packageName), "cache");
+ }
}
/**
@@ -137,20 +232,7 @@
private static final File MEDIA_STORAGE_DIRECTORY
= getDirectory("MEDIA_STORAGE", "/data/media");
- private static final File EXTERNAL_STORAGE_DIRECTORY
- = getDirectory("EXTERNAL_STORAGE", "/storage/sdcard0");
-
- private static final File EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY = new File(new File(
- getDirectory("EXTERNAL_STORAGE", "/storage/sdcard0"), "Android"), "data");
-
- private static final File EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY = new File(new File(
- getDirectory("EXTERNAL_STORAGE", "/storage/sdcard0"), "Android"), "media");
-
- private static final File EXTERNAL_STORAGE_ANDROID_OBB_DIRECTORY = new File(new File(
- getDirectory("EXTERNAL_STORAGE", "/storage/sdcard0"), "Android"), "obb");
-
- private static final File DOWNLOAD_CACHE_DIRECTORY
- = getDirectory("DOWNLOAD_CACHE", "/cache");
+ private static final File DOWNLOAD_CACHE_DIRECTORY = getDirectory("DOWNLOAD_CACHE", "/cache");
/**
* Gets the Android data directory.
@@ -198,7 +280,13 @@
* @see #isExternalStorageRemovable()
*/
public static File getExternalStorageDirectory() {
- return EXTERNAL_STORAGE_DIRECTORY;
+ throwIfSystem();
+ return sCurrentUser.getExternalStorageDirectory();
+ }
+
+ /** {@hide} */
+ public static File getLegacyExternalStorageDirectory() {
+ return new File(System.getenv(ENV_EXTERNAL_STORAGE));
}
/**
@@ -318,7 +406,8 @@
* using it such as with {@link File#mkdirs File.mkdirs()}.
*/
public static File getExternalStoragePublicDirectory(String type) {
- return new File(getExternalStorageDirectory(), type);
+ throwIfSystem();
+ return sCurrentUser.getExternalStoragePublicDirectory(type);
}
/**
@@ -326,7 +415,8 @@
* @hide
*/
public static File getExternalStorageAndroidDataDir() {
- return EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY;
+ throwIfSystem();
+ return sCurrentUser.getExternalStorageAndroidDataDir();
}
/**
@@ -334,7 +424,8 @@
* @hide
*/
public static File getExternalStorageAppDataDirectory(String packageName) {
- return new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY, packageName);
+ throwIfSystem();
+ return sCurrentUser.getExternalStorageAppDataDirectory(packageName);
}
/**
@@ -342,7 +433,8 @@
* @hide
*/
public static File getExternalStorageAppMediaDirectory(String packageName) {
- return new File(EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY, packageName);
+ throwIfSystem();
+ return sCurrentUser.getExternalStorageAppMediaDirectory(packageName);
}
/**
@@ -350,7 +442,8 @@
* @hide
*/
public static File getExternalStorageAppObbDirectory(String packageName) {
- return new File(EXTERNAL_STORAGE_ANDROID_OBB_DIRECTORY, packageName);
+ throwIfSystem();
+ return sCurrentUser.getExternalStorageAppObbDirectory(packageName);
}
/**
@@ -358,17 +451,17 @@
* @hide
*/
public static File getExternalStorageAppFilesDirectory(String packageName) {
- return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY,
- packageName), "files");
+ throwIfSystem();
+ return sCurrentUser.getExternalStorageAppFilesDirectory(packageName);
}
-
+
/**
* Generates the path to an application's cache.
* @hide
*/
public static File getExternalStorageAppCacheDirectory(String packageName) {
- return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY,
- packageName), "cache");
+ throwIfSystem();
+ return sCurrentUser.getExternalStorageAppCacheDirectory(packageName);
}
/**
@@ -441,9 +534,10 @@
try {
IMountService mountService = IMountService.Stub.asInterface(ServiceManager
.getService("mount"));
- return mountService.getVolumeState(getExternalStorageDirectory()
- .toString());
- } catch (Exception rex) {
+ final StorageVolume primary = getPrimaryVolume();
+ return mountService.getVolumeState(primary.getPath());
+ } catch (RemoteException rex) {
+ Log.w(TAG, "Failed to read external storage state; assuming REMOVED: " + rex);
return Environment.MEDIA_REMOVED;
}
}
@@ -457,8 +551,8 @@
* <p>See {@link #getExternalStorageDirectory()} for more information.
*/
public static boolean isExternalStorageRemovable() {
- StorageVolume volume = getPrimaryVolume();
- return (volume != null && volume.isRemovable());
+ final StorageVolume primary = getPrimaryVolume();
+ return (primary != null && primary.isRemovable());
}
/**
@@ -475,12 +569,30 @@
* android.content.ComponentName, boolean)} for additional details.
*/
public static boolean isExternalStorageEmulated() {
- StorageVolume volume = getPrimaryVolume();
- return (volume != null && volume.isEmulated());
+ final StorageVolume primary = getPrimaryVolume();
+ return (primary != null && primary.isEmulated());
}
static File getDirectory(String variableName, String defaultPath) {
String path = System.getenv(variableName);
return path == null ? new File(defaultPath) : new File(path);
}
+
+ private static void throwIfSystem() {
+ if (Process.myUid() == Process.SYSTEM_UID) {
+ Log.wtf(TAG, "Static storage paths aren't available from AID_SYSTEM", new Throwable());
+ }
+ }
+
+ private static File buildPath(File base, String... segments) {
+ File cur = base;
+ for (String segment : segments) {
+ if (cur == null) {
+ cur = new File(segment);
+ } else {
+ cur = new File(cur, segment);
+ }
+ }
+ return cur;
+ }
}