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