StorageManager.java revision 19d9c2d03c478c755eddbe7ac55d0dc778f332fd
1/* 2 * Copyright (C) 2008 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.storage; 18 19import static android.net.TrafficStats.MB_IN_BYTES; 20 21import android.annotation.NonNull; 22import android.annotation.Nullable; 23import android.annotation.SdkConstant; 24import android.app.ActivityThread; 25import android.content.ContentResolver; 26import android.content.Context; 27import android.content.pm.IPackageMoveObserver; 28import android.content.pm.PackageManager; 29import android.os.Binder; 30import android.os.Environment; 31import android.os.FileUtils; 32import android.os.Handler; 33import android.os.Looper; 34import android.os.Message; 35import android.os.ParcelFileDescriptor; 36import android.os.RemoteException; 37import android.os.ServiceManager; 38import android.os.SystemProperties; 39import android.os.UserHandle; 40import android.provider.Settings; 41import android.text.TextUtils; 42import android.util.Log; 43import android.util.Pair; 44import android.util.Slog; 45import android.util.SparseArray; 46 47import com.android.internal.os.SomeArgs; 48import com.android.internal.util.Preconditions; 49 50import java.io.BufferedReader; 51import java.io.File; 52import java.io.FileInputStream; 53import java.io.FileNotFoundException; 54import java.io.IOException; 55import java.io.InputStreamReader; 56import java.lang.ref.WeakReference; 57import java.util.ArrayList; 58import java.util.Arrays; 59import java.util.Collections; 60import java.util.Iterator; 61import java.util.List; 62import java.util.Objects; 63import java.util.concurrent.atomic.AtomicInteger; 64 65/** 66 * StorageManager is the interface to the systems storage service. The storage 67 * manager handles storage-related items such as Opaque Binary Blobs (OBBs). 68 * <p> 69 * OBBs contain a filesystem that maybe be encrypted on disk and mounted 70 * on-demand from an application. OBBs are a good way of providing large amounts 71 * of binary assets without packaging them into APKs as they may be multiple 72 * gigabytes in size. However, due to their size, they're most likely stored in 73 * a shared storage pool accessible from all programs. The system does not 74 * guarantee the security of the OBB file itself: if any program modifies the 75 * OBB, there is no guarantee that a read from that OBB will produce the 76 * expected output. 77 * <p> 78 * Get an instance of this class by calling 79 * {@link android.content.Context#getSystemService(java.lang.String)} with an 80 * argument of {@link android.content.Context#STORAGE_SERVICE}. 81 */ 82public class StorageManager { 83 private static final String TAG = "StorageManager"; 84 85 /** {@hide} */ 86 public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; 87 /** {@hide} */ 88 public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable"; 89 /** {@hide} */ 90 public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; 91 /** {@hide} */ 92 public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe"; 93 /** {@hide} */ 94 public static final String PROP_SDCARDFS = "persist.sys.sdcardfs"; 95 96 /** {@hide} */ 97 public static final String UUID_PRIVATE_INTERNAL = null; 98 /** {@hide} */ 99 public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; 100 101 102 /** 103 * Activity Action: Allows the user to manage their storage. This activity provides the ability 104 * to free up space on the device by deleting data such as apps. 105 * <p> 106 * Input: Nothing. 107 * <p> 108 * Output: Nothing. 109 */ 110 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 111 public static final String ACTION_MANAGE_STORAGE 112 = "android.os.storage.action.MANAGE_STORAGE"; 113 114 /** {@hide} */ 115 public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0; 116 /** {@hide} */ 117 public static final int DEBUG_EMULATE_FBE = 1 << 1; 118 /** {@hide} */ 119 public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2; 120 /** {@hide} */ 121 public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3; 122 123 // NOTE: keep in sync with installd 124 /** {@hide} */ 125 public static final int FLAG_STORAGE_DE = 1 << 0; 126 /** {@hide} */ 127 public static final int FLAG_STORAGE_CE = 1 << 1; 128 129 /** {@hide} */ 130 public static final int FLAG_FOR_WRITE = 1 << 8; 131 /** {@hide} */ 132 public static final int FLAG_REAL_STATE = 1 << 9; 133 /** {@hide} */ 134 public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10; 135 136 private static volatile IMountService sMountService = null; 137 138 // TODO: the location of the primary storage block varies from device to device, so we need to 139 // try the most likely candidates - a long-term solution would be a device-specific vold 140 // function that returns the calculated size. 141 private static final String[] INTERNAL_STORAGE_SIZE_PATHS = { 142 "/sys/block/mmcblk0/size", 143 "/sys/block/sda/size" 144 }; 145 private static final int INTERNAL_STORAGE_SECTOR_SIZE = 512; 146 147 private final Context mContext; 148 private final ContentResolver mResolver; 149 150 private final IMountService mMountService; 151 private final Looper mLooper; 152 private final AtomicInteger mNextNonce = new AtomicInteger(0); 153 154 private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>(); 155 156 private static class StorageEventListenerDelegate extends IMountServiceListener.Stub implements 157 Handler.Callback { 158 private static final int MSG_STORAGE_STATE_CHANGED = 1; 159 private static final int MSG_VOLUME_STATE_CHANGED = 2; 160 private static final int MSG_VOLUME_RECORD_CHANGED = 3; 161 private static final int MSG_VOLUME_FORGOTTEN = 4; 162 private static final int MSG_DISK_SCANNED = 5; 163 private static final int MSG_DISK_DESTROYED = 6; 164 165 final StorageEventListener mCallback; 166 final Handler mHandler; 167 168 public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) { 169 mCallback = callback; 170 mHandler = new Handler(looper, this); 171 } 172 173 @Override 174 public boolean handleMessage(Message msg) { 175 final SomeArgs args = (SomeArgs) msg.obj; 176 switch (msg.what) { 177 case MSG_STORAGE_STATE_CHANGED: 178 mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2, 179 (String) args.arg3); 180 args.recycle(); 181 return true; 182 case MSG_VOLUME_STATE_CHANGED: 183 mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); 184 args.recycle(); 185 return true; 186 case MSG_VOLUME_RECORD_CHANGED: 187 mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1); 188 args.recycle(); 189 return true; 190 case MSG_VOLUME_FORGOTTEN: 191 mCallback.onVolumeForgotten((String) args.arg1); 192 args.recycle(); 193 return true; 194 case MSG_DISK_SCANNED: 195 mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2); 196 args.recycle(); 197 return true; 198 case MSG_DISK_DESTROYED: 199 mCallback.onDiskDestroyed((DiskInfo) args.arg1); 200 args.recycle(); 201 return true; 202 } 203 args.recycle(); 204 return false; 205 } 206 207 @Override 208 public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException { 209 // Ignored 210 } 211 212 @Override 213 public void onStorageStateChanged(String path, String oldState, String newState) { 214 final SomeArgs args = SomeArgs.obtain(); 215 args.arg1 = path; 216 args.arg2 = oldState; 217 args.arg3 = newState; 218 mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); 219 } 220 221 @Override 222 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 223 final SomeArgs args = SomeArgs.obtain(); 224 args.arg1 = vol; 225 args.argi2 = oldState; 226 args.argi3 = newState; 227 mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); 228 } 229 230 @Override 231 public void onVolumeRecordChanged(VolumeRecord rec) { 232 final SomeArgs args = SomeArgs.obtain(); 233 args.arg1 = rec; 234 mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); 235 } 236 237 @Override 238 public void onVolumeForgotten(String fsUuid) { 239 final SomeArgs args = SomeArgs.obtain(); 240 args.arg1 = fsUuid; 241 mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); 242 } 243 244 @Override 245 public void onDiskScanned(DiskInfo disk, int volumeCount) { 246 final SomeArgs args = SomeArgs.obtain(); 247 args.arg1 = disk; 248 args.argi2 = volumeCount; 249 mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); 250 } 251 252 @Override 253 public void onDiskDestroyed(DiskInfo disk) throws RemoteException { 254 final SomeArgs args = SomeArgs.obtain(); 255 args.arg1 = disk; 256 mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget(); 257 } 258 } 259 260 /** 261 * Binder listener for OBB action results. 262 */ 263 private final ObbActionListener mObbActionListener = new ObbActionListener(); 264 265 private class ObbActionListener extends IObbActionListener.Stub { 266 @SuppressWarnings("hiding") 267 private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>(); 268 269 @Override 270 public void onObbResult(String filename, int nonce, int status) { 271 final ObbListenerDelegate delegate; 272 synchronized (mListeners) { 273 delegate = mListeners.get(nonce); 274 if (delegate != null) { 275 mListeners.remove(nonce); 276 } 277 } 278 279 if (delegate != null) { 280 delegate.sendObbStateChanged(filename, status); 281 } 282 } 283 284 public int addListener(OnObbStateChangeListener listener) { 285 final ObbListenerDelegate delegate = new ObbListenerDelegate(listener); 286 287 synchronized (mListeners) { 288 mListeners.put(delegate.nonce, delegate); 289 } 290 291 return delegate.nonce; 292 } 293 } 294 295 private int getNextNonce() { 296 return mNextNonce.getAndIncrement(); 297 } 298 299 /** 300 * Private class containing sender and receiver code for StorageEvents. 301 */ 302 private class ObbListenerDelegate { 303 private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef; 304 private final Handler mHandler; 305 306 private final int nonce; 307 308 ObbListenerDelegate(OnObbStateChangeListener listener) { 309 nonce = getNextNonce(); 310 mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener); 311 mHandler = new Handler(mLooper) { 312 @Override 313 public void handleMessage(Message msg) { 314 final OnObbStateChangeListener changeListener = getListener(); 315 if (changeListener == null) { 316 return; 317 } 318 319 changeListener.onObbStateChange((String) msg.obj, msg.arg1); 320 } 321 }; 322 } 323 324 OnObbStateChangeListener getListener() { 325 if (mObbEventListenerRef == null) { 326 return null; 327 } 328 return mObbEventListenerRef.get(); 329 } 330 331 void sendObbStateChanged(String path, int state) { 332 mHandler.obtainMessage(0, state, 0, path).sendToTarget(); 333 } 334 } 335 336 /** {@hide} */ 337 @Deprecated 338 public static StorageManager from(Context context) { 339 return context.getSystemService(StorageManager.class); 340 } 341 342 /** 343 * Constructs a StorageManager object through which an application can 344 * can communicate with the systems mount service. 345 * 346 * @param tgtLooper The {@link android.os.Looper} which events will be received on. 347 * 348 * <p>Applications can get instance of this class by calling 349 * {@link android.content.Context#getSystemService(java.lang.String)} with an argument 350 * of {@link android.content.Context#STORAGE_SERVICE}. 351 * 352 * @hide 353 */ 354 public StorageManager(Context context, Looper looper) { 355 mContext = context; 356 mResolver = context.getContentResolver(); 357 mLooper = looper; 358 mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); 359 if (mMountService == null) { 360 throw new IllegalStateException("Failed to find running mount service"); 361 } 362 } 363 364 /** 365 * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}. 366 * 367 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 368 * 369 * @hide 370 */ 371 public void registerListener(StorageEventListener listener) { 372 synchronized (mDelegates) { 373 final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener, 374 mLooper); 375 try { 376 mMountService.registerListener(delegate); 377 } catch (RemoteException e) { 378 throw e.rethrowFromSystemServer(); 379 } 380 mDelegates.add(delegate); 381 } 382 } 383 384 /** 385 * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}. 386 * 387 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 388 * 389 * @hide 390 */ 391 public void unregisterListener(StorageEventListener listener) { 392 synchronized (mDelegates) { 393 for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) { 394 final StorageEventListenerDelegate delegate = i.next(); 395 if (delegate.mCallback == listener) { 396 try { 397 mMountService.unregisterListener(delegate); 398 } catch (RemoteException e) { 399 throw e.rethrowFromSystemServer(); 400 } 401 i.remove(); 402 } 403 } 404 } 405 } 406 407 /** 408 * Enables USB Mass Storage (UMS) on the device. 409 * 410 * @hide 411 */ 412 @Deprecated 413 public void enableUsbMassStorage() { 414 } 415 416 /** 417 * Disables USB Mass Storage (UMS) on the device. 418 * 419 * @hide 420 */ 421 @Deprecated 422 public void disableUsbMassStorage() { 423 } 424 425 /** 426 * Query if a USB Mass Storage (UMS) host is connected. 427 * @return true if UMS host is connected. 428 * 429 * @hide 430 */ 431 @Deprecated 432 public boolean isUsbMassStorageConnected() { 433 return false; 434 } 435 436 /** 437 * Query if a USB Mass Storage (UMS) is enabled on the device. 438 * @return true if UMS host is enabled. 439 * 440 * @hide 441 */ 442 @Deprecated 443 public boolean isUsbMassStorageEnabled() { 444 return false; 445 } 446 447 /** 448 * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is 449 * specified, it is supplied to the mounting process to be used in any 450 * encryption used in the OBB. 451 * <p> 452 * The OBB will remain mounted for as long as the StorageManager reference 453 * is held by the application. As soon as this reference is lost, the OBBs 454 * in use will be unmounted. The {@link OnObbStateChangeListener} registered 455 * with this call will receive the success or failure of this operation. 456 * <p> 457 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 458 * file matches a package ID that is owned by the calling program's UID. 459 * That is, shared UID applications can attempt to mount any other 460 * application's OBB that shares its UID. 461 * 462 * @param rawPath the path to the OBB file 463 * @param key secret used to encrypt the OBB; may be <code>null</code> if no 464 * encryption was used on the OBB. 465 * @param listener will receive the success or failure of the operation 466 * @return whether the mount call was successfully queued or not 467 */ 468 public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) { 469 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 470 Preconditions.checkNotNull(listener, "listener cannot be null"); 471 472 try { 473 final String canonicalPath = new File(rawPath).getCanonicalPath(); 474 final int nonce = mObbActionListener.addListener(listener); 475 mMountService.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce); 476 return true; 477 } catch (IOException e) { 478 throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e); 479 } catch (RemoteException e) { 480 throw e.rethrowFromSystemServer(); 481 } 482 } 483 484 /** 485 * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the 486 * <code>force</code> flag is true, it will kill any application needed to 487 * unmount the given OBB (even the calling application). 488 * <p> 489 * The {@link OnObbStateChangeListener} registered with this call will 490 * receive the success or failure of this operation. 491 * <p> 492 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 493 * file matches a package ID that is owned by the calling program's UID. 494 * That is, shared UID applications can obtain access to any other 495 * application's OBB that shares its UID. 496 * <p> 497 * 498 * @param rawPath path to the OBB file 499 * @param force whether to kill any programs using this in order to unmount 500 * it 501 * @param listener will receive the success or failure of the operation 502 * @return whether the unmount call was successfully queued or not 503 */ 504 public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) { 505 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 506 Preconditions.checkNotNull(listener, "listener cannot be null"); 507 508 try { 509 final int nonce = mObbActionListener.addListener(listener); 510 mMountService.unmountObb(rawPath, force, mObbActionListener, nonce); 511 return true; 512 } catch (RemoteException e) { 513 throw e.rethrowFromSystemServer(); 514 } 515 } 516 517 /** 518 * Check whether an Opaque Binary Blob (OBB) is mounted or not. 519 * 520 * @param rawPath path to OBB image 521 * @return true if OBB is mounted; false if not mounted or on error 522 */ 523 public boolean isObbMounted(String rawPath) { 524 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 525 526 try { 527 return mMountService.isObbMounted(rawPath); 528 } catch (RemoteException e) { 529 throw e.rethrowFromSystemServer(); 530 } 531 } 532 533 /** 534 * Check the mounted path of an Opaque Binary Blob (OBB) file. This will 535 * give you the path to where you can obtain access to the internals of the 536 * OBB. 537 * 538 * @param rawPath path to OBB image 539 * @return absolute path to mounted OBB image data or <code>null</code> if 540 * not mounted or exception encountered trying to read status 541 */ 542 public String getMountedObbPath(String rawPath) { 543 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 544 545 try { 546 return mMountService.getMountedObbPath(rawPath); 547 } catch (RemoteException e) { 548 throw e.rethrowFromSystemServer(); 549 } 550 } 551 552 /** {@hide} */ 553 public @NonNull List<DiskInfo> getDisks() { 554 try { 555 return Arrays.asList(mMountService.getDisks()); 556 } catch (RemoteException e) { 557 throw e.rethrowFromSystemServer(); 558 } 559 } 560 561 /** {@hide} */ 562 public @Nullable DiskInfo findDiskById(String id) { 563 Preconditions.checkNotNull(id); 564 // TODO; go directly to service to make this faster 565 for (DiskInfo disk : getDisks()) { 566 if (Objects.equals(disk.id, id)) { 567 return disk; 568 } 569 } 570 return null; 571 } 572 573 /** {@hide} */ 574 public @Nullable VolumeInfo findVolumeById(String id) { 575 Preconditions.checkNotNull(id); 576 // TODO; go directly to service to make this faster 577 for (VolumeInfo vol : getVolumes()) { 578 if (Objects.equals(vol.id, id)) { 579 return vol; 580 } 581 } 582 return null; 583 } 584 585 /** {@hide} */ 586 public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) { 587 Preconditions.checkNotNull(fsUuid); 588 // TODO; go directly to service to make this faster 589 for (VolumeInfo vol : getVolumes()) { 590 if (Objects.equals(vol.fsUuid, fsUuid)) { 591 return vol; 592 } 593 } 594 return null; 595 } 596 597 /** {@hide} */ 598 public @Nullable VolumeRecord findRecordByUuid(String fsUuid) { 599 Preconditions.checkNotNull(fsUuid); 600 // TODO; go directly to service to make this faster 601 for (VolumeRecord rec : getVolumeRecords()) { 602 if (Objects.equals(rec.fsUuid, fsUuid)) { 603 return rec; 604 } 605 } 606 return null; 607 } 608 609 /** {@hide} */ 610 public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) { 611 if (emulatedVol != null) { 612 return findVolumeById(emulatedVol.getId().replace("emulated", "private")); 613 } else { 614 return null; 615 } 616 } 617 618 /** {@hide} */ 619 public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) { 620 if (privateVol != null) { 621 return findVolumeById(privateVol.getId().replace("private", "emulated")); 622 } else { 623 return null; 624 } 625 } 626 627 /** {@hide} */ 628 public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) { 629 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { 630 return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); 631 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 632 return getPrimaryPhysicalVolume(); 633 } else { 634 return findVolumeByUuid(volumeUuid); 635 } 636 } 637 638 /** {@hide} */ 639 public @NonNull List<VolumeInfo> getVolumes() { 640 try { 641 return Arrays.asList(mMountService.getVolumes(0)); 642 } catch (RemoteException e) { 643 throw e.rethrowFromSystemServer(); 644 } 645 } 646 647 /** {@hide} */ 648 public @NonNull List<VolumeInfo> getWritablePrivateVolumes() { 649 try { 650 final ArrayList<VolumeInfo> res = new ArrayList<>(); 651 for (VolumeInfo vol : mMountService.getVolumes(0)) { 652 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) { 653 res.add(vol); 654 } 655 } 656 return res; 657 } catch (RemoteException e) { 658 throw e.rethrowFromSystemServer(); 659 } 660 } 661 662 /** {@hide} */ 663 public @NonNull List<VolumeRecord> getVolumeRecords() { 664 try { 665 return Arrays.asList(mMountService.getVolumeRecords(0)); 666 } catch (RemoteException e) { 667 throw e.rethrowFromSystemServer(); 668 } 669 } 670 671 /** {@hide} */ 672 public @Nullable String getBestVolumeDescription(VolumeInfo vol) { 673 if (vol == null) return null; 674 675 // Nickname always takes precedence when defined 676 if (!TextUtils.isEmpty(vol.fsUuid)) { 677 final VolumeRecord rec = findRecordByUuid(vol.fsUuid); 678 if (rec != null && !TextUtils.isEmpty(rec.nickname)) { 679 return rec.nickname; 680 } 681 } 682 683 if (!TextUtils.isEmpty(vol.getDescription())) { 684 return vol.getDescription(); 685 } 686 687 if (vol.disk != null) { 688 return vol.disk.getDescription(); 689 } 690 691 return null; 692 } 693 694 /** {@hide} */ 695 public @Nullable VolumeInfo getPrimaryPhysicalVolume() { 696 final List<VolumeInfo> vols = getVolumes(); 697 for (VolumeInfo vol : vols) { 698 if (vol.isPrimaryPhysical()) { 699 return vol; 700 } 701 } 702 return null; 703 } 704 705 /** {@hide} */ 706 public void mount(String volId) { 707 try { 708 mMountService.mount(volId); 709 } catch (RemoteException e) { 710 throw e.rethrowFromSystemServer(); 711 } 712 } 713 714 /** {@hide} */ 715 public void unmount(String volId) { 716 try { 717 mMountService.unmount(volId); 718 } catch (RemoteException e) { 719 throw e.rethrowFromSystemServer(); 720 } 721 } 722 723 /** {@hide} */ 724 public void format(String volId) { 725 try { 726 mMountService.format(volId); 727 } catch (RemoteException e) { 728 throw e.rethrowFromSystemServer(); 729 } 730 } 731 732 /** {@hide} */ 733 public long benchmark(String volId) { 734 try { 735 return mMountService.benchmark(volId); 736 } catch (RemoteException e) { 737 throw e.rethrowFromSystemServer(); 738 } 739 } 740 741 /** {@hide} */ 742 public void partitionPublic(String diskId) { 743 try { 744 mMountService.partitionPublic(diskId); 745 } catch (RemoteException e) { 746 throw e.rethrowFromSystemServer(); 747 } 748 } 749 750 /** {@hide} */ 751 public void partitionPrivate(String diskId) { 752 try { 753 mMountService.partitionPrivate(diskId); 754 } catch (RemoteException e) { 755 throw e.rethrowFromSystemServer(); 756 } 757 } 758 759 /** {@hide} */ 760 public void partitionMixed(String diskId, int ratio) { 761 try { 762 mMountService.partitionMixed(diskId, ratio); 763 } catch (RemoteException e) { 764 throw e.rethrowFromSystemServer(); 765 } 766 } 767 768 /** {@hide} */ 769 public void wipeAdoptableDisks() { 770 // We only wipe devices in "adoptable" locations, which are in a 771 // long-term stable slot/location on the device, where apps have a 772 // reasonable chance of storing sensitive data. (Apps need to go through 773 // SAF to write to transient volumes.) 774 final List<DiskInfo> disks = getDisks(); 775 for (DiskInfo disk : disks) { 776 final String diskId = disk.getId(); 777 if (disk.isAdoptable()) { 778 Slog.d(TAG, "Found adoptable " + diskId + "; wiping"); 779 try { 780 // TODO: switch to explicit wipe command when we have it, 781 // for now rely on the fact that vfat format does a wipe 782 mMountService.partitionPublic(diskId); 783 } catch (Exception e) { 784 Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e); 785 } 786 } else { 787 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId()); 788 } 789 } 790 } 791 792 /** {@hide} */ 793 public void setVolumeNickname(String fsUuid, String nickname) { 794 try { 795 mMountService.setVolumeNickname(fsUuid, nickname); 796 } catch (RemoteException e) { 797 throw e.rethrowFromSystemServer(); 798 } 799 } 800 801 /** {@hide} */ 802 public void setVolumeInited(String fsUuid, boolean inited) { 803 try { 804 mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0, 805 VolumeRecord.USER_FLAG_INITED); 806 } catch (RemoteException e) { 807 throw e.rethrowFromSystemServer(); 808 } 809 } 810 811 /** {@hide} */ 812 public void setVolumeSnoozed(String fsUuid, boolean snoozed) { 813 try { 814 mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0, 815 VolumeRecord.USER_FLAG_SNOOZED); 816 } catch (RemoteException e) { 817 throw e.rethrowFromSystemServer(); 818 } 819 } 820 821 /** {@hide} */ 822 public void forgetVolume(String fsUuid) { 823 try { 824 mMountService.forgetVolume(fsUuid); 825 } catch (RemoteException e) { 826 throw e.rethrowFromSystemServer(); 827 } 828 } 829 830 /** 831 * This is not the API you're looking for. 832 * 833 * @see PackageManager#getPrimaryStorageCurrentVolume() 834 * @hide 835 */ 836 public String getPrimaryStorageUuid() { 837 try { 838 return mMountService.getPrimaryStorageUuid(); 839 } catch (RemoteException e) { 840 throw e.rethrowFromSystemServer(); 841 } 842 } 843 844 /** 845 * This is not the API you're looking for. 846 * 847 * @see PackageManager#movePrimaryStorage(VolumeInfo) 848 * @hide 849 */ 850 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { 851 try { 852 mMountService.setPrimaryStorageUuid(volumeUuid, callback); 853 } catch (RemoteException e) { 854 throw e.rethrowFromSystemServer(); 855 } 856 } 857 858 /** 859 * Return the {@link StorageVolume} that contains the given file, or {@code null} if none. 860 */ 861 public @Nullable StorageVolume getStorageVolume(File file) { 862 return getStorageVolume(getVolumeList(), file); 863 } 864 865 /** {@hide} */ 866 public static @Nullable StorageVolume getStorageVolume(File file, int userId) { 867 return getStorageVolume(getVolumeList(userId, 0), file); 868 } 869 870 /** {@hide} */ 871 private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) { 872 if (file == null) { 873 return null; 874 } 875 try { 876 file = file.getCanonicalFile(); 877 } catch (IOException ignored) { 878 Slog.d(TAG, "Could not get canonical path for " + file); 879 return null; 880 } 881 for (StorageVolume volume : volumes) { 882 File volumeFile = volume.getPathFile(); 883 try { 884 volumeFile = volumeFile.getCanonicalFile(); 885 } catch (IOException ignored) { 886 continue; 887 } 888 if (FileUtils.contains(volumeFile, file)) { 889 return volume; 890 } 891 } 892 return null; 893 } 894 895 /** 896 * Gets the state of a volume via its mountpoint. 897 * @hide 898 */ 899 @Deprecated 900 public @NonNull String getVolumeState(String mountPoint) { 901 final StorageVolume vol = getStorageVolume(new File(mountPoint)); 902 if (vol != null) { 903 return vol.getState(); 904 } else { 905 return Environment.MEDIA_UNKNOWN; 906 } 907 } 908 909 /** 910 * Return the list of shared/external storage volumes available to the 911 * current user. This includes both the primary shared storage device and 912 * any attached external volumes including SD cards and USB drives. 913 * 914 * @see Environment#getExternalStorageDirectory() 915 * @see StorageVolume#createAccessIntent(String) 916 */ 917 public @NonNull List<StorageVolume> getStorageVolumes() { 918 final ArrayList<StorageVolume> res = new ArrayList<>(); 919 Collections.addAll(res, 920 getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)); 921 return res; 922 } 923 924 /** 925 * Return the primary shared/external storage volume available to the 926 * current user. This volume is the same storage device returned by 927 * {@link Environment#getExternalStorageDirectory()} and 928 * {@link Context#getExternalFilesDir(String)}. 929 */ 930 public @NonNull StorageVolume getPrimaryStorageVolume() { 931 return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0]; 932 } 933 934 /** {@hide} */ 935 public static Pair<String, Long> getPrimaryStoragePathAndSize() { 936 for (String path : INTERNAL_STORAGE_SIZE_PATHS) { 937 final long numberBlocks = readLong(path); 938 if (numberBlocks > 0) { 939 return new Pair<>(path, Long.valueOf(numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE)); 940 } 941 } 942 return null; 943 } 944 945 946 /** {@hide} */ 947 public long getPrimaryStorageSize() { 948 final Pair<String, Long> pair = getPrimaryStoragePathAndSize(); 949 return pair == null ? 0 : pair.second.longValue(); 950 } 951 952 private static long readLong(String path) { 953 try (final FileInputStream fis = new FileInputStream(path); 954 final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) { 955 return Long.parseLong(reader.readLine()); 956 } catch (FileNotFoundException e) { 957 // This is expected since we are trying to parse multiple paths. 958 Slog.i(TAG, "readLong(): Path doesn't exist: " + path + ": " + e); 959 return 0; 960 } catch (NumberFormatException e) { 961 Slog.e(TAG, "readLong(): Could not parse " + path + ": " + e); 962 return 0; 963 } catch (Exception e) { 964 Slog.e(TAG, "readLong(): Unknown exception while opening " + path + ": " + e); 965 return 0; 966 } 967 } 968 969 /** @removed */ 970 public @NonNull StorageVolume[] getVolumeList() { 971 return getVolumeList(mContext.getUserId(), 0); 972 } 973 974 /** {@hide} */ 975 public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) { 976 final IMountService mountService = IMountService.Stub.asInterface( 977 ServiceManager.getService("mount")); 978 try { 979 String packageName = ActivityThread.currentOpPackageName(); 980 if (packageName == null) { 981 // Package name can be null if the activity thread is running but the app 982 // hasn't bound yet. In this case we fall back to the first package in the 983 // current UID. This works for runtime permissions as permission state is 984 // per UID and permission realted app ops are updated for all UID packages. 985 String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid( 986 android.os.Process.myUid()); 987 if (packageNames == null || packageNames.length <= 0) { 988 return new StorageVolume[0]; 989 } 990 packageName = packageNames[0]; 991 } 992 final int uid = ActivityThread.getPackageManager().getPackageUid(packageName, 993 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId); 994 if (uid <= 0) { 995 return new StorageVolume[0]; 996 } 997 return mountService.getVolumeList(uid, packageName, flags); 998 } catch (RemoteException e) { 999 throw e.rethrowFromSystemServer(); 1000 } 1001 } 1002 1003 /** 1004 * Returns list of paths for all mountable volumes. 1005 * @hide 1006 */ 1007 @Deprecated 1008 public @NonNull String[] getVolumePaths() { 1009 StorageVolume[] volumes = getVolumeList(); 1010 int count = volumes.length; 1011 String[] paths = new String[count]; 1012 for (int i = 0; i < count; i++) { 1013 paths[i] = volumes[i].getPath(); 1014 } 1015 return paths; 1016 } 1017 1018 /** @removed */ 1019 public @NonNull StorageVolume getPrimaryVolume() { 1020 return getPrimaryVolume(getVolumeList()); 1021 } 1022 1023 /** {@hide} */ 1024 public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) { 1025 for (StorageVolume volume : volumes) { 1026 if (volume.isPrimary()) { 1027 return volume; 1028 } 1029 } 1030 throw new IllegalStateException("Missing primary storage"); 1031 } 1032 1033 /** {@hide} */ 1034 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; 1035 private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES; 1036 private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES; 1037 1038 /** 1039 * Return the number of available bytes until the given path is considered 1040 * running low on storage. 1041 * 1042 * @hide 1043 */ 1044 public long getStorageBytesUntilLow(File path) { 1045 return path.getUsableSpace() - getStorageFullBytes(path); 1046 } 1047 1048 /** 1049 * Return the number of available bytes at which the given path is 1050 * considered running low on storage. 1051 * 1052 * @hide 1053 */ 1054 public long getStorageLowBytes(File path) { 1055 final long lowPercent = Settings.Global.getInt(mResolver, 1056 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); 1057 final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; 1058 1059 final long maxLowBytes = Settings.Global.getLong(mResolver, 1060 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); 1061 1062 return Math.min(lowBytes, maxLowBytes); 1063 } 1064 1065 /** 1066 * Return the number of available bytes at which the given path is 1067 * considered full. 1068 * 1069 * @hide 1070 */ 1071 public long getStorageFullBytes(File path) { 1072 return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, 1073 DEFAULT_FULL_THRESHOLD_BYTES); 1074 } 1075 1076 /** {@hide} */ 1077 public void createUserKey(int userId, int serialNumber, boolean ephemeral) { 1078 try { 1079 mMountService.createUserKey(userId, serialNumber, ephemeral); 1080 } catch (RemoteException e) { 1081 throw e.rethrowFromSystemServer(); 1082 } 1083 } 1084 1085 /** {@hide} */ 1086 public void destroyUserKey(int userId) { 1087 try { 1088 mMountService.destroyUserKey(userId); 1089 } catch (RemoteException e) { 1090 throw e.rethrowFromSystemServer(); 1091 } 1092 } 1093 1094 /** {@hide} */ 1095 public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) { 1096 try { 1097 mMountService.unlockUserKey(userId, serialNumber, token, secret); 1098 } catch (RemoteException e) { 1099 throw e.rethrowFromSystemServer(); 1100 } 1101 } 1102 1103 /** {@hide} */ 1104 public void lockUserKey(int userId) { 1105 try { 1106 mMountService.lockUserKey(userId); 1107 } catch (RemoteException e) { 1108 throw e.rethrowFromSystemServer(); 1109 } 1110 } 1111 1112 /** {@hide} */ 1113 public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { 1114 try { 1115 mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, flags); 1116 } catch (RemoteException e) { 1117 throw e.rethrowFromSystemServer(); 1118 } 1119 } 1120 1121 /** {@hide} */ 1122 public void destroyUserStorage(String volumeUuid, int userId, int flags) { 1123 try { 1124 mMountService.destroyUserStorage(volumeUuid, userId, flags); 1125 } catch (RemoteException e) { 1126 throw e.rethrowFromSystemServer(); 1127 } 1128 } 1129 1130 /** {@hide} */ 1131 public static boolean isUserKeyUnlocked(int userId) { 1132 if (sMountService == null) { 1133 sMountService = IMountService.Stub 1134 .asInterface(ServiceManager.getService("mount")); 1135 } 1136 if (sMountService == null) { 1137 Slog.w(TAG, "Early during boot, assuming locked"); 1138 return false; 1139 } 1140 final long token = Binder.clearCallingIdentity(); 1141 try { 1142 return sMountService.isUserKeyUnlocked(userId); 1143 } catch (RemoteException e) { 1144 throw e.rethrowAsRuntimeException(); 1145 } finally { 1146 Binder.restoreCallingIdentity(token); 1147 } 1148 } 1149 1150 /** 1151 * Return if data stored at or under the given path will be encrypted while 1152 * at rest. This can help apps avoid the overhead of double-encrypting data. 1153 */ 1154 public boolean isEncrypted(File file) { 1155 if (FileUtils.contains(Environment.getDataDirectory(), file)) { 1156 return isEncrypted(); 1157 } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) { 1158 return true; 1159 } 1160 // TODO: extend to support shared storage 1161 return false; 1162 } 1163 1164 /** {@hide} 1165 * Is this device encryptable or already encrypted? 1166 * @return true for encryptable or encrypted 1167 * false not encrypted and not encryptable 1168 */ 1169 public static boolean isEncryptable() { 1170 final String state = SystemProperties.get("ro.crypto.state", "unsupported"); 1171 return !"unsupported".equalsIgnoreCase(state); 1172 } 1173 1174 /** {@hide} 1175 * Is this device already encrypted? 1176 * @return true for encrypted. (Implies isEncryptable() == true) 1177 * false not encrypted 1178 */ 1179 public static boolean isEncrypted() { 1180 final String state = SystemProperties.get("ro.crypto.state", ""); 1181 return "encrypted".equalsIgnoreCase(state); 1182 } 1183 1184 /** {@hide} 1185 * Is this device file encrypted? 1186 * @return true for file encrypted. (Implies isEncrypted() == true) 1187 * false not encrypted or block encrypted 1188 */ 1189 public static boolean isFileEncryptedNativeOnly() { 1190 if (!isEncrypted()) { 1191 return false; 1192 } 1193 1194 final String status = SystemProperties.get("ro.crypto.type", ""); 1195 return "file".equalsIgnoreCase(status); 1196 } 1197 1198 /** {@hide} 1199 * Is this device block encrypted? 1200 * @return true for block encrypted. (Implies isEncrypted() == true) 1201 * false not encrypted or file encrypted 1202 */ 1203 public static boolean isBlockEncrypted() { 1204 if (!isEncrypted()) { 1205 return false; 1206 } 1207 final String status = SystemProperties.get("ro.crypto.type", ""); 1208 return "block".equalsIgnoreCase(status); 1209 } 1210 1211 /** {@hide} 1212 * Is this device block encrypted with credentials? 1213 * @return true for crediential block encrypted. 1214 * (Implies isBlockEncrypted() == true) 1215 * false not encrypted, file encrypted or default block encrypted 1216 */ 1217 public static boolean isNonDefaultBlockEncrypted() { 1218 if (!isBlockEncrypted()) { 1219 return false; 1220 } 1221 1222 try { 1223 IMountService mountService = IMountService.Stub.asInterface( 1224 ServiceManager.getService("mount")); 1225 return mountService.getPasswordType() != CRYPT_TYPE_DEFAULT; 1226 } catch (RemoteException e) { 1227 Log.e(TAG, "Error getting encryption type"); 1228 return false; 1229 } 1230 } 1231 1232 /** {@hide} 1233 * Is this device in the process of being block encrypted? 1234 * @return true for encrypting. 1235 * false otherwise 1236 * Whether device isEncrypted at this point is undefined 1237 * Note that only system services and CryptKeeper will ever see this return 1238 * true - no app will ever be launched in this state. 1239 * Also note that this state will not change without a teardown of the 1240 * framework, so no service needs to check for changes during their lifespan 1241 */ 1242 public static boolean isBlockEncrypting() { 1243 final String state = SystemProperties.get("vold.encrypt_progress", ""); 1244 return !"".equalsIgnoreCase(state); 1245 } 1246 1247 /** {@hide} 1248 * Is this device non default block encrypted and in the process of 1249 * prompting for credentials? 1250 * @return true for prompting for credentials. 1251 * (Implies isNonDefaultBlockEncrypted() == true) 1252 * false otherwise 1253 * Note that only system services and CryptKeeper will ever see this return 1254 * true - no app will ever be launched in this state. 1255 * Also note that this state will not change without a teardown of the 1256 * framework, so no service needs to check for changes during their lifespan 1257 */ 1258 public static boolean inCryptKeeperBounce() { 1259 final String status = SystemProperties.get("vold.decrypt"); 1260 return "trigger_restart_min_framework".equals(status); 1261 } 1262 1263 /** {@hide} */ 1264 public static boolean isFileEncryptedEmulatedOnly() { 1265 return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false); 1266 } 1267 1268 /** {@hide} 1269 * Is this device running in a file encrypted mode, either native or emulated? 1270 * @return true for file encrypted, false otherwise 1271 */ 1272 public static boolean isFileEncryptedNativeOrEmulated() { 1273 return isFileEncryptedNativeOnly() 1274 || isFileEncryptedEmulatedOnly(); 1275 } 1276 1277 /** {@hide} */ 1278 public static File maybeTranslateEmulatedPathToInternal(File path) { 1279 final IMountService mountService = IMountService.Stub.asInterface( 1280 ServiceManager.getService("mount")); 1281 try { 1282 final VolumeInfo[] vols = mountService.getVolumes(0); 1283 for (VolumeInfo vol : vols) { 1284 if ((vol.getType() == VolumeInfo.TYPE_EMULATED 1285 || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) { 1286 final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(), 1287 vol.getInternalPath(), path); 1288 if (internalPath != null && internalPath.exists()) { 1289 return internalPath; 1290 } 1291 } 1292 } 1293 } catch (RemoteException e) { 1294 throw e.rethrowFromSystemServer(); 1295 } 1296 return path; 1297 } 1298 1299 /** {@hide} */ 1300 public ParcelFileDescriptor mountAppFuse(String name) { 1301 try { 1302 return mMountService.mountAppFuse(name); 1303 } catch (RemoteException e) { 1304 throw e.rethrowFromSystemServer(); 1305 } 1306 } 1307 1308 /// Consts to match the password types in cryptfs.h 1309 /** @hide */ 1310 public static final int CRYPT_TYPE_PASSWORD = 0; 1311 /** @hide */ 1312 public static final int CRYPT_TYPE_DEFAULT = 1; 1313 /** @hide */ 1314 public static final int CRYPT_TYPE_PATTERN = 2; 1315 /** @hide */ 1316 public static final int CRYPT_TYPE_PIN = 3; 1317 1318 // Constants for the data available via MountService.getField. 1319 /** @hide */ 1320 public static final String SYSTEM_LOCALE_KEY = "SystemLocale"; 1321 /** @hide */ 1322 public static final String OWNER_INFO_KEY = "OwnerInfo"; 1323 /** @hide */ 1324 public static final String PATTERN_VISIBLE_KEY = "PatternVisible"; 1325 /** @hide */ 1326 public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible"; 1327} 1328