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