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