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