Environment.java revision 6959133d84b52ad177b22efc80b10dc1ad3fb62f
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
17package android.os;
18
19import android.content.Context;
20import android.os.storage.IMountService;
21import android.os.storage.StorageManager;
22import android.os.storage.StorageVolume;
23import android.text.TextUtils;
24import android.util.Log;
25
26import com.android.internal.annotations.GuardedBy;
27import com.google.android.collect.Lists;
28
29import java.io.File;
30import java.io.IOException;
31import java.util.ArrayList;
32
33/**
34 * Provides access to environment variables.
35 */
36public class Environment {
37    private static final String TAG = "Environment";
38
39    private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
40    private static final String ENV_EMULATED_STORAGE_SOURCE = "EMULATED_STORAGE_SOURCE";
41    private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
42    private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
43    private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE";
44    private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
45    private static final String ENV_OEM_ROOT = "OEM_ROOT";
46    private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
47
48    /** {@hide} */
49    public static final String DIR_ANDROID = "Android";
50    private static final String DIR_DATA = "data";
51    private static final String DIR_MEDIA = "media";
52    private static final String DIR_OBB = "obb";
53    private static final String DIR_FILES = "files";
54    private static final String DIR_CACHE = "cache";
55
56    /** {@hide} */
57    @Deprecated
58    public static final String DIRECTORY_ANDROID = DIR_ANDROID;
59
60    private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
61    private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
62    private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
63    private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media");
64
65    private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull(
66            ENV_EMULATED_STORAGE_TARGET);
67
68    private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
69
70    private static UserEnvironment sCurrentUser;
71    private static boolean sUserRequired;
72
73    private static final Object sLock = new Object();
74
75    @GuardedBy("sLock")
76    private static volatile StorageVolume sPrimaryVolume;
77
78    private static StorageVolume getPrimaryVolume() {
79        if (SystemProperties.getBoolean("config.disable_storage", false)) {
80            return null;
81        }
82
83        if (sPrimaryVolume == null) {
84            synchronized (sLock) {
85                if (sPrimaryVolume == null) {
86                    try {
87                        IMountService mountService = IMountService.Stub.asInterface(ServiceManager
88                                .getService("mount"));
89                        final StorageVolume[] volumes = mountService.getVolumeList();
90                        sPrimaryVolume = StorageManager.getPrimaryVolume(volumes);
91                    } catch (Exception e) {
92                        Log.e(TAG, "couldn't talk to MountService", e);
93                    }
94                }
95            }
96        }
97        return sPrimaryVolume;
98    }
99
100    static {
101        initForCurrentUser();
102    }
103
104    /** {@hide} */
105    public static void initForCurrentUser() {
106        final int userId = UserHandle.myUserId();
107        sCurrentUser = new UserEnvironment(userId);
108
109        synchronized (sLock) {
110            sPrimaryVolume = null;
111        }
112    }
113
114    /** {@hide} */
115    public static class UserEnvironment {
116        // TODO: generalize further to create package-specific environment
117
118        /** External storage dirs, as visible to vold */
119        private final File[] mExternalDirsForVold;
120        /** External storage dirs, as visible to apps */
121        private final File[] mExternalDirsForApp;
122        /** Primary emulated storage dir for direct access */
123        private final File mEmulatedDirForDirect;
124
125        public UserEnvironment(int userId) {
126            // See storage config details at http://source.android.com/tech/storage/
127            String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
128            String rawEmulatedSource = System.getenv(ENV_EMULATED_STORAGE_SOURCE);
129            String rawEmulatedTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
130
131            String rawMediaStorage = System.getenv(ENV_MEDIA_STORAGE);
132            if (TextUtils.isEmpty(rawMediaStorage)) {
133                rawMediaStorage = "/data/media";
134            }
135
136            ArrayList<File> externalForVold = Lists.newArrayList();
137            ArrayList<File> externalForApp = Lists.newArrayList();
138
139            if (!TextUtils.isEmpty(rawEmulatedTarget)) {
140                // Device has emulated storage; external storage paths should have
141                // userId burned into them.
142                final String rawUserId = Integer.toString(userId);
143                final File emulatedSourceBase = new File(rawEmulatedSource);
144                final File emulatedTargetBase = new File(rawEmulatedTarget);
145                final File mediaBase = new File(rawMediaStorage);
146
147                // /storage/emulated/0
148                externalForVold.add(buildPath(emulatedSourceBase, rawUserId));
149                externalForApp.add(buildPath(emulatedTargetBase, rawUserId));
150                // /data/media/0
151                mEmulatedDirForDirect = buildPath(mediaBase, rawUserId);
152
153            } else {
154                // Device has physical external storage; use plain paths.
155                if (TextUtils.isEmpty(rawExternalStorage)) {
156                    Log.w(TAG, "EXTERNAL_STORAGE undefined; falling back to default");
157                    rawExternalStorage = "/storage/sdcard0";
158                }
159
160                // /storage/sdcard0
161                externalForVold.add(new File(rawExternalStorage));
162                externalForApp.add(new File(rawExternalStorage));
163                // /data/media
164                mEmulatedDirForDirect = new File(rawMediaStorage);
165            }
166
167            // Splice in any secondary storage paths, but only for owner
168            final String rawSecondaryStorage = System.getenv(ENV_SECONDARY_STORAGE);
169            if (!TextUtils.isEmpty(rawSecondaryStorage) && userId == UserHandle.USER_OWNER) {
170                for (String secondaryPath : rawSecondaryStorage.split(":")) {
171                    externalForVold.add(new File(secondaryPath));
172                    externalForApp.add(new File(secondaryPath));
173                }
174            }
175
176            mExternalDirsForVold = externalForVold.toArray(new File[externalForVold.size()]);
177            mExternalDirsForApp = externalForApp.toArray(new File[externalForApp.size()]);
178        }
179
180        @Deprecated
181        public File getExternalStorageDirectory() {
182            return mExternalDirsForApp[0];
183        }
184
185        @Deprecated
186        public File getExternalStoragePublicDirectory(String type) {
187            return buildExternalStoragePublicDirs(type)[0];
188        }
189
190        public File[] getExternalDirsForVold() {
191            return mExternalDirsForVold;
192        }
193
194        public File[] getExternalDirsForApp() {
195            return mExternalDirsForApp;
196        }
197
198        public File getMediaDir() {
199            return mEmulatedDirForDirect;
200        }
201
202        public File[] buildExternalStoragePublicDirs(String type) {
203            return buildPaths(mExternalDirsForApp, type);
204        }
205
206        public File[] buildExternalStorageAndroidDataDirs() {
207            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA);
208        }
209
210        public File[] buildExternalStorageAndroidObbDirs() {
211            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB);
212        }
213
214        public File[] buildExternalStorageAppDataDirs(String packageName) {
215            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName);
216        }
217
218        public File[] buildExternalStorageAppDataDirsForVold(String packageName) {
219            return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_DATA, packageName);
220        }
221
222        public File[] buildExternalStorageAppMediaDirs(String packageName) {
223            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName);
224        }
225
226        public File[] buildExternalStorageAppObbDirs(String packageName) {
227            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName);
228        }
229
230        public File[] buildExternalStorageAppObbDirsForVold(String packageName) {
231            return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_OBB, packageName);
232        }
233
234        public File[] buildExternalStorageAppFilesDirs(String packageName) {
235            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
236        }
237
238        public File[] buildExternalStorageAppCacheDirs(String packageName) {
239            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
240        }
241    }
242
243    /**
244     * Gets the Android root directory.
245     */
246    public static File getRootDirectory() {
247        return DIR_ANDROID_ROOT;
248    }
249
250    /**
251     * Return root directory of the "oem" partition holding OEM customizations,
252     * if any. If present, the partition is mounted read-only.
253     *
254     * @hide
255     */
256    public static File getOemDirectory() {
257        return DIR_OEM_ROOT;
258    }
259
260    /**
261     * Return root directory of the "vendor" partition that holds vendor-provided
262     * software that should persist across simple reflashing of the "system" partition.
263     * @hide
264     */
265    public static File getVendorDirectory() {
266        return DIR_VENDOR_ROOT;
267    }
268
269    /**
270     * Gets the system directory available for secure storage.
271     * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
272     * Otherwise, it returns the unencrypted /data/system directory.
273     * @return File object representing the secure storage system directory.
274     * @hide
275     */
276    public static File getSystemSecureDirectory() {
277        if (isEncryptedFilesystemEnabled()) {
278            return new File(SECURE_DATA_DIRECTORY, "system");
279        } else {
280            return new File(DATA_DIRECTORY, "system");
281        }
282    }
283
284    /**
285     * Gets the data directory for secure storage.
286     * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure).
287     * Otherwise, it returns the unencrypted /data directory.
288     * @return File object representing the data directory for secure storage.
289     * @hide
290     */
291    public static File getSecureDataDirectory() {
292        if (isEncryptedFilesystemEnabled()) {
293            return SECURE_DATA_DIRECTORY;
294        } else {
295            return DATA_DIRECTORY;
296        }
297    }
298
299    /**
300     * Return directory used for internal media storage, which is protected by
301     * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
302     *
303     * @hide
304     */
305    public static File getMediaStorageDirectory() {
306        throwIfUserRequired();
307        return sCurrentUser.getMediaDir();
308    }
309
310    /**
311     * Return the system directory for a user. This is for use by system services to store
312     * files relating to the user. This directory will be automatically deleted when the user
313     * is removed.
314     *
315     * @hide
316     */
317    public static File getUserSystemDirectory(int userId) {
318        return new File(new File(getSystemSecureDirectory(), "users"), Integer.toString(userId));
319    }
320
321    /**
322     * Returns the config directory for a user. This is for use by system services to store files
323     * relating to the user which should be readable by any app running as that user.
324     *
325     * @hide
326     */
327    public static File getUserConfigDirectory(int userId) {
328        return new File(new File(new File(
329                getDataDirectory(), "misc"), "user"), Integer.toString(userId));
330    }
331
332    /**
333     * Returns whether the Encrypted File System feature is enabled on the device or not.
334     * @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code>
335     * if disabled.
336     * @hide
337     */
338    public static boolean isEncryptedFilesystemEnabled() {
339        return SystemProperties.getBoolean(SYSTEM_PROPERTY_EFS_ENABLED, false);
340    }
341
342    private static final File DATA_DIRECTORY
343            = getDirectory("ANDROID_DATA", "/data");
344
345    /**
346     * @hide
347     */
348    private static final File SECURE_DATA_DIRECTORY
349            = getDirectory("ANDROID_SECURE_DATA", "/data/secure");
350
351    private static final File DOWNLOAD_CACHE_DIRECTORY = getDirectory("DOWNLOAD_CACHE", "/cache");
352
353    /**
354     * Return the user data directory.
355     */
356    public static File getDataDirectory() {
357        return DATA_DIRECTORY;
358    }
359
360    /**
361     * Return the primary external storage directory. This directory may not
362     * currently be accessible if it has been mounted by the user on their
363     * computer, has been removed from the device, or some other problem has
364     * happened. You can determine its current state with
365     * {@link #getExternalStorageState()}.
366     * <p>
367     * <em>Note: don't be confused by the word "external" here. This directory
368     * can better be thought as media/shared storage. It is a filesystem that
369     * can hold a relatively large amount of data and that is shared across all
370     * applications (does not enforce permissions). Traditionally this is an SD
371     * card, but it may also be implemented as built-in storage in a device that
372     * is distinct from the protected internal storage and can be mounted as a
373     * filesystem on a computer.</em>
374     * <p>
375     * On devices with multiple users (as described by {@link UserManager}),
376     * each user has their own isolated external storage. Applications only have
377     * access to the external storage for the user they're running as.
378     * <p>
379     * In devices with multiple "external" storage directories, this directory
380     * represents the "primary" external storage that the user will interact
381     * with. Access to secondary storage is available through
382     * <p>
383     * Applications should not directly use this top-level directory, in order
384     * to avoid polluting the user's root namespace. Any files that are private
385     * to the application should be placed in a directory returned by
386     * {@link android.content.Context#getExternalFilesDir
387     * Context.getExternalFilesDir}, which the system will take care of deleting
388     * if the application is uninstalled. Other shared files should be placed in
389     * one of the directories returned by
390     * {@link #getExternalStoragePublicDirectory}.
391     * <p>
392     * Writing to this path requires the
393     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission,
394     * and starting in read access requires the
395     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission,
396     * which is automatically granted if you hold the write permission.
397     * <p>
398     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, if your
399     * application only needs to store internal data, consider using
400     * {@link Context#getExternalFilesDir(String)} or
401     * {@link Context#getExternalCacheDir()}, which require no permissions to
402     * read or write.
403     * <p>
404     * This path may change between platform versions, so applications should
405     * only persist relative paths.
406     * <p>
407     * Here is an example of typical code to monitor the state of external
408     * storage:
409     * <p>
410     * {@sample
411     * development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
412     * monitor_storage}
413     *
414     * @see #getExternalStorageState()
415     * @see #isExternalStorageRemovable()
416     */
417    public static File getExternalStorageDirectory() {
418        throwIfUserRequired();
419        return sCurrentUser.getExternalDirsForApp()[0];
420    }
421
422    /** {@hide} */
423    public static File getLegacyExternalStorageDirectory() {
424        return new File(System.getenv(ENV_EXTERNAL_STORAGE));
425    }
426
427    /** {@hide} */
428    public static File getLegacyExternalStorageObbDirectory() {
429        return buildPath(getLegacyExternalStorageDirectory(), DIR_ANDROID, DIR_OBB);
430    }
431
432    /** {@hide} */
433    public static File getEmulatedStorageSource(int userId) {
434        // /mnt/shell/emulated/0
435        return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), String.valueOf(userId));
436    }
437
438    /** {@hide} */
439    public static File getEmulatedStorageObbSource() {
440        // /mnt/shell/emulated/obb
441        return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), DIR_OBB);
442    }
443
444    /**
445     * Standard directory in which to place any audio files that should be
446     * in the regular list of music for the user.
447     * This may be combined with
448     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
449     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
450     * of directories to categories a particular audio file as more than one
451     * type.
452     */
453    public static String DIRECTORY_MUSIC = "Music";
454
455    /**
456     * Standard directory in which to place any audio files that should be
457     * in the list of podcasts that the user can select (not as regular
458     * music).
459     * This may be combined with {@link #DIRECTORY_MUSIC},
460     * {@link #DIRECTORY_NOTIFICATIONS},
461     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
462     * of directories to categories a particular audio file as more than one
463     * type.
464     */
465    public static String DIRECTORY_PODCASTS = "Podcasts";
466
467    /**
468     * Standard directory in which to place any audio files that should be
469     * in the list of ringtones that the user can select (not as regular
470     * music).
471     * This may be combined with {@link #DIRECTORY_MUSIC},
472     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and
473     * {@link #DIRECTORY_ALARMS} as a series
474     * of directories to categories a particular audio file as more than one
475     * type.
476     */
477    public static String DIRECTORY_RINGTONES = "Ringtones";
478
479    /**
480     * Standard directory in which to place any audio files that should be
481     * in the list of alarms that the user can select (not as regular
482     * music).
483     * This may be combined with {@link #DIRECTORY_MUSIC},
484     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
485     * and {@link #DIRECTORY_RINGTONES} as a series
486     * of directories to categories a particular audio file as more than one
487     * type.
488     */
489    public static String DIRECTORY_ALARMS = "Alarms";
490
491    /**
492     * Standard directory in which to place any audio files that should be
493     * in the list of notifications that the user can select (not as regular
494     * music).
495     * This may be combined with {@link #DIRECTORY_MUSIC},
496     * {@link #DIRECTORY_PODCASTS},
497     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
498     * of directories to categories a particular audio file as more than one
499     * type.
500     */
501    public static String DIRECTORY_NOTIFICATIONS = "Notifications";
502
503    /**
504     * Standard directory in which to place pictures that are available to
505     * the user.  Note that this is primarily a convention for the top-level
506     * public directory, as the media scanner will find and collect pictures
507     * in any directory.
508     */
509    public static String DIRECTORY_PICTURES = "Pictures";
510
511    /**
512     * Standard directory in which to place movies that are available to
513     * the user.  Note that this is primarily a convention for the top-level
514     * public directory, as the media scanner will find and collect movies
515     * in any directory.
516     */
517    public static String DIRECTORY_MOVIES = "Movies";
518
519    /**
520     * Standard directory in which to place files that have been downloaded by
521     * the user.  Note that this is primarily a convention for the top-level
522     * public directory, you are free to download files anywhere in your own
523     * private directories.  Also note that though the constant here is
524     * named DIRECTORY_DOWNLOADS (plural), the actual file name is non-plural for
525     * backwards compatibility reasons.
526     */
527    public static String DIRECTORY_DOWNLOADS = "Download";
528
529    /**
530     * The traditional location for pictures and videos when mounting the
531     * device as a camera.  Note that this is primarily a convention for the
532     * top-level public directory, as this convention makes no sense elsewhere.
533     */
534    public static String DIRECTORY_DCIM = "DCIM";
535
536    /**
537     * Standard directory in which to place documents that have been created by
538     * the user.
539     */
540    public static String DIRECTORY_DOCUMENTS = "Documents";
541
542    /**
543     * Get a top-level public external storage directory for placing files of
544     * a particular type.  This is where the user will typically place and
545     * manage their own files, so you should be careful about what you put here
546     * to ensure you don't erase their files or get in the way of their own
547     * organization.
548     *
549     * <p>On devices with multiple users (as described by {@link UserManager}),
550     * each user has their own isolated external storage. Applications only
551     * have access to the external storage for the user they're running as.</p>
552     *
553     * <p>Here is an example of typical code to manipulate a picture on
554     * the public external storage:</p>
555     *
556     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
557     * public_picture}
558     *
559     * @param type The type of storage directory to return.  Should be one of
560     * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
561     * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
562     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
563     * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
564     * {@link #DIRECTORY_DCIM}.  May not be null.
565     *
566     * @return Returns the File path for the directory.  Note that this
567     * directory may not yet exist, so you must make sure it exists before
568     * using it such as with {@link File#mkdirs File.mkdirs()}.
569     */
570    public static File getExternalStoragePublicDirectory(String type) {
571        throwIfUserRequired();
572        return sCurrentUser.buildExternalStoragePublicDirs(type)[0];
573    }
574
575    /**
576     * Returns the path for android-specific data on the SD card.
577     * @hide
578     */
579    public static File[] buildExternalStorageAndroidDataDirs() {
580        throwIfUserRequired();
581        return sCurrentUser.buildExternalStorageAndroidDataDirs();
582    }
583
584    /**
585     * Generates the raw path to an application's data
586     * @hide
587     */
588    public static File[] buildExternalStorageAppDataDirs(String packageName) {
589        throwIfUserRequired();
590        return sCurrentUser.buildExternalStorageAppDataDirs(packageName);
591    }
592
593    /**
594     * Generates the raw path to an application's media
595     * @hide
596     */
597    public static File[] buildExternalStorageAppMediaDirs(String packageName) {
598        throwIfUserRequired();
599        return sCurrentUser.buildExternalStorageAppMediaDirs(packageName);
600    }
601
602    /**
603     * Generates the raw path to an application's OBB files
604     * @hide
605     */
606    public static File[] buildExternalStorageAppObbDirs(String packageName) {
607        throwIfUserRequired();
608        return sCurrentUser.buildExternalStorageAppObbDirs(packageName);
609    }
610
611    /**
612     * Generates the path to an application's files.
613     * @hide
614     */
615    public static File[] buildExternalStorageAppFilesDirs(String packageName) {
616        throwIfUserRequired();
617        return sCurrentUser.buildExternalStorageAppFilesDirs(packageName);
618    }
619
620    /**
621     * Generates the path to an application's cache.
622     * @hide
623     */
624    public static File[] buildExternalStorageAppCacheDirs(String packageName) {
625        throwIfUserRequired();
626        return sCurrentUser.buildExternalStorageAppCacheDirs(packageName);
627    }
628
629    /**
630     * Return the download/cache content directory.
631     */
632    public static File getDownloadCacheDirectory() {
633        return DOWNLOAD_CACHE_DIRECTORY;
634    }
635
636    /**
637     * Unknown storage state, such as when a path isn't backed by known storage
638     * media.
639     *
640     * @see #getStorageState(File)
641     */
642    public static final String MEDIA_UNKNOWN = "unknown";
643
644    /**
645     * Storage state if the media is not present.
646     *
647     * @see #getStorageState(File)
648     */
649    public static final String MEDIA_REMOVED = "removed";
650
651    /**
652     * Storage state if the media is present but not mounted.
653     *
654     * @see #getStorageState(File)
655     */
656    public static final String MEDIA_UNMOUNTED = "unmounted";
657
658    /**
659     * Storage state if the media is present and being disk-checked.
660     *
661     * @see #getStorageState(File)
662     */
663    public static final String MEDIA_CHECKING = "checking";
664
665    /**
666     * Storage state if the media is present but is blank or is using an
667     * unsupported filesystem.
668     *
669     * @see #getStorageState(File)
670     */
671    public static final String MEDIA_NOFS = "nofs";
672
673    /**
674     * Storage state if the media is present and mounted at its mount point with
675     * read/write access.
676     *
677     * @see #getStorageState(File)
678     */
679    public static final String MEDIA_MOUNTED = "mounted";
680
681    /**
682     * Storage state if the media is present and mounted at its mount point with
683     * read-only access.
684     *
685     * @see #getStorageState(File)
686     */
687    public static final String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
688
689    /**
690     * Storage state if the media is present not mounted, and shared via USB
691     * mass storage.
692     *
693     * @see #getStorageState(File)
694     */
695    public static final String MEDIA_SHARED = "shared";
696
697    /**
698     * Storage state if the media was removed before it was unmounted.
699     *
700     * @see #getStorageState(File)
701     */
702    public static final String MEDIA_BAD_REMOVAL = "bad_removal";
703
704    /**
705     * Storage state if the media is present but cannot be mounted. Typically
706     * this happens if the file system on the media is corrupted.
707     *
708     * @see #getStorageState(File)
709     */
710    public static final String MEDIA_UNMOUNTABLE = "unmountable";
711
712    /**
713     * Returns the current state of the primary "external" storage device.
714     *
715     * @see #getExternalStorageDirectory()
716     * @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
717     *         {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING},
718     *         {@link #MEDIA_NOFS}, {@link #MEDIA_MOUNTED},
719     *         {@link #MEDIA_MOUNTED_READ_ONLY}, {@link #MEDIA_SHARED},
720     *         {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
721     */
722    public static String getExternalStorageState() {
723        final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
724        return getStorageState(externalDir);
725    }
726
727    /**
728     * Returns the current state of the storage device that provides the given
729     * path.
730     *
731     * @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
732     *         {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING},
733     *         {@link #MEDIA_NOFS}, {@link #MEDIA_MOUNTED},
734     *         {@link #MEDIA_MOUNTED_READ_ONLY}, {@link #MEDIA_SHARED},
735     *         {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
736     */
737    public static String getStorageState(File path) {
738        final String rawPath;
739        try {
740            rawPath = path.getCanonicalPath();
741        } catch (IOException e) {
742            Log.w(TAG, "Failed to resolve target path: " + e);
743            return Environment.MEDIA_UNKNOWN;
744        }
745
746        try {
747            final IMountService mountService = IMountService.Stub.asInterface(
748                    ServiceManager.getService("mount"));
749            final StorageVolume[] volumes = mountService.getVolumeList();
750            for (StorageVolume volume : volumes) {
751                if (rawPath.startsWith(volume.getPath())) {
752                    return mountService.getVolumeState(volume.getPath());
753                }
754            }
755        } catch (RemoteException e) {
756            Log.w(TAG, "Failed to find external storage state: " + e);
757        }
758        return Environment.MEDIA_UNKNOWN;
759    }
760
761    /**
762     * Returns whether the primary "external" storage device is removable.
763     * If true is returned, this device is for example an SD card that the
764     * user can remove.  If false is returned, the storage is built into
765     * the device and can not be physically removed.
766     *
767     * <p>See {@link #getExternalStorageDirectory()} for more information.
768     */
769    public static boolean isExternalStorageRemovable() {
770        final StorageVolume primary = getPrimaryVolume();
771        return (primary != null && primary.isRemovable());
772    }
773
774    /**
775     * Returns whether the device has an external storage device which is
776     * emulated. If true, the device does not have real external storage, and the directory
777     * returned by {@link #getExternalStorageDirectory()} will be allocated using a portion of
778     * the internal storage system.
779     *
780     * <p>Certain system services, such as the package manager, use this
781     * to determine where to install an application.
782     *
783     * <p>Emulated external storage may also be encrypted - see
784     * {@link android.app.admin.DevicePolicyManager#setStorageEncryption(
785     * android.content.ComponentName, boolean)} for additional details.
786     */
787    public static boolean isExternalStorageEmulated() {
788        final StorageVolume primary = getPrimaryVolume();
789        return (primary != null && primary.isEmulated());
790    }
791
792    static File getDirectory(String variableName, String defaultPath) {
793        String path = System.getenv(variableName);
794        return path == null ? new File(defaultPath) : new File(path);
795    }
796
797    private static String getCanonicalPathOrNull(String variableName) {
798        String path = System.getenv(variableName);
799        if (path == null) {
800            return null;
801        }
802        try {
803            return new File(path).getCanonicalPath();
804        } catch (IOException e) {
805            Log.w(TAG, "Unable to resolve canonical path for " + path);
806            return null;
807        }
808    }
809
810    /** {@hide} */
811    public static void setUserRequired(boolean userRequired) {
812        sUserRequired = userRequired;
813    }
814
815    private static void throwIfUserRequired() {
816        if (sUserRequired) {
817            Log.wtf(TAG, "Path requests must specify a user by using UserEnvironment",
818                    new Throwable());
819        }
820    }
821
822    /**
823     * Append path segments to each given base path, returning result.
824     *
825     * @hide
826     */
827    public static File[] buildPaths(File[] base, String... segments) {
828        File[] result = new File[base.length];
829        for (int i = 0; i < base.length; i++) {
830            result[i] = buildPath(base[i], segments);
831        }
832        return result;
833    }
834
835    /**
836     * Append path segments to given base path, returning result.
837     *
838     * @hide
839     */
840    public static File buildPath(File base, String... segments) {
841        File cur = base;
842        for (String segment : segments) {
843            if (cur == null) {
844                cur = new File(segment);
845            } else {
846                cur = new File(cur, segment);
847            }
848        }
849        return cur;
850    }
851
852    /**
853     * If the given path exists on emulated external storage, return the
854     * translated backing path hosted on internal storage. This bypasses any
855     * emulation later, improving performance. This is <em>only</em> suitable
856     * for read-only access.
857     * <p>
858     * Returns original path if given path doesn't meet these criteria. Callers
859     * must hold {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}
860     * permission.
861     *
862     * @hide
863     */
864    public static File maybeTranslateEmulatedPathToInternal(File path) {
865        // Fast return if not emulated, or missing variables
866        if (!Environment.isExternalStorageEmulated()
867                || CANONCIAL_EMULATED_STORAGE_TARGET == null) {
868            return path;
869        }
870
871        try {
872            final String rawPath = path.getCanonicalPath();
873            if (rawPath.startsWith(CANONCIAL_EMULATED_STORAGE_TARGET)) {
874                final File internalPath = new File(DIR_MEDIA_STORAGE,
875                        rawPath.substring(CANONCIAL_EMULATED_STORAGE_TARGET.length()));
876                if (internalPath.exists()) {
877                    return internalPath;
878                }
879            }
880        } catch (IOException e) {
881            Log.w(TAG, "Failed to resolve canonical path for " + path);
882        }
883
884        // Unable to translate to internal path; use original
885        return path;
886    }
887}
888