1616
1717package android .os ;
1818
19- import android .content .res .Resources ;
2019import android .os .storage .IMountService ;
20+ import android .os .storage .StorageManager ;
2121import android .os .storage .StorageVolume ;
22+ import android .text .TextUtils ;
2223import android .util .Log ;
2324
2425import java .io .File ;
2930public class Environment {
3031 private static final String TAG = "Environment" ;
3132
33+ private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE" ;
34+ private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET" ;
35+
3236 private static final File ROOT_DIRECTORY
3337 = getDirectory ("ANDROID_ROOT" , "/system" );
3438
3539 private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled" ;
3640
37- private static final Object mLock = new Object ();
41+ private static UserEnvironment sCurrentUser ;
42+
43+ private static final Object sLock = new Object ();
3844
39- private volatile static StorageVolume mPrimaryVolume = null ;
45+ // @GuardedBy("sLock")
46+ private static volatile StorageVolume sPrimaryVolume ;
4047
4148 private static StorageVolume getPrimaryVolume () {
42- if (mPrimaryVolume == null ) {
43- synchronized (mLock ) {
44- if (mPrimaryVolume == null ) {
49+ if (sPrimaryVolume == null ) {
50+ synchronized (sLock ) {
51+ if (sPrimaryVolume == null ) {
4552 try {
4653 IMountService mountService = IMountService .Stub .asInterface (ServiceManager
4754 .getService ("mount" ));
48- Parcelable [] volumes = mountService .getVolumeList ();
49- mPrimaryVolume = ( StorageVolume ) volumes [ 0 ] ;
55+ final StorageVolume [] volumes = mountService .getVolumeList ();
56+ sPrimaryVolume = StorageManager . getPrimaryVolume ( volumes ) ;
5057 } catch (Exception e ) {
5158 Log .e (TAG , "couldn't talk to MountService" , e );
5259 }
5360 }
5461 }
5562 }
56- return mPrimaryVolume ;
63+ return sPrimaryVolume ;
64+ }
65+
66+ static {
67+ initForCurrentUser ();
68+ }
69+
70+ /** {@hide} */
71+ public static void initForCurrentUser () {
72+ final int userId = UserHandle .myUserId ();
73+ sCurrentUser = new UserEnvironment (userId );
74+
75+ synchronized (sLock ) {
76+ sPrimaryVolume = null ;
77+ }
78+ }
79+
80+ /** {@hide} */
81+ public static class UserEnvironment {
82+ // TODO: generalize further to create package-specific environment
83+
84+ private final File mExternalStorage ;
85+ private final File mExternalStorageAndroidData ;
86+ private final File mExternalStorageAndroidMedia ;
87+ private final File mExternalStorageAndroidObb ;
88+
89+ public UserEnvironment (int userId ) {
90+ // See storage config details at http://source.android.com/tech/storage/
91+ String rawExternalStorage = System .getenv (ENV_EXTERNAL_STORAGE );
92+ String rawEmulatedStorageTarget = System .getenv (ENV_EMULATED_STORAGE_TARGET );
93+
94+ if (!TextUtils .isEmpty (rawEmulatedStorageTarget )) {
95+ // Device has emulated storage; external storage paths should have
96+ // userId burned into them.
97+ final File emulatedBase = new File (rawEmulatedStorageTarget );
98+
99+ // /storage/emulated/0
100+ mExternalStorage = buildPath (emulatedBase , Integer .toString (userId ));
101+ // /storage/emulated/obb
102+ mExternalStorageAndroidObb = buildPath (emulatedBase , "obb" );
103+
104+ } else {
105+ // Device has physical external storage; use plain paths.
106+ if (TextUtils .isEmpty (rawExternalStorage )) {
107+ Log .w (TAG , "EXTERNAL_STORAGE undefined; falling back to default" );
108+ rawExternalStorage = "/storage/sdcard0" ;
109+ }
110+
111+ // /storage/sdcard0
112+ mExternalStorage = new File (rawExternalStorage );
113+ // /storage/sdcard0/Android/obb
114+ mExternalStorageAndroidObb = buildPath (mExternalStorage , "Android" , "obb" );
115+ }
116+
117+ mExternalStorageAndroidData = buildPath (mExternalStorage , "Android" , "data" );
118+ mExternalStorageAndroidMedia = buildPath (mExternalStorage , "Android" , "media" );
119+ }
120+
121+ public File getExternalStorageDirectory () {
122+ return mExternalStorage ;
123+ }
124+
125+ public File getExternalStoragePublicDirectory (String type ) {
126+ return new File (mExternalStorage , type );
127+ }
128+
129+ public File getExternalStorageAndroidDataDir () {
130+ return mExternalStorageAndroidData ;
131+ }
132+
133+ public File getExternalStorageAppDataDirectory (String packageName ) {
134+ return new File (mExternalStorageAndroidData , packageName );
135+ }
136+
137+ public File getExternalStorageAppMediaDirectory (String packageName ) {
138+ return new File (mExternalStorageAndroidMedia , packageName );
139+ }
140+
141+ public File getExternalStorageAppObbDirectory (String packageName ) {
142+ return new File (mExternalStorageAndroidObb , packageName );
143+ }
144+
145+ public File getExternalStorageAppFilesDirectory (String packageName ) {
146+ return new File (new File (mExternalStorageAndroidData , packageName ), "files" );
147+ }
148+
149+ public File getExternalStorageAppCacheDirectory (String packageName ) {
150+ return new File (new File (mExternalStorageAndroidData , packageName ), "cache" );
151+ }
57152 }
58153
59154 /**
@@ -137,20 +232,7 @@ public static boolean isEncryptedFilesystemEnabled() {
137232 private static final File MEDIA_STORAGE_DIRECTORY
138233 = getDirectory ("MEDIA_STORAGE" , "/data/media" );
139234
140- private static final File EXTERNAL_STORAGE_DIRECTORY
141- = getDirectory ("EXTERNAL_STORAGE" , "/storage/sdcard0" );
142-
143- private static final File EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY = new File (new File (
144- getDirectory ("EXTERNAL_STORAGE" , "/storage/sdcard0" ), "Android" ), "data" );
145-
146- private static final File EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY = new File (new File (
147- getDirectory ("EXTERNAL_STORAGE" , "/storage/sdcard0" ), "Android" ), "media" );
148-
149- private static final File EXTERNAL_STORAGE_ANDROID_OBB_DIRECTORY = new File (new File (
150- getDirectory ("EXTERNAL_STORAGE" , "/storage/sdcard0" ), "Android" ), "obb" );
151-
152- private static final File DOWNLOAD_CACHE_DIRECTORY
153- = getDirectory ("DOWNLOAD_CACHE" , "/cache" );
235+ private static final File DOWNLOAD_CACHE_DIRECTORY = getDirectory ("DOWNLOAD_CACHE" , "/cache" );
154236
155237 /**
156238 * Gets the Android data directory.
@@ -198,7 +280,13 @@ public static File getDataDirectory() {
198280 * @see #isExternalStorageRemovable()
199281 */
200282 public static File getExternalStorageDirectory () {
201- return EXTERNAL_STORAGE_DIRECTORY ;
283+ throwIfSystem ();
284+ return sCurrentUser .getExternalStorageDirectory ();
285+ }
286+
287+ /** {@hide} */
288+ public static File getLegacyExternalStorageDirectory () {
289+ return new File (System .getenv (ENV_EXTERNAL_STORAGE ));
202290 }
203291
204292 /**
@@ -318,57 +406,62 @@ public static File getExternalStorageDirectory() {
318406 * using it such as with {@link File#mkdirs File.mkdirs()}.
319407 */
320408 public static File getExternalStoragePublicDirectory (String type ) {
321- return new File (getExternalStorageDirectory (), type );
409+ throwIfSystem ();
410+ return sCurrentUser .getExternalStoragePublicDirectory (type );
322411 }
323412
324413 /**
325414 * Returns the path for android-specific data on the SD card.
326415 * @hide
327416 */
328417 public static File getExternalStorageAndroidDataDir () {
329- return EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY ;
418+ throwIfSystem ();
419+ return sCurrentUser .getExternalStorageAndroidDataDir ();
330420 }
331421
332422 /**
333423 * Generates the raw path to an application's data
334424 * @hide
335425 */
336426 public static File getExternalStorageAppDataDirectory (String packageName ) {
337- return new File (EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY , packageName );
427+ throwIfSystem ();
428+ return sCurrentUser .getExternalStorageAppDataDirectory (packageName );
338429 }
339430
340431 /**
341432 * Generates the raw path to an application's media
342433 * @hide
343434 */
344435 public static File getExternalStorageAppMediaDirectory (String packageName ) {
345- return new File (EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY , packageName );
436+ throwIfSystem ();
437+ return sCurrentUser .getExternalStorageAppMediaDirectory (packageName );
346438 }
347439
348440 /**
349441 * Generates the raw path to an application's OBB files
350442 * @hide
351443 */
352444 public static File getExternalStorageAppObbDirectory (String packageName ) {
353- return new File (EXTERNAL_STORAGE_ANDROID_OBB_DIRECTORY , packageName );
445+ throwIfSystem ();
446+ return sCurrentUser .getExternalStorageAppObbDirectory (packageName );
354447 }
355448
356449 /**
357450 * Generates the path to an application's files.
358451 * @hide
359452 */
360453 public static File getExternalStorageAppFilesDirectory (String packageName ) {
361- return new File ( new File ( EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY ,
362- packageName ), "files" );
454+ throwIfSystem ();
455+ return sCurrentUser . getExternalStorageAppFilesDirectory ( packageName );
363456 }
364-
457+
365458 /**
366459 * Generates the path to an application's cache.
367460 * @hide
368461 */
369462 public static File getExternalStorageAppCacheDirectory (String packageName ) {
370- return new File ( new File ( EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY ,
371- packageName ), "cache" );
463+ throwIfSystem ();
464+ return sCurrentUser . getExternalStorageAppCacheDirectory ( packageName );
372465 }
373466
374467 /**
@@ -441,9 +534,10 @@ public static String getExternalStorageState() {
441534 try {
442535 IMountService mountService = IMountService .Stub .asInterface (ServiceManager
443536 .getService ("mount" ));
444- return mountService .getVolumeState (getExternalStorageDirectory ()
445- .toString ());
446- } catch (Exception rex ) {
537+ final StorageVolume primary = getPrimaryVolume ();
538+ return mountService .getVolumeState (primary .getPath ());
539+ } catch (RemoteException rex ) {
540+ Log .w (TAG , "Failed to read external storage state; assuming REMOVED: " + rex );
447541 return Environment .MEDIA_REMOVED ;
448542 }
449543 }
@@ -457,8 +551,8 @@ public static String getExternalStorageState() {
457551 * <p>See {@link #getExternalStorageDirectory()} for more information.
458552 */
459553 public static boolean isExternalStorageRemovable () {
460- StorageVolume volume = getPrimaryVolume ();
461- return (volume != null && volume .isRemovable ());
554+ final StorageVolume primary = getPrimaryVolume ();
555+ return (primary != null && primary .isRemovable ());
462556 }
463557
464558 /**
@@ -475,12 +569,30 @@ public static boolean isExternalStorageRemovable() {
475569 * android.content.ComponentName, boolean)} for additional details.
476570 */
477571 public static boolean isExternalStorageEmulated () {
478- StorageVolume volume = getPrimaryVolume ();
479- return (volume != null && volume .isEmulated ());
572+ final StorageVolume primary = getPrimaryVolume ();
573+ return (primary != null && primary .isEmulated ());
480574 }
481575
482576 static File getDirectory (String variableName , String defaultPath ) {
483577 String path = System .getenv (variableName );
484578 return path == null ? new File (defaultPath ) : new File (path );
485579 }
580+
581+ private static void throwIfSystem () {
582+ if (Process .myUid () == Process .SYSTEM_UID ) {
583+ Log .wtf (TAG , "Static storage paths aren't available from AID_SYSTEM" , new Throwable ());
584+ }
585+ }
586+
587+ private static File buildPath (File base , String ... segments ) {
588+ File cur = base ;
589+ for (String segment : segments ) {
590+ if (cur == null ) {
591+ cur = new File (segment );
592+ } else {
593+ cur = new File (cur , segment );
594+ }
595+ }
596+ return cur ;
597+ }
486598}
0 commit comments