StorageManager.java revision bcf48ed2262d655ebf59153dea645ca761b73db5
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 if (emulatedVol != null) { 561 return findVolumeById(emulatedVol.getId().replace("emulated", "private")); 562 } else { 563 return null; 564 } 565 } 566 567 /** {@hide} */ 568 public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) { 569 if (privateVol != null) { 570 return findVolumeById(privateVol.getId().replace("private", "emulated")); 571 } else { 572 return null; 573 } 574 } 575 576 /** {@hide} */ 577 public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) { 578 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { 579 return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); 580 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 581 return getPrimaryPhysicalVolume(); 582 } else { 583 return findVolumeByUuid(volumeUuid); 584 } 585 } 586 587 /** {@hide} */ 588 public @NonNull List<VolumeInfo> getVolumes() { 589 try { 590 return Arrays.asList(mMountService.getVolumes(0)); 591 } catch (RemoteException e) { 592 throw e.rethrowAsRuntimeException(); 593 } 594 } 595 596 /** {@hide} */ 597 public @NonNull List<VolumeInfo> getWritablePrivateVolumes() { 598 try { 599 final ArrayList<VolumeInfo> res = new ArrayList<>(); 600 for (VolumeInfo vol : mMountService.getVolumes(0)) { 601 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) { 602 res.add(vol); 603 } 604 } 605 return res; 606 } catch (RemoteException e) { 607 throw e.rethrowAsRuntimeException(); 608 } 609 } 610 611 /** {@hide} */ 612 public @NonNull List<VolumeRecord> getVolumeRecords() { 613 try { 614 return Arrays.asList(mMountService.getVolumeRecords(0)); 615 } catch (RemoteException e) { 616 throw e.rethrowAsRuntimeException(); 617 } 618 } 619 620 /** {@hide} */ 621 public @Nullable String getBestVolumeDescription(VolumeInfo vol) { 622 if (vol == null) return null; 623 624 // Nickname always takes precedence when defined 625 if (!TextUtils.isEmpty(vol.fsUuid)) { 626 final VolumeRecord rec = findRecordByUuid(vol.fsUuid); 627 if (rec != null && !TextUtils.isEmpty(rec.nickname)) { 628 return rec.nickname; 629 } 630 } 631 632 if (!TextUtils.isEmpty(vol.getDescription())) { 633 return vol.getDescription(); 634 } 635 636 if (vol.disk != null) { 637 return vol.disk.getDescription(); 638 } 639 640 return null; 641 } 642 643 /** {@hide} */ 644 public @Nullable VolumeInfo getPrimaryPhysicalVolume() { 645 final List<VolumeInfo> vols = getVolumes(); 646 for (VolumeInfo vol : vols) { 647 if (vol.isPrimaryPhysical()) { 648 return vol; 649 } 650 } 651 return null; 652 } 653 654 /** {@hide} */ 655 public void mount(String volId) { 656 try { 657 mMountService.mount(volId); 658 } catch (RemoteException e) { 659 throw e.rethrowAsRuntimeException(); 660 } 661 } 662 663 /** {@hide} */ 664 public void unmount(String volId) { 665 try { 666 mMountService.unmount(volId); 667 } catch (RemoteException e) { 668 throw e.rethrowAsRuntimeException(); 669 } 670 } 671 672 /** {@hide} */ 673 public void format(String volId) { 674 try { 675 mMountService.format(volId); 676 } catch (RemoteException e) { 677 throw e.rethrowAsRuntimeException(); 678 } 679 } 680 681 /** {@hide} */ 682 public long benchmark(String volId) { 683 try { 684 return mMountService.benchmark(volId); 685 } catch (RemoteException e) { 686 throw e.rethrowAsRuntimeException(); 687 } 688 } 689 690 /** {@hide} */ 691 public void partitionPublic(String diskId) { 692 try { 693 mMountService.partitionPublic(diskId); 694 } catch (RemoteException e) { 695 throw e.rethrowAsRuntimeException(); 696 } 697 } 698 699 /** {@hide} */ 700 public void partitionPrivate(String diskId) { 701 try { 702 mMountService.partitionPrivate(diskId); 703 } catch (RemoteException e) { 704 throw e.rethrowAsRuntimeException(); 705 } 706 } 707 708 /** {@hide} */ 709 public void partitionMixed(String diskId, int ratio) { 710 try { 711 mMountService.partitionMixed(diskId, ratio); 712 } catch (RemoteException e) { 713 throw e.rethrowAsRuntimeException(); 714 } 715 } 716 717 /** {@hide} */ 718 public void wipeAdoptableDisks() { 719 // We only wipe devices in "adoptable" locations, which are in a 720 // long-term stable slot/location on the device, where apps have a 721 // reasonable chance of storing sensitive data. (Apps need to go through 722 // SAF to write to transient volumes.) 723 final List<DiskInfo> disks = getDisks(); 724 for (DiskInfo disk : disks) { 725 final String diskId = disk.getId(); 726 if (disk.isAdoptable()) { 727 Slog.d(TAG, "Found adoptable " + diskId + "; wiping"); 728 try { 729 // TODO: switch to explicit wipe command when we have it, 730 // for now rely on the fact that vfat format does a wipe 731 mMountService.partitionPublic(diskId); 732 } catch (Exception e) { 733 Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e); 734 } 735 } else { 736 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId()); 737 } 738 } 739 } 740 741 /** {@hide} */ 742 public void setVolumeNickname(String fsUuid, String nickname) { 743 try { 744 mMountService.setVolumeNickname(fsUuid, nickname); 745 } catch (RemoteException e) { 746 throw e.rethrowAsRuntimeException(); 747 } 748 } 749 750 /** {@hide} */ 751 public void setVolumeInited(String fsUuid, boolean inited) { 752 try { 753 mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0, 754 VolumeRecord.USER_FLAG_INITED); 755 } catch (RemoteException e) { 756 throw e.rethrowAsRuntimeException(); 757 } 758 } 759 760 /** {@hide} */ 761 public void setVolumeSnoozed(String fsUuid, boolean snoozed) { 762 try { 763 mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0, 764 VolumeRecord.USER_FLAG_SNOOZED); 765 } catch (RemoteException e) { 766 throw e.rethrowAsRuntimeException(); 767 } 768 } 769 770 /** {@hide} */ 771 public void forgetVolume(String fsUuid) { 772 try { 773 mMountService.forgetVolume(fsUuid); 774 } catch (RemoteException e) { 775 throw e.rethrowAsRuntimeException(); 776 } 777 } 778 779 /** 780 * This is not the API you're looking for. 781 * 782 * @see PackageManager#getPrimaryStorageCurrentVolume() 783 * @hide 784 */ 785 public String getPrimaryStorageUuid() { 786 try { 787 return mMountService.getPrimaryStorageUuid(); 788 } catch (RemoteException e) { 789 throw e.rethrowAsRuntimeException(); 790 } 791 } 792 793 /** 794 * This is not the API you're looking for. 795 * 796 * @see PackageManager#movePrimaryStorage(VolumeInfo) 797 * @hide 798 */ 799 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { 800 try { 801 mMountService.setPrimaryStorageUuid(volumeUuid, callback); 802 } catch (RemoteException e) { 803 throw e.rethrowAsRuntimeException(); 804 } 805 } 806 807 /** {@hide} */ 808 public @Nullable StorageVolume getStorageVolume(File file) { 809 return getStorageVolume(getVolumeList(), file); 810 } 811 812 /** {@hide} */ 813 public static @Nullable StorageVolume getStorageVolume(File file, int userId) { 814 return getStorageVolume(getVolumeList(userId), file); 815 } 816 817 /** {@hide} */ 818 private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) { 819 File canonicalFile = null; 820 try { 821 canonicalFile = file.getCanonicalFile(); 822 } catch (IOException ignored) { 823 canonicalFile = null; 824 } 825 for (StorageVolume volume : volumes) { 826 if (volume.getPathFile().equals(file)) { 827 return volume; 828 } 829 if (FileUtils.contains(volume.getPathFile(), canonicalFile)) { 830 return volume; 831 } 832 } 833 return null; 834 } 835 836 /** 837 * Gets the state of a volume via its mountpoint. 838 * @hide 839 */ 840 @Deprecated 841 public @NonNull String getVolumeState(String mountPoint) { 842 final StorageVolume vol = getStorageVolume(new File(mountPoint)); 843 if (vol != null) { 844 return vol.getState(); 845 } else { 846 return Environment.MEDIA_UNKNOWN; 847 } 848 } 849 850 /** {@hide} */ 851 public @NonNull StorageVolume[] getVolumeList() { 852 return getVolumeList(mContext.getUserId()); 853 } 854 855 /** {@hide} */ 856 public static @NonNull StorageVolume[] getVolumeList(int userId) { 857 final IMountService mountService = IMountService.Stub.asInterface( 858 ServiceManager.getService("mount")); 859 try { 860 return mountService.getVolumeList(userId); 861 } catch (RemoteException e) { 862 throw e.rethrowAsRuntimeException(); 863 } 864 } 865 866 /** 867 * Returns list of paths for all mountable volumes. 868 * @hide 869 */ 870 @Deprecated 871 public @NonNull String[] getVolumePaths() { 872 StorageVolume[] volumes = getVolumeList(); 873 int count = volumes.length; 874 String[] paths = new String[count]; 875 for (int i = 0; i < count; i++) { 876 paths[i] = volumes[i].getPath(); 877 } 878 return paths; 879 } 880 881 /** {@hide} */ 882 public @NonNull StorageVolume getPrimaryVolume() { 883 return getPrimaryVolume(getVolumeList()); 884 } 885 886 /** {@hide} */ 887 public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) { 888 for (StorageVolume volume : volumes) { 889 if (volume.isPrimary()) { 890 return volume; 891 } 892 } 893 throw new IllegalStateException("Missing primary storage"); 894 } 895 896 /** {@hide} */ 897 public void remountUid(int uid) { 898 try { 899 mMountService.remountUid(uid); 900 } catch (RemoteException e) { 901 throw e.rethrowAsRuntimeException(); 902 } 903 } 904 905 /** {@hide} */ 906 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; 907 private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES; 908 private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES; 909 910 /** 911 * Return the number of available bytes until the given path is considered 912 * running low on storage. 913 * 914 * @hide 915 */ 916 public long getStorageBytesUntilLow(File path) { 917 return path.getUsableSpace() - getStorageFullBytes(path); 918 } 919 920 /** 921 * Return the number of available bytes at which the given path is 922 * considered running low on storage. 923 * 924 * @hide 925 */ 926 public long getStorageLowBytes(File path) { 927 final long lowPercent = Settings.Global.getInt(mResolver, 928 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); 929 final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; 930 931 final long maxLowBytes = Settings.Global.getLong(mResolver, 932 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); 933 934 return Math.min(lowBytes, maxLowBytes); 935 } 936 937 /** 938 * Return the number of available bytes at which the given path is 939 * considered full. 940 * 941 * @hide 942 */ 943 public long getStorageFullBytes(File path) { 944 return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, 945 DEFAULT_FULL_THRESHOLD_BYTES); 946 } 947 948 /** {@hide} */ 949 public void createNewUserDir(int userHandle, File path) { 950 try { 951 mMountService.createNewUserDir(userHandle, path.getAbsolutePath()); 952 } catch (RemoteException e) { 953 throw e.rethrowAsRuntimeException(); 954 } 955 } 956 957 /** {@hide} */ 958 public static File maybeTranslateEmulatedPathToInternal(File path) { 959 final IMountService mountService = IMountService.Stub.asInterface( 960 ServiceManager.getService("mount")); 961 try { 962 final VolumeInfo[] vols = mountService.getVolumes(0); 963 for (VolumeInfo vol : vols) { 964 if ((vol.getType() == VolumeInfo.TYPE_EMULATED 965 || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) { 966 final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(), 967 vol.getInternalPath(), path); 968 if (internalPath != null) { 969 return internalPath; 970 } 971 } 972 } 973 } catch (RemoteException ignored) { 974 } 975 return path; 976 } 977 978 /// Consts to match the password types in cryptfs.h 979 /** @hide */ 980 public static final int CRYPT_TYPE_PASSWORD = 0; 981 /** @hide */ 982 public static final int CRYPT_TYPE_DEFAULT = 1; 983 /** @hide */ 984 public static final int CRYPT_TYPE_PATTERN = 2; 985 /** @hide */ 986 public static final int CRYPT_TYPE_PIN = 3; 987 988 // Constants for the data available via MountService.getField. 989 /** @hide */ 990 public static final String SYSTEM_LOCALE_KEY = "SystemLocale"; 991 /** @hide */ 992 public static final String OWNER_INFO_KEY = "OwnerInfo"; 993 /** @hide */ 994 public static final String PATTERN_VISIBLE_KEY = "PatternVisible"; 995 /** @hide */ 996 public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible"; 997} 998