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