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