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