StorageManager.java revision 50a05454795c93ac483f5cb6819e74cb17be1b5b
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.content.ContentResolver; 24import android.content.Context; 25import android.content.pm.IPackageMoveObserver; 26import android.content.pm.PackageManager; 27import android.os.Environment; 28import android.os.FileUtils; 29import android.os.Handler; 30import android.os.Looper; 31import android.os.Message; 32import android.os.RemoteException; 33import android.os.ServiceManager; 34import android.provider.Settings; 35import android.text.TextUtils; 36import android.util.Log; 37import android.util.Slog; 38import android.util.SparseArray; 39 40import com.android.internal.os.SomeArgs; 41import com.android.internal.util.Preconditions; 42 43import java.io.File; 44import java.io.IOException; 45import java.lang.ref.WeakReference; 46import java.util.ArrayList; 47import java.util.Arrays; 48import java.util.Iterator; 49import java.util.List; 50import java.util.Objects; 51import java.util.concurrent.atomic.AtomicInteger; 52 53/** 54 * StorageManager is the interface to the systems storage service. The storage 55 * manager handles storage-related items such as Opaque Binary Blobs (OBBs). 56 * <p> 57 * OBBs contain a filesystem that maybe be encrypted on disk and mounted 58 * on-demand from an application. OBBs are a good way of providing large amounts 59 * of binary assets without packaging them into APKs as they may be multiple 60 * gigabytes in size. However, due to their size, they're most likely stored in 61 * a shared storage pool accessible from all programs. The system does not 62 * guarantee the security of the OBB file itself: if any program modifies the 63 * OBB, there is no guarantee that a read from that OBB will produce the 64 * expected output. 65 * <p> 66 * Get an instance of this class by calling 67 * {@link android.content.Context#getSystemService(java.lang.String)} with an 68 * argument of {@link android.content.Context#STORAGE_SERVICE}. 69 */ 70public class StorageManager { 71 private static final String TAG = "StorageManager"; 72 73 /** {@hide} */ 74 public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; 75 /** {@hide} */ 76 public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; 77 78 /** {@hide} */ 79 public static final String UUID_PRIVATE_INTERNAL = null; 80 /** {@hide} */ 81 public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; 82 83 private final Context mContext; 84 private final ContentResolver mResolver; 85 86 private final IMountService mMountService; 87 private final Looper mLooper; 88 private final AtomicInteger mNextNonce = new AtomicInteger(0); 89 90 private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>(); 91 92 private static class StorageEventListenerDelegate extends IMountServiceListener.Stub implements 93 Handler.Callback { 94 private static final int MSG_STORAGE_STATE_CHANGED = 1; 95 private static final int MSG_VOLUME_STATE_CHANGED = 2; 96 private static final int MSG_VOLUME_RECORD_CHANGED = 3; 97 private static final int MSG_VOLUME_FORGOTTEN = 4; 98 private static final int MSG_DISK_SCANNED = 5; 99 100 final StorageEventListener mCallback; 101 final Handler mHandler; 102 103 public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) { 104 mCallback = callback; 105 mHandler = new Handler(looper, this); 106 } 107 108 @Override 109 public boolean handleMessage(Message msg) { 110 final SomeArgs args = (SomeArgs) msg.obj; 111 switch (msg.what) { 112 case MSG_STORAGE_STATE_CHANGED: 113 mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2, 114 (String) args.arg3); 115 args.recycle(); 116 return true; 117 case MSG_VOLUME_STATE_CHANGED: 118 mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); 119 args.recycle(); 120 return true; 121 case MSG_VOLUME_RECORD_CHANGED: 122 mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1); 123 args.recycle(); 124 return true; 125 case MSG_VOLUME_FORGOTTEN: 126 mCallback.onVolumeForgotten((String) args.arg1); 127 args.recycle(); 128 return true; 129 case MSG_DISK_SCANNED: 130 mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2); 131 args.recycle(); 132 return true; 133 } 134 args.recycle(); 135 return false; 136 } 137 138 @Override 139 public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException { 140 // Ignored 141 } 142 143 @Override 144 public void onStorageStateChanged(String path, String oldState, String newState) { 145 final SomeArgs args = SomeArgs.obtain(); 146 args.arg1 = path; 147 args.arg2 = oldState; 148 args.arg3 = newState; 149 mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); 150 } 151 152 @Override 153 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 154 final SomeArgs args = SomeArgs.obtain(); 155 args.arg1 = vol; 156 args.argi2 = oldState; 157 args.argi3 = newState; 158 mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); 159 } 160 161 @Override 162 public void onVolumeRecordChanged(VolumeRecord rec) { 163 final SomeArgs args = SomeArgs.obtain(); 164 args.arg1 = rec; 165 mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); 166 } 167 168 @Override 169 public void onVolumeForgotten(String fsUuid) { 170 final SomeArgs args = SomeArgs.obtain(); 171 args.arg1 = fsUuid; 172 mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); 173 } 174 175 @Override 176 public void onDiskScanned(DiskInfo disk, int volumeCount) { 177 final SomeArgs args = SomeArgs.obtain(); 178 args.arg1 = disk; 179 args.argi2 = volumeCount; 180 mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); 181 } 182 } 183 184 /** 185 * Binder listener for OBB action results. 186 */ 187 private final ObbActionListener mObbActionListener = new ObbActionListener(); 188 189 private class ObbActionListener extends IObbActionListener.Stub { 190 @SuppressWarnings("hiding") 191 private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>(); 192 193 @Override 194 public void onObbResult(String filename, int nonce, int status) { 195 final ObbListenerDelegate delegate; 196 synchronized (mListeners) { 197 delegate = mListeners.get(nonce); 198 if (delegate != null) { 199 mListeners.remove(nonce); 200 } 201 } 202 203 if (delegate != null) { 204 delegate.sendObbStateChanged(filename, status); 205 } 206 } 207 208 public int addListener(OnObbStateChangeListener listener) { 209 final ObbListenerDelegate delegate = new ObbListenerDelegate(listener); 210 211 synchronized (mListeners) { 212 mListeners.put(delegate.nonce, delegate); 213 } 214 215 return delegate.nonce; 216 } 217 } 218 219 private int getNextNonce() { 220 return mNextNonce.getAndIncrement(); 221 } 222 223 /** 224 * Private class containing sender and receiver code for StorageEvents. 225 */ 226 private class ObbListenerDelegate { 227 private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef; 228 private final Handler mHandler; 229 230 private final int nonce; 231 232 ObbListenerDelegate(OnObbStateChangeListener listener) { 233 nonce = getNextNonce(); 234 mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener); 235 mHandler = new Handler(mLooper) { 236 @Override 237 public void handleMessage(Message msg) { 238 final OnObbStateChangeListener changeListener = getListener(); 239 if (changeListener == null) { 240 return; 241 } 242 243 changeListener.onObbStateChange((String) msg.obj, msg.arg1); 244 } 245 }; 246 } 247 248 OnObbStateChangeListener getListener() { 249 if (mObbEventListenerRef == null) { 250 return null; 251 } 252 return mObbEventListenerRef.get(); 253 } 254 255 void sendObbStateChanged(String path, int state) { 256 mHandler.obtainMessage(0, state, 0, path).sendToTarget(); 257 } 258 } 259 260 /** {@hide} */ 261 @Deprecated 262 public static StorageManager from(Context context) { 263 return context.getSystemService(StorageManager.class); 264 } 265 266 /** 267 * Constructs a StorageManager object through which an application can 268 * can communicate with the systems mount service. 269 * 270 * @param tgtLooper The {@link android.os.Looper} which events will be received on. 271 * 272 * <p>Applications can get instance of this class by calling 273 * {@link android.content.Context#getSystemService(java.lang.String)} with an argument 274 * of {@link android.content.Context#STORAGE_SERVICE}. 275 * 276 * @hide 277 */ 278 public StorageManager(Context context, Looper looper) { 279 mContext = context; 280 mResolver = context.getContentResolver(); 281 mLooper = looper; 282 mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); 283 if (mMountService == null) { 284 throw new IllegalStateException("Failed to find running mount service"); 285 } 286 } 287 288 /** 289 * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}. 290 * 291 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 292 * 293 * @hide 294 */ 295 public void registerListener(StorageEventListener listener) { 296 synchronized (mDelegates) { 297 final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener, 298 mLooper); 299 try { 300 mMountService.registerListener(delegate); 301 } catch (RemoteException e) { 302 throw e.rethrowAsRuntimeException(); 303 } 304 mDelegates.add(delegate); 305 } 306 } 307 308 /** 309 * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}. 310 * 311 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 312 * 313 * @hide 314 */ 315 public void unregisterListener(StorageEventListener listener) { 316 synchronized (mDelegates) { 317 for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) { 318 final StorageEventListenerDelegate delegate = i.next(); 319 if (delegate.mCallback == listener) { 320 try { 321 mMountService.unregisterListener(delegate); 322 } catch (RemoteException e) { 323 throw e.rethrowAsRuntimeException(); 324 } 325 i.remove(); 326 } 327 } 328 } 329 } 330 331 /** 332 * Enables USB Mass Storage (UMS) on the device. 333 * 334 * @hide 335 */ 336 @Deprecated 337 public void enableUsbMassStorage() { 338 } 339 340 /** 341 * Disables USB Mass Storage (UMS) on the device. 342 * 343 * @hide 344 */ 345 @Deprecated 346 public void disableUsbMassStorage() { 347 } 348 349 /** 350 * Query if a USB Mass Storage (UMS) host is connected. 351 * @return true if UMS host is connected. 352 * 353 * @hide 354 */ 355 @Deprecated 356 public boolean isUsbMassStorageConnected() { 357 return false; 358 } 359 360 /** 361 * Query if a USB Mass Storage (UMS) is enabled on the device. 362 * @return true if UMS host is enabled. 363 * 364 * @hide 365 */ 366 @Deprecated 367 public boolean isUsbMassStorageEnabled() { 368 return false; 369 } 370 371 /** 372 * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is 373 * specified, it is supplied to the mounting process to be used in any 374 * encryption used in the OBB. 375 * <p> 376 * The OBB will remain mounted for as long as the StorageManager reference 377 * is held by the application. As soon as this reference is lost, the OBBs 378 * in use will be unmounted. The {@link OnObbStateChangeListener} registered 379 * with this call will receive the success or failure of this operation. 380 * <p> 381 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 382 * file matches a package ID that is owned by the calling program's UID. 383 * That is, shared UID applications can attempt to mount any other 384 * application's OBB that shares its UID. 385 * 386 * @param rawPath the path to the OBB file 387 * @param key secret used to encrypt the OBB; may be <code>null</code> if no 388 * encryption was used on the OBB. 389 * @param listener will receive the success or failure of the operation 390 * @return whether the mount call was successfully queued or not 391 */ 392 public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) { 393 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 394 Preconditions.checkNotNull(listener, "listener cannot be null"); 395 396 try { 397 final String canonicalPath = new File(rawPath).getCanonicalPath(); 398 final int nonce = mObbActionListener.addListener(listener); 399 mMountService.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce); 400 return true; 401 } catch (IOException e) { 402 throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e); 403 } catch (RemoteException e) { 404 Log.e(TAG, "Failed to mount OBB", e); 405 } 406 407 return false; 408 } 409 410 /** 411 * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the 412 * <code>force</code> flag is true, it will kill any application needed to 413 * unmount the given OBB (even the calling application). 414 * <p> 415 * The {@link OnObbStateChangeListener} registered with this call will 416 * receive the success or failure of this operation. 417 * <p> 418 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 419 * file matches a package ID that is owned by the calling program's UID. 420 * That is, shared UID applications can obtain access to any other 421 * application's OBB that shares its UID. 422 * <p> 423 * 424 * @param rawPath path to the OBB file 425 * @param force whether to kill any programs using this in order to unmount 426 * it 427 * @param listener will receive the success or failure of the operation 428 * @return whether the unmount call was successfully queued or not 429 */ 430 public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) { 431 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 432 Preconditions.checkNotNull(listener, "listener cannot be null"); 433 434 try { 435 final int nonce = mObbActionListener.addListener(listener); 436 mMountService.unmountObb(rawPath, force, mObbActionListener, nonce); 437 return true; 438 } catch (RemoteException e) { 439 Log.e(TAG, "Failed to mount OBB", e); 440 } 441 442 return false; 443 } 444 445 /** 446 * Check whether an Opaque Binary Blob (OBB) is mounted or not. 447 * 448 * @param rawPath path to OBB image 449 * @return true if OBB is mounted; false if not mounted or on error 450 */ 451 public boolean isObbMounted(String rawPath) { 452 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 453 454 try { 455 return mMountService.isObbMounted(rawPath); 456 } catch (RemoteException e) { 457 Log.e(TAG, "Failed to check if OBB is mounted", e); 458 } 459 460 return false; 461 } 462 463 /** 464 * Check the mounted path of an Opaque Binary Blob (OBB) file. This will 465 * give you the path to where you can obtain access to the internals of the 466 * OBB. 467 * 468 * @param rawPath path to OBB image 469 * @return absolute path to mounted OBB image data or <code>null</code> if 470 * not mounted or exception encountered trying to read status 471 */ 472 public String getMountedObbPath(String rawPath) { 473 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 474 475 try { 476 return mMountService.getMountedObbPath(rawPath); 477 } catch (RemoteException e) { 478 Log.e(TAG, "Failed to find mounted path for OBB", e); 479 } 480 481 return null; 482 } 483 484 /** {@hide} */ 485 public @NonNull List<DiskInfo> getDisks() { 486 try { 487 return Arrays.asList(mMountService.getDisks()); 488 } catch (RemoteException e) { 489 throw e.rethrowAsRuntimeException(); 490 } 491 } 492 493 /** {@hide} */ 494 public @Nullable DiskInfo findDiskById(String id) { 495 Preconditions.checkNotNull(id); 496 // TODO; go directly to service to make this faster 497 for (DiskInfo disk : getDisks()) { 498 if (Objects.equals(disk.id, id)) { 499 return disk; 500 } 501 } 502 return null; 503 } 504 505 /** {@hide} */ 506 public @Nullable VolumeInfo findVolumeById(String id) { 507 Preconditions.checkNotNull(id); 508 // TODO; go directly to service to make this faster 509 for (VolumeInfo vol : getVolumes()) { 510 if (Objects.equals(vol.id, id)) { 511 return vol; 512 } 513 } 514 return null; 515 } 516 517 /** {@hide} */ 518 public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) { 519 Preconditions.checkNotNull(fsUuid); 520 // TODO; go directly to service to make this faster 521 for (VolumeInfo vol : getVolumes()) { 522 if (Objects.equals(vol.fsUuid, fsUuid)) { 523 return vol; 524 } 525 } 526 return null; 527 } 528 529 /** {@hide} */ 530 public @Nullable VolumeRecord findRecordByUuid(String fsUuid) { 531 Preconditions.checkNotNull(fsUuid); 532 // TODO; go directly to service to make this faster 533 for (VolumeRecord rec : getVolumeRecords()) { 534 if (Objects.equals(rec.fsUuid, fsUuid)) { 535 return rec; 536 } 537 } 538 return null; 539 } 540 541 /** {@hide} */ 542 public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) { 543 return findVolumeById(emulatedVol.getId().replace("emulated", "private")); 544 } 545 546 /** {@hide} */ 547 public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) { 548 return findVolumeById(privateVol.getId().replace("private", "emulated")); 549 } 550 551 /** {@hide} */ 552 public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) { 553 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { 554 return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); 555 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 556 return getPrimaryPhysicalVolume(); 557 } else { 558 return findVolumeByUuid(volumeUuid); 559 } 560 } 561 562 /** {@hide} */ 563 public @NonNull List<VolumeInfo> getVolumes() { 564 try { 565 return Arrays.asList(mMountService.getVolumes(0)); 566 } catch (RemoteException e) { 567 throw e.rethrowAsRuntimeException(); 568 } 569 } 570 571 /** {@hide} */ 572 public @NonNull List<VolumeRecord> getVolumeRecords() { 573 try { 574 return Arrays.asList(mMountService.getVolumeRecords(0)); 575 } catch (RemoteException e) { 576 throw e.rethrowAsRuntimeException(); 577 } 578 } 579 580 /** {@hide} */ 581 public @Nullable String getBestVolumeDescription(VolumeInfo vol) { 582 if (vol == null) return null; 583 584 // Nickname always takes precedence when defined 585 if (!TextUtils.isEmpty(vol.fsUuid)) { 586 final VolumeRecord rec = findRecordByUuid(vol.fsUuid); 587 if (!TextUtils.isEmpty(rec.nickname)) { 588 return rec.nickname; 589 } 590 } 591 592 if (!TextUtils.isEmpty(vol.getDescription())) { 593 return vol.getDescription(); 594 } 595 596 if (vol.disk != null) { 597 return vol.disk.getDescription(); 598 } 599 600 return null; 601 } 602 603 /** {@hide} */ 604 public @Nullable VolumeInfo getPrimaryPhysicalVolume() { 605 final List<VolumeInfo> vols = getVolumes(); 606 for (VolumeInfo vol : vols) { 607 if (vol.isPrimaryPhysical()) { 608 return vol; 609 } 610 } 611 return null; 612 } 613 614 /** {@hide} */ 615 public void mount(String volId) { 616 try { 617 mMountService.mount(volId); 618 } catch (RemoteException e) { 619 throw e.rethrowAsRuntimeException(); 620 } 621 } 622 623 /** {@hide} */ 624 public void unmount(String volId) { 625 try { 626 mMountService.unmount(volId); 627 } catch (RemoteException e) { 628 throw e.rethrowAsRuntimeException(); 629 } 630 } 631 632 /** {@hide} */ 633 public void format(String volId) { 634 try { 635 mMountService.format(volId); 636 } catch (RemoteException e) { 637 throw e.rethrowAsRuntimeException(); 638 } 639 } 640 641 /** {@hide} */ 642 public void partitionPublic(String diskId) { 643 try { 644 mMountService.partitionPublic(diskId); 645 } catch (RemoteException e) { 646 throw e.rethrowAsRuntimeException(); 647 } 648 } 649 650 /** {@hide} */ 651 public void partitionPrivate(String diskId) { 652 try { 653 mMountService.partitionPrivate(diskId); 654 } catch (RemoteException e) { 655 throw e.rethrowAsRuntimeException(); 656 } 657 } 658 659 /** {@hide} */ 660 public void partitionMixed(String diskId, int ratio) { 661 try { 662 mMountService.partitionMixed(diskId, ratio); 663 } catch (RemoteException e) { 664 throw e.rethrowAsRuntimeException(); 665 } 666 } 667 668 /** {@hide} */ 669 public void wipeAdoptableDisks() { 670 // We only wipe devices in "adoptable" locations, which are in a 671 // long-term stable slot/location on the device, where apps have a 672 // reasonable chance of storing sensitive data. (Apps need to go through 673 // SAF to write to transient volumes.) 674 final List<DiskInfo> disks = getDisks(); 675 for (DiskInfo disk : disks) { 676 final String diskId = disk.getId(); 677 if (disk.isAdoptable()) { 678 Slog.d(TAG, "Found adoptable " + diskId + "; wiping"); 679 try { 680 // TODO: switch to explicit wipe command when we have it, 681 // for now rely on the fact that vfat format does a wipe 682 mMountService.partitionPublic(diskId); 683 } catch (Exception e) { 684 Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e); 685 } 686 } else { 687 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId()); 688 } 689 } 690 } 691 692 /** {@hide} */ 693 public void setVolumeNickname(String fsUuid, String nickname) { 694 try { 695 mMountService.setVolumeNickname(fsUuid, nickname); 696 } catch (RemoteException e) { 697 throw e.rethrowAsRuntimeException(); 698 } 699 } 700 701 /** {@hide} */ 702 public void setVolumeInited(String fsUuid, boolean inited) { 703 try { 704 mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0, 705 VolumeRecord.USER_FLAG_INITED); 706 } catch (RemoteException e) { 707 throw e.rethrowAsRuntimeException(); 708 } 709 } 710 711 /** {@hide} */ 712 public void setVolumeSnoozed(String fsUuid, boolean snoozed) { 713 try { 714 mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0, 715 VolumeRecord.USER_FLAG_SNOOZED); 716 } catch (RemoteException e) { 717 throw e.rethrowAsRuntimeException(); 718 } 719 } 720 721 /** {@hide} */ 722 public void forgetVolume(String fsUuid) { 723 try { 724 mMountService.forgetVolume(fsUuid); 725 } catch (RemoteException e) { 726 throw e.rethrowAsRuntimeException(); 727 } 728 } 729 730 /** 731 * This is not the API you're looking for. 732 * 733 * @see PackageManager#getPrimaryStorageCurrentVolume() 734 * @hide 735 */ 736 public String getPrimaryStorageUuid() { 737 try { 738 return mMountService.getPrimaryStorageUuid(); 739 } catch (RemoteException e) { 740 throw e.rethrowAsRuntimeException(); 741 } 742 } 743 744 /** 745 * This is not the API you're looking for. 746 * 747 * @see PackageManager#movePrimaryStorage(VolumeInfo) 748 * @hide 749 */ 750 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { 751 try { 752 mMountService.setPrimaryStorageUuid(volumeUuid, callback); 753 } catch (RemoteException e) { 754 throw e.rethrowAsRuntimeException(); 755 } 756 } 757 758 /** {@hide} */ 759 public @Nullable StorageVolume getStorageVolume(File file) { 760 return getStorageVolume(getVolumeList(), file); 761 } 762 763 /** {@hide} */ 764 public static @Nullable StorageVolume getStorageVolume(File file, int userId) { 765 return getStorageVolume(getVolumeList(userId), file); 766 } 767 768 /** {@hide} */ 769 private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) { 770 File canonicalFile = null; 771 try { 772 canonicalFile = file.getCanonicalFile(); 773 } catch (IOException ignored) { 774 canonicalFile = null; 775 } 776 for (StorageVolume volume : volumes) { 777 if (volume.getPathFile().equals(file)) { 778 return volume; 779 } 780 if (FileUtils.contains(volume.getPathFile(), canonicalFile)) { 781 return volume; 782 } 783 } 784 return null; 785 } 786 787 /** 788 * Gets the state of a volume via its mountpoint. 789 * @hide 790 */ 791 @Deprecated 792 public @NonNull String getVolumeState(String mountPoint) { 793 final StorageVolume vol = getStorageVolume(new File(mountPoint)); 794 if (vol != null) { 795 return vol.getState(); 796 } else { 797 return Environment.MEDIA_UNKNOWN; 798 } 799 } 800 801 /** {@hide} */ 802 public @NonNull StorageVolume[] getVolumeList() { 803 return getVolumeList(mContext.getUserId()); 804 } 805 806 /** {@hide} */ 807 public static @NonNull StorageVolume[] getVolumeList(int userId) { 808 final IMountService mountService = IMountService.Stub.asInterface( 809 ServiceManager.getService("mount")); 810 try { 811 return mountService.getVolumeList(userId); 812 } catch (RemoteException e) { 813 throw e.rethrowAsRuntimeException(); 814 } 815 } 816 817 /** 818 * Returns list of paths for all mountable volumes. 819 * @hide 820 */ 821 @Deprecated 822 public @NonNull String[] getVolumePaths() { 823 StorageVolume[] volumes = getVolumeList(); 824 int count = volumes.length; 825 String[] paths = new String[count]; 826 for (int i = 0; i < count; i++) { 827 paths[i] = volumes[i].getPath(); 828 } 829 return paths; 830 } 831 832 /** {@hide} */ 833 public @NonNull StorageVolume getPrimaryVolume() { 834 return getPrimaryVolume(getVolumeList()); 835 } 836 837 /** {@hide} */ 838 public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) { 839 for (StorageVolume volume : volumes) { 840 if (volume.isPrimary()) { 841 return volume; 842 } 843 } 844 throw new IllegalStateException("Missing primary storage"); 845 } 846 847 /** {@hide} */ 848 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; 849 private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES; 850 private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES; 851 852 /** 853 * Return the number of available bytes until the given path is considered 854 * running low on storage. 855 * 856 * @hide 857 */ 858 public long getStorageBytesUntilLow(File path) { 859 return path.getUsableSpace() - getStorageFullBytes(path); 860 } 861 862 /** 863 * Return the number of available bytes at which the given path is 864 * considered running low on storage. 865 * 866 * @hide 867 */ 868 public long getStorageLowBytes(File path) { 869 final long lowPercent = Settings.Global.getInt(mResolver, 870 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); 871 final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; 872 873 final long maxLowBytes = Settings.Global.getLong(mResolver, 874 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); 875 876 return Math.min(lowBytes, maxLowBytes); 877 } 878 879 /** 880 * Return the number of available bytes at which the given path is 881 * considered full. 882 * 883 * @hide 884 */ 885 public long getStorageFullBytes(File path) { 886 return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, 887 DEFAULT_FULL_THRESHOLD_BYTES); 888 } 889 890 /** {@hide} */ 891 public static File maybeTranslateEmulatedPathToInternal(File path) { 892 final IMountService mountService = IMountService.Stub.asInterface( 893 ServiceManager.getService("mount")); 894 try { 895 final VolumeInfo[] vols = mountService.getVolumes(0); 896 for (VolumeInfo vol : vols) { 897 if ((vol.getType() == VolumeInfo.TYPE_EMULATED 898 || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) { 899 final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(), 900 vol.getInternalPath(), path); 901 if (internalPath != null) { 902 return internalPath; 903 } 904 } 905 } 906 } catch (RemoteException ignored) { 907 } 908 return path; 909 } 910 911 /// Consts to match the password types in cryptfs.h 912 /** @hide */ 913 public static final int CRYPT_TYPE_PASSWORD = 0; 914 /** @hide */ 915 public static final int CRYPT_TYPE_DEFAULT = 1; 916 /** @hide */ 917 public static final int CRYPT_TYPE_PATTERN = 2; 918 /** @hide */ 919 public static final int CRYPT_TYPE_PIN = 3; 920 921 // Constants for the data available via MountService.getField. 922 /** @hide */ 923 public static final String SYSTEM_LOCALE_KEY = "SystemLocale"; 924 /** @hide */ 925 public static final String OWNER_INFO_KEY = "OwnerInfo"; 926 /** @hide */ 927 public static final String PATTERN_VISIBLE_KEY = "PatternVisible"; 928} 929