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