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