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