• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.os;
18 
19 import android.os.storage.IMountService;
20 import android.os.storage.StorageManager;
21 import android.os.storage.StorageVolume;
22 import android.text.TextUtils;
23 import android.util.Log;
24 
25 import com.android.internal.annotations.GuardedBy;
26 
27 import java.io.File;
28 
29 /**
30  * Provides access to environment variables.
31  */
32 public class Environment {
33     private static final String TAG = "Environment";
34 
35     private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
36     private static final String ENV_EMULATED_STORAGE_SOURCE = "EMULATED_STORAGE_SOURCE";
37     private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
38     private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
39 
40     /** {@hide} */
41     public static String DIRECTORY_ANDROID = "Android";
42 
43     private static final File ROOT_DIRECTORY
44             = getDirectory("ANDROID_ROOT", "/system");
45 
46     private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
47 
48     private static UserEnvironment sCurrentUser;
49 
50     private static final Object sLock = new Object();
51 
52     @GuardedBy("sLock")
53     private static volatile StorageVolume sPrimaryVolume;
54 
getPrimaryVolume()55     private static StorageVolume getPrimaryVolume() {
56         if (sPrimaryVolume == null) {
57             synchronized (sLock) {
58                 if (sPrimaryVolume == null) {
59                     try {
60                         IMountService mountService = IMountService.Stub.asInterface(ServiceManager
61                                 .getService("mount"));
62                         final StorageVolume[] volumes = mountService.getVolumeList();
63                         sPrimaryVolume = StorageManager.getPrimaryVolume(volumes);
64                     } catch (Exception e) {
65                         Log.e(TAG, "couldn't talk to MountService", e);
66                     }
67                 }
68             }
69         }
70         return sPrimaryVolume;
71     }
72 
73     static {
initForCurrentUser()74         initForCurrentUser();
75     }
76 
77     /** {@hide} */
initForCurrentUser()78     public static void initForCurrentUser() {
79         final int userId = UserHandle.myUserId();
80         sCurrentUser = new UserEnvironment(userId);
81 
82         synchronized (sLock) {
83             sPrimaryVolume = null;
84         }
85     }
86 
87     /** {@hide} */
88     public static class UserEnvironment {
89         // TODO: generalize further to create package-specific environment
90 
91         private final File mExternalStorage;
92         private final File mExternalStorageAndroidData;
93         private final File mExternalStorageAndroidMedia;
94         private final File mExternalStorageAndroidObb;
95         private final File mMediaStorage;
96 
UserEnvironment(int userId)97         public UserEnvironment(int userId) {
98             // See storage config details at http://source.android.com/tech/storage/
99             String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
100             String rawEmulatedStorageTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
101             String rawMediaStorage = System.getenv(ENV_MEDIA_STORAGE);
102             if (TextUtils.isEmpty(rawMediaStorage)) {
103                 rawMediaStorage = "/data/media";
104             }
105 
106             if (!TextUtils.isEmpty(rawEmulatedStorageTarget)) {
107                 // Device has emulated storage; external storage paths should have
108                 // userId burned into them.
109                 final String rawUserId = Integer.toString(userId);
110                 final File emulatedBase = new File(rawEmulatedStorageTarget);
111                 final File mediaBase = new File(rawMediaStorage);
112 
113                 // /storage/emulated/0
114                 mExternalStorage = buildPath(emulatedBase, rawUserId);
115                 // /data/media/0
116                 mMediaStorage = buildPath(mediaBase, rawUserId);
117 
118             } else {
119                 // Device has physical external storage; use plain paths.
120                 if (TextUtils.isEmpty(rawExternalStorage)) {
121                     Log.w(TAG, "EXTERNAL_STORAGE undefined; falling back to default");
122                     rawExternalStorage = "/storage/sdcard0";
123                 }
124 
125                 // /storage/sdcard0
126                 mExternalStorage = new File(rawExternalStorage);
127                 // /data/media
128                 mMediaStorage = new File(rawMediaStorage);
129             }
130 
131             mExternalStorageAndroidObb = buildPath(mExternalStorage, DIRECTORY_ANDROID, "obb");
132             mExternalStorageAndroidData = buildPath(mExternalStorage, DIRECTORY_ANDROID, "data");
133             mExternalStorageAndroidMedia = buildPath(mExternalStorage, DIRECTORY_ANDROID, "media");
134         }
135 
getExternalStorageDirectory()136         public File getExternalStorageDirectory() {
137             return mExternalStorage;
138         }
139 
getExternalStorageObbDirectory()140         public File getExternalStorageObbDirectory() {
141             return mExternalStorageAndroidObb;
142         }
143 
getExternalStoragePublicDirectory(String type)144         public File getExternalStoragePublicDirectory(String type) {
145             return new File(mExternalStorage, type);
146         }
147 
getExternalStorageAndroidDataDir()148         public File getExternalStorageAndroidDataDir() {
149             return mExternalStorageAndroidData;
150         }
151 
getExternalStorageAppDataDirectory(String packageName)152         public File getExternalStorageAppDataDirectory(String packageName) {
153             return new File(mExternalStorageAndroidData, packageName);
154         }
155 
getExternalStorageAppMediaDirectory(String packageName)156         public File getExternalStorageAppMediaDirectory(String packageName) {
157             return new File(mExternalStorageAndroidMedia, packageName);
158         }
159 
getExternalStorageAppObbDirectory(String packageName)160         public File getExternalStorageAppObbDirectory(String packageName) {
161             return new File(mExternalStorageAndroidObb, packageName);
162         }
163 
getExternalStorageAppFilesDirectory(String packageName)164         public File getExternalStorageAppFilesDirectory(String packageName) {
165             return new File(new File(mExternalStorageAndroidData, packageName), "files");
166         }
167 
getExternalStorageAppCacheDirectory(String packageName)168         public File getExternalStorageAppCacheDirectory(String packageName) {
169             return new File(new File(mExternalStorageAndroidData, packageName), "cache");
170         }
171 
getMediaStorageDirectory()172         public File getMediaStorageDirectory() {
173             return mMediaStorage;
174         }
175     }
176 
177     /**
178      * Gets the Android root directory.
179      */
getRootDirectory()180     public static File getRootDirectory() {
181         return ROOT_DIRECTORY;
182     }
183 
184     /**
185      * Gets the system directory available for secure storage.
186      * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
187      * Otherwise, it returns the unencrypted /data/system directory.
188      * @return File object representing the secure storage system directory.
189      * @hide
190      */
getSystemSecureDirectory()191     public static File getSystemSecureDirectory() {
192         if (isEncryptedFilesystemEnabled()) {
193             return new File(SECURE_DATA_DIRECTORY, "system");
194         } else {
195             return new File(DATA_DIRECTORY, "system");
196         }
197     }
198 
199     /**
200      * Gets the data directory for secure storage.
201      * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure).
202      * Otherwise, it returns the unencrypted /data directory.
203      * @return File object representing the data directory for secure storage.
204      * @hide
205      */
getSecureDataDirectory()206     public static File getSecureDataDirectory() {
207         if (isEncryptedFilesystemEnabled()) {
208             return SECURE_DATA_DIRECTORY;
209         } else {
210             return DATA_DIRECTORY;
211         }
212     }
213 
214     /**
215      * Return directory used for internal media storage, which is protected by
216      * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
217      *
218      * @hide
219      */
getMediaStorageDirectory()220     public static File getMediaStorageDirectory() {
221         throwIfSystem();
222         return sCurrentUser.getMediaStorageDirectory();
223     }
224 
225     /**
226      * Return the system directory for a user. This is for use by system services to store
227      * files relating to the user. This directory will be automatically deleted when the user
228      * is removed.
229      *
230      * @hide
231      */
getUserSystemDirectory(int userId)232     public static File getUserSystemDirectory(int userId) {
233         return new File(new File(getSystemSecureDirectory(), "users"), Integer.toString(userId));
234     }
235 
236     /**
237      * Returns whether the Encrypted File System feature is enabled on the device or not.
238      * @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code>
239      * if disabled.
240      * @hide
241      */
isEncryptedFilesystemEnabled()242     public static boolean isEncryptedFilesystemEnabled() {
243         return SystemProperties.getBoolean(SYSTEM_PROPERTY_EFS_ENABLED, false);
244     }
245 
246     private static final File DATA_DIRECTORY
247             = getDirectory("ANDROID_DATA", "/data");
248 
249     /**
250      * @hide
251      */
252     private static final File SECURE_DATA_DIRECTORY
253             = getDirectory("ANDROID_SECURE_DATA", "/data/secure");
254 
255     private static final File DOWNLOAD_CACHE_DIRECTORY = getDirectory("DOWNLOAD_CACHE", "/cache");
256 
257     /**
258      * Gets the Android data directory.
259      */
getDataDirectory()260     public static File getDataDirectory() {
261         return DATA_DIRECTORY;
262     }
263 
264     /**
265      * Gets the Android external storage directory.  This directory may not
266      * currently be accessible if it has been mounted by the user on their
267      * computer, has been removed from the device, or some other problem has
268      * happened.  You can determine its current state with
269      * {@link #getExternalStorageState()}.
270      *
271      * <p><em>Note: don't be confused by the word "external" here.  This
272      * directory can better be thought as media/shared storage.  It is a
273      * filesystem that can hold a relatively large amount of data and that
274      * is shared across all applications (does not enforce permissions).
275      * Traditionally this is an SD card, but it may also be implemented as
276      * built-in storage in a device that is distinct from the protected
277      * internal storage and can be mounted as a filesystem on a computer.</em></p>
278      *
279      * <p>On devices with multiple users (as described by {@link UserManager}),
280      * each user has their own isolated external storage. Applications only
281      * have access to the external storage for the user they're running as.</p>
282      *
283      * <p>In devices with multiple "external" storage directories (such as
284      * both secure app storage and mountable shared storage), this directory
285      * represents the "primary" external storage that the user will interact
286      * with.</p>
287      *
288      * <p>Applications should not directly use this top-level directory, in
289      * order to avoid polluting the user's root namespace.  Any files that are
290      * private to the application should be placed in a directory returned
291      * by {@link android.content.Context#getExternalFilesDir
292      * Context.getExternalFilesDir}, which the system will take care of deleting
293      * if the application is uninstalled.  Other shared files should be placed
294      * in one of the directories returned by
295      * {@link #getExternalStoragePublicDirectory}.</p>
296      *
297      * <p>Writing to this path requires the
298      * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission. In
299      * a future platform release, access to this path will require the
300      * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission,
301      * which is automatically granted if you hold the write permission.</p>
302      *
303      * <p>This path may change between platform versions, so applications
304      * should only persist relative paths.</p>
305      *
306      * <p>Here is an example of typical code to monitor the state of
307      * external storage:</p>
308      *
309      * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
310      * monitor_storage}
311      *
312      * @see #getExternalStorageState()
313      * @see #isExternalStorageRemovable()
314      */
getExternalStorageDirectory()315     public static File getExternalStorageDirectory() {
316         throwIfSystem();
317         return sCurrentUser.getExternalStorageDirectory();
318     }
319 
320     /** {@hide} */
getLegacyExternalStorageDirectory()321     public static File getLegacyExternalStorageDirectory() {
322         return new File(System.getenv(ENV_EXTERNAL_STORAGE));
323     }
324 
325     /** {@hide} */
getLegacyExternalStorageObbDirectory()326     public static File getLegacyExternalStorageObbDirectory() {
327         return buildPath(getLegacyExternalStorageDirectory(), DIRECTORY_ANDROID, "obb");
328     }
329 
330     /** {@hide} */
getEmulatedStorageSource(int userId)331     public static File getEmulatedStorageSource(int userId) {
332         // /mnt/shell/emulated/0
333         return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), String.valueOf(userId));
334     }
335 
336     /** {@hide} */
getEmulatedStorageObbSource()337     public static File getEmulatedStorageObbSource() {
338         // /mnt/shell/emulated/obb
339         return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), "obb");
340     }
341 
342     /**
343      * Standard directory in which to place any audio files that should be
344      * in the regular list of music for the user.
345      * This may be combined with
346      * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
347      * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
348      * of directories to categories a particular audio file as more than one
349      * type.
350      */
351     public static String DIRECTORY_MUSIC = "Music";
352 
353     /**
354      * Standard directory in which to place any audio files that should be
355      * in the list of podcasts that the user can select (not as regular
356      * music).
357      * This may be combined with {@link #DIRECTORY_MUSIC},
358      * {@link #DIRECTORY_NOTIFICATIONS},
359      * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
360      * of directories to categories a particular audio file as more than one
361      * type.
362      */
363     public static String DIRECTORY_PODCASTS = "Podcasts";
364 
365     /**
366      * Standard directory in which to place any audio files that should be
367      * in the list of ringtones that the user can select (not as regular
368      * music).
369      * This may be combined with {@link #DIRECTORY_MUSIC},
370      * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and
371      * {@link #DIRECTORY_ALARMS} as a series
372      * of directories to categories a particular audio file as more than one
373      * type.
374      */
375     public static String DIRECTORY_RINGTONES = "Ringtones";
376 
377     /**
378      * Standard directory in which to place any audio files that should be
379      * in the list of alarms that the user can select (not as regular
380      * music).
381      * This may be combined with {@link #DIRECTORY_MUSIC},
382      * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
383      * and {@link #DIRECTORY_RINGTONES} as a series
384      * of directories to categories a particular audio file as more than one
385      * type.
386      */
387     public static String DIRECTORY_ALARMS = "Alarms";
388 
389     /**
390      * Standard directory in which to place any audio files that should be
391      * in the list of notifications that the user can select (not as regular
392      * music).
393      * This may be combined with {@link #DIRECTORY_MUSIC},
394      * {@link #DIRECTORY_PODCASTS},
395      * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
396      * of directories to categories a particular audio file as more than one
397      * type.
398      */
399     public static String DIRECTORY_NOTIFICATIONS = "Notifications";
400 
401     /**
402      * Standard directory in which to place pictures that are available to
403      * the user.  Note that this is primarily a convention for the top-level
404      * public directory, as the media scanner will find and collect pictures
405      * in any directory.
406      */
407     public static String DIRECTORY_PICTURES = "Pictures";
408 
409     /**
410      * Standard directory in which to place movies that are available to
411      * the user.  Note that this is primarily a convention for the top-level
412      * public directory, as the media scanner will find and collect movies
413      * in any directory.
414      */
415     public static String DIRECTORY_MOVIES = "Movies";
416 
417     /**
418      * Standard directory in which to place files that have been downloaded by
419      * the user.  Note that this is primarily a convention for the top-level
420      * public directory, you are free to download files anywhere in your own
421      * private directories.  Also note that though the constant here is
422      * named DIRECTORY_DOWNLOADS (plural), the actual file name is non-plural for
423      * backwards compatibility reasons.
424      */
425     public static String DIRECTORY_DOWNLOADS = "Download";
426 
427     /**
428      * The traditional location for pictures and videos when mounting the
429      * device as a camera.  Note that this is primarily a convention for the
430      * top-level public directory, as this convention makes no sense elsewhere.
431      */
432     public static String DIRECTORY_DCIM = "DCIM";
433 
434     /**
435      * Get a top-level public external storage directory for placing files of
436      * a particular type.  This is where the user will typically place and
437      * manage their own files, so you should be careful about what you put here
438      * to ensure you don't erase their files or get in the way of their own
439      * organization.
440      *
441      * <p>On devices with multiple users (as described by {@link UserManager}),
442      * each user has their own isolated external storage. Applications only
443      * have access to the external storage for the user they're running as.</p>
444      *
445      * <p>Here is an example of typical code to manipulate a picture on
446      * the public external storage:</p>
447      *
448      * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
449      * public_picture}
450      *
451      * @param type The type of storage directory to return.  Should be one of
452      * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
453      * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
454      * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
455      * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
456      * {@link #DIRECTORY_DCIM}.  May not be null.
457      *
458      * @return Returns the File path for the directory.  Note that this
459      * directory may not yet exist, so you must make sure it exists before
460      * using it such as with {@link File#mkdirs File.mkdirs()}.
461      */
getExternalStoragePublicDirectory(String type)462     public static File getExternalStoragePublicDirectory(String type) {
463         throwIfSystem();
464         return sCurrentUser.getExternalStoragePublicDirectory(type);
465     }
466 
467     /**
468      * Returns the path for android-specific data on the SD card.
469      * @hide
470      */
getExternalStorageAndroidDataDir()471     public static File getExternalStorageAndroidDataDir() {
472         throwIfSystem();
473         return sCurrentUser.getExternalStorageAndroidDataDir();
474     }
475 
476     /**
477      * Generates the raw path to an application's data
478      * @hide
479      */
getExternalStorageAppDataDirectory(String packageName)480     public static File getExternalStorageAppDataDirectory(String packageName) {
481         throwIfSystem();
482         return sCurrentUser.getExternalStorageAppDataDirectory(packageName);
483     }
484 
485     /**
486      * Generates the raw path to an application's media
487      * @hide
488      */
getExternalStorageAppMediaDirectory(String packageName)489     public static File getExternalStorageAppMediaDirectory(String packageName) {
490         throwIfSystem();
491         return sCurrentUser.getExternalStorageAppMediaDirectory(packageName);
492     }
493 
494     /**
495      * Generates the raw path to an application's OBB files
496      * @hide
497      */
getExternalStorageAppObbDirectory(String packageName)498     public static File getExternalStorageAppObbDirectory(String packageName) {
499         throwIfSystem();
500         return sCurrentUser.getExternalStorageAppObbDirectory(packageName);
501     }
502 
503     /**
504      * Generates the path to an application's files.
505      * @hide
506      */
getExternalStorageAppFilesDirectory(String packageName)507     public static File getExternalStorageAppFilesDirectory(String packageName) {
508         throwIfSystem();
509         return sCurrentUser.getExternalStorageAppFilesDirectory(packageName);
510     }
511 
512     /**
513      * Generates the path to an application's cache.
514      * @hide
515      */
getExternalStorageAppCacheDirectory(String packageName)516     public static File getExternalStorageAppCacheDirectory(String packageName) {
517         throwIfSystem();
518         return sCurrentUser.getExternalStorageAppCacheDirectory(packageName);
519     }
520 
521     /**
522      * Gets the Android download/cache content directory.
523      */
getDownloadCacheDirectory()524     public static File getDownloadCacheDirectory() {
525         return DOWNLOAD_CACHE_DIRECTORY;
526     }
527 
528     /**
529      * {@link #getExternalStorageState()} returns MEDIA_REMOVED if the media is not present.
530      */
531     public static final String MEDIA_REMOVED = "removed";
532 
533     /**
534      * {@link #getExternalStorageState()} returns MEDIA_UNMOUNTED if the media is present
535      * but not mounted.
536      */
537     public static final String MEDIA_UNMOUNTED = "unmounted";
538 
539     /**
540      * {@link #getExternalStorageState()} returns MEDIA_CHECKING if the media is present
541      * and being disk-checked
542      */
543     public static final String MEDIA_CHECKING = "checking";
544 
545     /**
546      * {@link #getExternalStorageState()} returns MEDIA_NOFS if the media is present
547      * but is blank or is using an unsupported filesystem
548      */
549     public static final String MEDIA_NOFS = "nofs";
550 
551     /**
552      * {@link #getExternalStorageState()} returns MEDIA_MOUNTED if the media is present
553      * and mounted at its mount point with read/write access.
554      */
555     public static final String MEDIA_MOUNTED = "mounted";
556 
557     /**
558      * {@link #getExternalStorageState()} returns MEDIA_MOUNTED_READ_ONLY if the media is present
559      * and mounted at its mount point with read only access.
560      */
561     public static final String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
562 
563     /**
564      * {@link #getExternalStorageState()} returns MEDIA_SHARED if the media is present
565      * not mounted, and shared via USB mass storage.
566      */
567     public static final String MEDIA_SHARED = "shared";
568 
569     /**
570      * {@link #getExternalStorageState()} returns MEDIA_BAD_REMOVAL if the media was
571      * removed before it was unmounted.
572      */
573     public static final String MEDIA_BAD_REMOVAL = "bad_removal";
574 
575     /**
576      * {@link #getExternalStorageState()} returns MEDIA_UNMOUNTABLE if the media is present
577      * but cannot be mounted.  Typically this happens if the file system on the
578      * media is corrupted.
579      */
580     public static final String MEDIA_UNMOUNTABLE = "unmountable";
581 
582     /**
583      * Gets the current state of the primary "external" storage device.
584      *
585      * @see #getExternalStorageDirectory()
586      */
getExternalStorageState()587     public static String getExternalStorageState() {
588         try {
589             IMountService mountService = IMountService.Stub.asInterface(ServiceManager
590                     .getService("mount"));
591             final StorageVolume primary = getPrimaryVolume();
592             return mountService.getVolumeState(primary.getPath());
593         } catch (RemoteException rex) {
594             Log.w(TAG, "Failed to read external storage state; assuming REMOVED: " + rex);
595             return Environment.MEDIA_REMOVED;
596         }
597     }
598 
599     /**
600      * Returns whether the primary "external" storage device is removable.
601      * If true is returned, this device is for example an SD card that the
602      * user can remove.  If false is returned, the storage is built into
603      * the device and can not be physically removed.
604      *
605      * <p>See {@link #getExternalStorageDirectory()} for more information.
606      */
isExternalStorageRemovable()607     public static boolean isExternalStorageRemovable() {
608         final StorageVolume primary = getPrimaryVolume();
609         return (primary != null && primary.isRemovable());
610     }
611 
612     /**
613      * Returns whether the device has an external storage device which is
614      * emulated. If true, the device does not have real external storage, and the directory
615      * returned by {@link #getExternalStorageDirectory()} will be allocated using a portion of
616      * the internal storage system.
617      *
618      * <p>Certain system services, such as the package manager, use this
619      * to determine where to install an application.
620      *
621      * <p>Emulated external storage may also be encrypted - see
622      * {@link android.app.admin.DevicePolicyManager#setStorageEncryption(
623      * android.content.ComponentName, boolean)} for additional details.
624      */
isExternalStorageEmulated()625     public static boolean isExternalStorageEmulated() {
626         final StorageVolume primary = getPrimaryVolume();
627         return (primary != null && primary.isEmulated());
628     }
629 
getDirectory(String variableName, String defaultPath)630     static File getDirectory(String variableName, String defaultPath) {
631         String path = System.getenv(variableName);
632         return path == null ? new File(defaultPath) : new File(path);
633     }
634 
throwIfSystem()635     private static void throwIfSystem() {
636         if (Process.myUid() == Process.SYSTEM_UID) {
637             Log.wtf(TAG, "Static storage paths aren't available from AID_SYSTEM", new Throwable());
638         }
639     }
640 
buildPath(File base, String... segments)641     private static File buildPath(File base, String... segments) {
642         File cur = base;
643         for (String segment : segments) {
644             if (cur == null) {
645                 cur = new File(segment);
646             } else {
647                 cur = new File(cur, segment);
648             }
649         }
650         return cur;
651     }
652 }
653