StorageManager.java revision aa67f684ff43c81e3280c846245ec6ebe907787e
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.annotation.SdkConstant; 24import android.app.ActivityThread; 25import android.content.ContentResolver; 26import android.content.Context; 27import android.content.pm.IPackageMoveObserver; 28import android.content.pm.PackageManager; 29import android.os.Binder; 30import android.os.Environment; 31import android.os.FileUtils; 32import android.os.Handler; 33import android.os.Looper; 34import android.os.Message; 35import android.os.ParcelFileDescriptor; 36import android.os.RemoteException; 37import android.os.ServiceManager; 38import android.os.ServiceManager.ServiceNotFoundException; 39import android.os.SystemProperties; 40import android.os.UserHandle; 41import android.provider.Settings; 42import android.text.TextUtils; 43import android.util.Log; 44import android.util.Slog; 45import android.util.SparseArray; 46 47import com.android.internal.os.RoSystemProperties; 48import com.android.internal.os.SomeArgs; 49import com.android.internal.util.Preconditions; 50 51import java.io.BufferedReader; 52import java.io.File; 53import java.io.FileInputStream; 54import java.io.IOException; 55import java.io.InputStreamReader; 56import java.lang.ref.WeakReference; 57import java.util.ArrayList; 58import java.util.Arrays; 59import java.util.Collections; 60import java.util.Iterator; 61import java.util.List; 62import java.util.Objects; 63import java.util.concurrent.atomic.AtomicInteger; 64 65/** 66 * StorageManager is the interface to the systems storage service. The storage 67 * manager handles storage-related items such as Opaque Binary Blobs (OBBs). 68 * <p> 69 * OBBs contain a filesystem that maybe be encrypted on disk and mounted 70 * on-demand from an application. OBBs are a good way of providing large amounts 71 * of binary assets without packaging them into APKs as they may be multiple 72 * gigabytes in size. However, due to their size, they're most likely stored in 73 * a shared storage pool accessible from all programs. The system does not 74 * guarantee the security of the OBB file itself: if any program modifies the 75 * OBB, there is no guarantee that a read from that OBB will produce the 76 * expected output. 77 * <p> 78 * Get an instance of this class by calling 79 * {@link android.content.Context#getSystemService(java.lang.String)} with an 80 * argument of {@link android.content.Context#STORAGE_SERVICE}. 81 */ 82public class StorageManager { 83 private static final String TAG = "StorageManager"; 84 85 /** {@hide} */ 86 public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; 87 /** {@hide} */ 88 public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable"; 89 /** {@hide} */ 90 public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; 91 /** {@hide} */ 92 public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe"; 93 /** {@hide} */ 94 public static final String PROP_SDCARDFS = "persist.sys.sdcardfs"; 95 96 /** {@hide} */ 97 public static final String UUID_PRIVATE_INTERNAL = null; 98 /** {@hide} */ 99 public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; 100 101 102 /** 103 * Activity Action: Allows the user to manage their storage. This activity provides the ability 104 * to free up space on the device by deleting data such as apps. 105 * <p> 106 * Input: Nothing. 107 * <p> 108 * Output: Nothing. 109 */ 110 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 111 public static final String ACTION_MANAGE_STORAGE 112 = "android.os.storage.action.MANAGE_STORAGE"; 113 114 /** {@hide} */ 115 public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0; 116 /** {@hide} */ 117 public static final int DEBUG_EMULATE_FBE = 1 << 1; 118 /** {@hide} */ 119 public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2; 120 /** {@hide} */ 121 public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3; 122 123 // NOTE: keep in sync with installd 124 /** {@hide} */ 125 public static final int FLAG_STORAGE_DE = 1 << 0; 126 /** {@hide} */ 127 public static final int FLAG_STORAGE_CE = 1 << 1; 128 129 /** {@hide} */ 130 public static final int FLAG_FOR_WRITE = 1 << 8; 131 /** {@hide} */ 132 public static final int FLAG_REAL_STATE = 1 << 9; 133 /** {@hide} */ 134 public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10; 135 136 private static volatile IMountService sMountService = null; 137 138 // TODO: the location of the primary storage block varies from device to device, so we need to 139 // try the most likely candidates - a long-term solution would be a device-specific vold 140 // function that returns the calculated size. 141 private static final String[] INTERNAL_STORAGE_SIZE_PATHS = { 142 "/sys/block/mmcblk0/size", 143 "/sys/block/sda/size" 144 }; 145 private static final int INTERNAL_STORAGE_SECTOR_SIZE = 512; 146 147 private final Context mContext; 148 private final ContentResolver mResolver; 149 150 private final IMountService mMountService; 151 private final Looper mLooper; 152 private final AtomicInteger mNextNonce = new AtomicInteger(0); 153 154 private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>(); 155 156 private static class StorageEventListenerDelegate extends IMountServiceListener.Stub implements 157 Handler.Callback { 158 private static final int MSG_STORAGE_STATE_CHANGED = 1; 159 private static final int MSG_VOLUME_STATE_CHANGED = 2; 160 private static final int MSG_VOLUME_RECORD_CHANGED = 3; 161 private static final int MSG_VOLUME_FORGOTTEN = 4; 162 private static final int MSG_DISK_SCANNED = 5; 163 private static final int MSG_DISK_DESTROYED = 6; 164 165 final StorageEventListener mCallback; 166 final Handler mHandler; 167 168 public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) { 169 mCallback = callback; 170 mHandler = new Handler(looper, this); 171 } 172 173 @Override 174 public boolean handleMessage(Message msg) { 175 final SomeArgs args = (SomeArgs) msg.obj; 176 switch (msg.what) { 177 case MSG_STORAGE_STATE_CHANGED: 178 mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2, 179 (String) args.arg3); 180 args.recycle(); 181 return true; 182 case MSG_VOLUME_STATE_CHANGED: 183 mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); 184 args.recycle(); 185 return true; 186 case MSG_VOLUME_RECORD_CHANGED: 187 mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1); 188 args.recycle(); 189 return true; 190 case MSG_VOLUME_FORGOTTEN: 191 mCallback.onVolumeForgotten((String) args.arg1); 192 args.recycle(); 193 return true; 194 case MSG_DISK_SCANNED: 195 mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2); 196 args.recycle(); 197 return true; 198 case MSG_DISK_DESTROYED: 199 mCallback.onDiskDestroyed((DiskInfo) args.arg1); 200 args.recycle(); 201 return true; 202 } 203 args.recycle(); 204 return false; 205 } 206 207 @Override 208 public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException { 209 // Ignored 210 } 211 212 @Override 213 public void onStorageStateChanged(String path, String oldState, String newState) { 214 final SomeArgs args = SomeArgs.obtain(); 215 args.arg1 = path; 216 args.arg2 = oldState; 217 args.arg3 = newState; 218 mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); 219 } 220 221 @Override 222 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 223 final SomeArgs args = SomeArgs.obtain(); 224 args.arg1 = vol; 225 args.argi2 = oldState; 226 args.argi3 = newState; 227 mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); 228 } 229 230 @Override 231 public void onVolumeRecordChanged(VolumeRecord rec) { 232 final SomeArgs args = SomeArgs.obtain(); 233 args.arg1 = rec; 234 mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); 235 } 236 237 @Override 238 public void onVolumeForgotten(String fsUuid) { 239 final SomeArgs args = SomeArgs.obtain(); 240 args.arg1 = fsUuid; 241 mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); 242 } 243 244 @Override 245 public void onDiskScanned(DiskInfo disk, int volumeCount) { 246 final SomeArgs args = SomeArgs.obtain(); 247 args.arg1 = disk; 248 args.argi2 = volumeCount; 249 mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); 250 } 251 252 @Override 253 public void onDiskDestroyed(DiskInfo disk) throws RemoteException { 254 final SomeArgs args = SomeArgs.obtain(); 255 args.arg1 = disk; 256 mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget(); 257 } 258 } 259 260 /** 261 * Binder listener for OBB action results. 262 */ 263 private final ObbActionListener mObbActionListener = new ObbActionListener(); 264 265 private class ObbActionListener extends IObbActionListener.Stub { 266 @SuppressWarnings("hiding") 267 private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>(); 268 269 @Override 270 public void onObbResult(String filename, int nonce, int status) { 271 final ObbListenerDelegate delegate; 272 synchronized (mListeners) { 273 delegate = mListeners.get(nonce); 274 if (delegate != null) { 275 mListeners.remove(nonce); 276 } 277 } 278 279 if (delegate != null) { 280 delegate.sendObbStateChanged(filename, status); 281 } 282 } 283 284 public int addListener(OnObbStateChangeListener listener) { 285 final ObbListenerDelegate delegate = new ObbListenerDelegate(listener); 286 287 synchronized (mListeners) { 288 mListeners.put(delegate.nonce, delegate); 289 } 290 291 return delegate.nonce; 292 } 293 } 294 295 private int getNextNonce() { 296 return mNextNonce.getAndIncrement(); 297 } 298 299 /** 300 * Private class containing sender and receiver code for StorageEvents. 301 */ 302 private class ObbListenerDelegate { 303 private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef; 304 private final Handler mHandler; 305 306 private final int nonce; 307 308 ObbListenerDelegate(OnObbStateChangeListener listener) { 309 nonce = getNextNonce(); 310 mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener); 311 mHandler = new Handler(mLooper) { 312 @Override 313 public void handleMessage(Message msg) { 314 final OnObbStateChangeListener changeListener = getListener(); 315 if (changeListener == null) { 316 return; 317 } 318 319 changeListener.onObbStateChange((String) msg.obj, msg.arg1); 320 } 321 }; 322 } 323 324 OnObbStateChangeListener getListener() { 325 if (mObbEventListenerRef == null) { 326 return null; 327 } 328 return mObbEventListenerRef.get(); 329 } 330 331 void sendObbStateChanged(String path, int state) { 332 mHandler.obtainMessage(0, state, 0, path).sendToTarget(); 333 } 334 } 335 336 /** {@hide} */ 337 @Deprecated 338 public static StorageManager from(Context context) { 339 return context.getSystemService(StorageManager.class); 340 } 341 342 /** 343 * Constructs a StorageManager object through which an application can 344 * can communicate with the systems mount service. 345 * 346 * @param looper The {@link android.os.Looper} which events will be received on. 347 * 348 * <p>Applications can get instance of this class by calling 349 * {@link android.content.Context#getSystemService(java.lang.String)} with an argument 350 * of {@link android.content.Context#STORAGE_SERVICE}. 351 * 352 * @hide 353 */ 354 public StorageManager(Context context, Looper looper) throws ServiceNotFoundException { 355 mContext = context; 356 mResolver = context.getContentResolver(); 357 mLooper = looper; 358 mMountService = IMountService.Stub.asInterface(ServiceManager.getServiceOrThrow("mount")); 359 } 360 361 /** 362 * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}. 363 * 364 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 365 * 366 * @hide 367 */ 368 public void registerListener(StorageEventListener listener) { 369 synchronized (mDelegates) { 370 final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener, 371 mLooper); 372 try { 373 mMountService.registerListener(delegate); 374 } catch (RemoteException e) { 375 throw e.rethrowFromSystemServer(); 376 } 377 mDelegates.add(delegate); 378 } 379 } 380 381 /** 382 * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}. 383 * 384 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 385 * 386 * @hide 387 */ 388 public void unregisterListener(StorageEventListener listener) { 389 synchronized (mDelegates) { 390 for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) { 391 final StorageEventListenerDelegate delegate = i.next(); 392 if (delegate.mCallback == listener) { 393 try { 394 mMountService.unregisterListener(delegate); 395 } catch (RemoteException e) { 396 throw e.rethrowFromSystemServer(); 397 } 398 i.remove(); 399 } 400 } 401 } 402 } 403 404 /** 405 * Enables USB Mass Storage (UMS) on the device. 406 * 407 * @hide 408 */ 409 @Deprecated 410 public void enableUsbMassStorage() { 411 } 412 413 /** 414 * Disables USB Mass Storage (UMS) on the device. 415 * 416 * @hide 417 */ 418 @Deprecated 419 public void disableUsbMassStorage() { 420 } 421 422 /** 423 * Query if a USB Mass Storage (UMS) host is connected. 424 * @return true if UMS host is connected. 425 * 426 * @hide 427 */ 428 @Deprecated 429 public boolean isUsbMassStorageConnected() { 430 return false; 431 } 432 433 /** 434 * Query if a USB Mass Storage (UMS) is enabled on the device. 435 * @return true if UMS host is enabled. 436 * 437 * @hide 438 */ 439 @Deprecated 440 public boolean isUsbMassStorageEnabled() { 441 return false; 442 } 443 444 /** 445 * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is 446 * specified, it is supplied to the mounting process to be used in any 447 * encryption used in the OBB. 448 * <p> 449 * The OBB will remain mounted for as long as the StorageManager reference 450 * is held by the application. As soon as this reference is lost, the OBBs 451 * in use will be unmounted. The {@link OnObbStateChangeListener} registered 452 * with this call will receive the success or failure of this operation. 453 * <p> 454 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 455 * file matches a package ID that is owned by the calling program's UID. 456 * That is, shared UID applications can attempt to mount any other 457 * application's OBB that shares its UID. 458 * 459 * @param rawPath the path to the OBB file 460 * @param key secret used to encrypt the OBB; may be <code>null</code> if no 461 * encryption was used on the OBB. 462 * @param listener will receive the success or failure of the operation 463 * @return whether the mount call was successfully queued or not 464 */ 465 public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) { 466 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 467 Preconditions.checkNotNull(listener, "listener cannot be null"); 468 469 try { 470 final String canonicalPath = new File(rawPath).getCanonicalPath(); 471 final int nonce = mObbActionListener.addListener(listener); 472 mMountService.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce); 473 return true; 474 } catch (IOException e) { 475 throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e); 476 } catch (RemoteException e) { 477 throw e.rethrowFromSystemServer(); 478 } 479 } 480 481 /** 482 * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the 483 * <code>force</code> flag is true, it will kill any application needed to 484 * unmount the given OBB (even the calling application). 485 * <p> 486 * The {@link OnObbStateChangeListener} registered with this call will 487 * receive the success or failure of this operation. 488 * <p> 489 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 490 * file matches a package ID that is owned by the calling program's UID. 491 * That is, shared UID applications can obtain access to any other 492 * application's OBB that shares its UID. 493 * <p> 494 * 495 * @param rawPath path to the OBB file 496 * @param force whether to kill any programs using this in order to unmount 497 * it 498 * @param listener will receive the success or failure of the operation 499 * @return whether the unmount call was successfully queued or not 500 */ 501 public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) { 502 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 503 Preconditions.checkNotNull(listener, "listener cannot be null"); 504 505 try { 506 final int nonce = mObbActionListener.addListener(listener); 507 mMountService.unmountObb(rawPath, force, mObbActionListener, nonce); 508 return true; 509 } catch (RemoteException e) { 510 throw e.rethrowFromSystemServer(); 511 } 512 } 513 514 /** 515 * Check whether an Opaque Binary Blob (OBB) is mounted or not. 516 * 517 * @param rawPath path to OBB image 518 * @return true if OBB is mounted; false if not mounted or on error 519 */ 520 public boolean isObbMounted(String rawPath) { 521 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 522 523 try { 524 return mMountService.isObbMounted(rawPath); 525 } catch (RemoteException e) { 526 throw e.rethrowFromSystemServer(); 527 } 528 } 529 530 /** 531 * Check the mounted path of an Opaque Binary Blob (OBB) file. This will 532 * give you the path to where you can obtain access to the internals of the 533 * OBB. 534 * 535 * @param rawPath path to OBB image 536 * @return absolute path to mounted OBB image data or <code>null</code> if 537 * not mounted or exception encountered trying to read status 538 */ 539 public String getMountedObbPath(String rawPath) { 540 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 541 542 try { 543 return mMountService.getMountedObbPath(rawPath); 544 } catch (RemoteException e) { 545 throw e.rethrowFromSystemServer(); 546 } 547 } 548 549 /** {@hide} */ 550 public @NonNull List<DiskInfo> getDisks() { 551 try { 552 return Arrays.asList(mMountService.getDisks()); 553 } catch (RemoteException e) { 554 throw e.rethrowFromSystemServer(); 555 } 556 } 557 558 /** {@hide} */ 559 public @Nullable DiskInfo findDiskById(String id) { 560 Preconditions.checkNotNull(id); 561 // TODO; go directly to service to make this faster 562 for (DiskInfo disk : getDisks()) { 563 if (Objects.equals(disk.id, id)) { 564 return disk; 565 } 566 } 567 return null; 568 } 569 570 /** {@hide} */ 571 public @Nullable VolumeInfo findVolumeById(String id) { 572 Preconditions.checkNotNull(id); 573 // TODO; go directly to service to make this faster 574 for (VolumeInfo vol : getVolumes()) { 575 if (Objects.equals(vol.id, id)) { 576 return vol; 577 } 578 } 579 return null; 580 } 581 582 /** {@hide} */ 583 public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) { 584 Preconditions.checkNotNull(fsUuid); 585 // TODO; go directly to service to make this faster 586 for (VolumeInfo vol : getVolumes()) { 587 if (Objects.equals(vol.fsUuid, fsUuid)) { 588 return vol; 589 } 590 } 591 return null; 592 } 593 594 /** {@hide} */ 595 public @Nullable VolumeRecord findRecordByUuid(String fsUuid) { 596 Preconditions.checkNotNull(fsUuid); 597 // TODO; go directly to service to make this faster 598 for (VolumeRecord rec : getVolumeRecords()) { 599 if (Objects.equals(rec.fsUuid, fsUuid)) { 600 return rec; 601 } 602 } 603 return null; 604 } 605 606 /** {@hide} */ 607 public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) { 608 if (emulatedVol != null) { 609 return findVolumeById(emulatedVol.getId().replace("emulated", "private")); 610 } else { 611 return null; 612 } 613 } 614 615 /** {@hide} */ 616 public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) { 617 if (privateVol != null) { 618 return findVolumeById(privateVol.getId().replace("private", "emulated")); 619 } else { 620 return null; 621 } 622 } 623 624 /** {@hide} */ 625 public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) { 626 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { 627 return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); 628 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 629 return getPrimaryPhysicalVolume(); 630 } else { 631 return findVolumeByUuid(volumeUuid); 632 } 633 } 634 635 /** {@hide} */ 636 public @NonNull List<VolumeInfo> getVolumes() { 637 try { 638 return Arrays.asList(mMountService.getVolumes(0)); 639 } catch (RemoteException e) { 640 throw e.rethrowFromSystemServer(); 641 } 642 } 643 644 /** {@hide} */ 645 public @NonNull List<VolumeInfo> getWritablePrivateVolumes() { 646 try { 647 final ArrayList<VolumeInfo> res = new ArrayList<>(); 648 for (VolumeInfo vol : mMountService.getVolumes(0)) { 649 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) { 650 res.add(vol); 651 } 652 } 653 return res; 654 } catch (RemoteException e) { 655 throw e.rethrowFromSystemServer(); 656 } 657 } 658 659 /** {@hide} */ 660 public @NonNull List<VolumeRecord> getVolumeRecords() { 661 try { 662 return Arrays.asList(mMountService.getVolumeRecords(0)); 663 } catch (RemoteException e) { 664 throw e.rethrowFromSystemServer(); 665 } 666 } 667 668 /** {@hide} */ 669 public @Nullable String getBestVolumeDescription(VolumeInfo vol) { 670 if (vol == null) return null; 671 672 // Nickname always takes precedence when defined 673 if (!TextUtils.isEmpty(vol.fsUuid)) { 674 final VolumeRecord rec = findRecordByUuid(vol.fsUuid); 675 if (rec != null && !TextUtils.isEmpty(rec.nickname)) { 676 return rec.nickname; 677 } 678 } 679 680 if (!TextUtils.isEmpty(vol.getDescription())) { 681 return vol.getDescription(); 682 } 683 684 if (vol.disk != null) { 685 return vol.disk.getDescription(); 686 } 687 688 return null; 689 } 690 691 /** {@hide} */ 692 public @Nullable VolumeInfo getPrimaryPhysicalVolume() { 693 final List<VolumeInfo> vols = getVolumes(); 694 for (VolumeInfo vol : vols) { 695 if (vol.isPrimaryPhysical()) { 696 return vol; 697 } 698 } 699 return null; 700 } 701 702 /** {@hide} */ 703 public void mount(String volId) { 704 try { 705 mMountService.mount(volId); 706 } catch (RemoteException e) { 707 throw e.rethrowFromSystemServer(); 708 } 709 } 710 711 /** {@hide} */ 712 public void unmount(String volId) { 713 try { 714 mMountService.unmount(volId); 715 } catch (RemoteException e) { 716 throw e.rethrowFromSystemServer(); 717 } 718 } 719 720 /** {@hide} */ 721 public void format(String volId) { 722 try { 723 mMountService.format(volId); 724 } catch (RemoteException e) { 725 throw e.rethrowFromSystemServer(); 726 } 727 } 728 729 /** {@hide} */ 730 public long benchmark(String volId) { 731 try { 732 return mMountService.benchmark(volId); 733 } catch (RemoteException e) { 734 throw e.rethrowFromSystemServer(); 735 } 736 } 737 738 /** {@hide} */ 739 public void partitionPublic(String diskId) { 740 try { 741 mMountService.partitionPublic(diskId); 742 } catch (RemoteException e) { 743 throw e.rethrowFromSystemServer(); 744 } 745 } 746 747 /** {@hide} */ 748 public void partitionPrivate(String diskId) { 749 try { 750 mMountService.partitionPrivate(diskId); 751 } catch (RemoteException e) { 752 throw e.rethrowFromSystemServer(); 753 } 754 } 755 756 /** {@hide} */ 757 public void partitionMixed(String diskId, int ratio) { 758 try { 759 mMountService.partitionMixed(diskId, ratio); 760 } catch (RemoteException e) { 761 throw e.rethrowFromSystemServer(); 762 } 763 } 764 765 /** {@hide} */ 766 public void wipeAdoptableDisks() { 767 // We only wipe devices in "adoptable" locations, which are in a 768 // long-term stable slot/location on the device, where apps have a 769 // reasonable chance of storing sensitive data. (Apps need to go through 770 // SAF to write to transient volumes.) 771 final List<DiskInfo> disks = getDisks(); 772 for (DiskInfo disk : disks) { 773 final String diskId = disk.getId(); 774 if (disk.isAdoptable()) { 775 Slog.d(TAG, "Found adoptable " + diskId + "; wiping"); 776 try { 777 // TODO: switch to explicit wipe command when we have it, 778 // for now rely on the fact that vfat format does a wipe 779 mMountService.partitionPublic(diskId); 780 } catch (Exception e) { 781 Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e); 782 } 783 } else { 784 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId()); 785 } 786 } 787 } 788 789 /** {@hide} */ 790 public void setVolumeNickname(String fsUuid, String nickname) { 791 try { 792 mMountService.setVolumeNickname(fsUuid, nickname); 793 } catch (RemoteException e) { 794 throw e.rethrowFromSystemServer(); 795 } 796 } 797 798 /** {@hide} */ 799 public void setVolumeInited(String fsUuid, boolean inited) { 800 try { 801 mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0, 802 VolumeRecord.USER_FLAG_INITED); 803 } catch (RemoteException e) { 804 throw e.rethrowFromSystemServer(); 805 } 806 } 807 808 /** {@hide} */ 809 public void setVolumeSnoozed(String fsUuid, boolean snoozed) { 810 try { 811 mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0, 812 VolumeRecord.USER_FLAG_SNOOZED); 813 } catch (RemoteException e) { 814 throw e.rethrowFromSystemServer(); 815 } 816 } 817 818 /** {@hide} */ 819 public void forgetVolume(String fsUuid) { 820 try { 821 mMountService.forgetVolume(fsUuid); 822 } catch (RemoteException e) { 823 throw e.rethrowFromSystemServer(); 824 } 825 } 826 827 /** 828 * This is not the API you're looking for. 829 * 830 * @see PackageManager#getPrimaryStorageCurrentVolume() 831 * @hide 832 */ 833 public String getPrimaryStorageUuid() { 834 try { 835 return mMountService.getPrimaryStorageUuid(); 836 } catch (RemoteException e) { 837 throw e.rethrowFromSystemServer(); 838 } 839 } 840 841 /** 842 * This is not the API you're looking for. 843 * 844 * @see PackageManager#movePrimaryStorage(VolumeInfo) 845 * @hide 846 */ 847 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { 848 try { 849 mMountService.setPrimaryStorageUuid(volumeUuid, callback); 850 } catch (RemoteException e) { 851 throw e.rethrowFromSystemServer(); 852 } 853 } 854 855 /** 856 * Return the {@link StorageVolume} that contains the given file, or {@code null} if none. 857 */ 858 public @Nullable StorageVolume getStorageVolume(File file) { 859 return getStorageVolume(getVolumeList(), file); 860 } 861 862 /** {@hide} */ 863 public static @Nullable StorageVolume getStorageVolume(File file, int userId) { 864 return getStorageVolume(getVolumeList(userId, 0), file); 865 } 866 867 /** {@hide} */ 868 private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) { 869 if (file == null) { 870 return null; 871 } 872 try { 873 file = file.getCanonicalFile(); 874 } catch (IOException ignored) { 875 Slog.d(TAG, "Could not get canonical path for " + file); 876 return null; 877 } 878 for (StorageVolume volume : volumes) { 879 File volumeFile = volume.getPathFile(); 880 try { 881 volumeFile = volumeFile.getCanonicalFile(); 882 } catch (IOException ignored) { 883 continue; 884 } 885 if (FileUtils.contains(volumeFile, file)) { 886 return volume; 887 } 888 } 889 return null; 890 } 891 892 /** 893 * Gets the state of a volume via its mountpoint. 894 * @hide 895 */ 896 @Deprecated 897 public @NonNull String getVolumeState(String mountPoint) { 898 final StorageVolume vol = getStorageVolume(new File(mountPoint)); 899 if (vol != null) { 900 return vol.getState(); 901 } else { 902 return Environment.MEDIA_UNKNOWN; 903 } 904 } 905 906 /** 907 * Return the list of shared/external storage volumes available to the 908 * current user. This includes both the primary shared storage device and 909 * any attached external volumes including SD cards and USB drives. 910 * 911 * @see Environment#getExternalStorageDirectory() 912 * @see StorageVolume#createAccessIntent(String) 913 */ 914 public @NonNull List<StorageVolume> getStorageVolumes() { 915 final ArrayList<StorageVolume> res = new ArrayList<>(); 916 Collections.addAll(res, 917 getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)); 918 return res; 919 } 920 921 /** 922 * Return the primary shared/external storage volume available to the 923 * current user. This volume is the same storage device returned by 924 * {@link Environment#getExternalStorageDirectory()} and 925 * {@link Context#getExternalFilesDir(String)}. 926 */ 927 public @NonNull StorageVolume getPrimaryStorageVolume() { 928 return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0]; 929 } 930 931 /** {@hide} */ 932 public long getPrimaryStorageSize() { 933 for (String path : INTERNAL_STORAGE_SIZE_PATHS) { 934 final long numberBlocks = readLong(path); 935 if (numberBlocks > 0) { 936 return numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE; 937 } 938 } 939 return 0; 940 } 941 942 private long readLong(String path) { 943 try (final FileInputStream fis = new FileInputStream(path); 944 final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) { 945 return Long.parseLong(reader.readLine()); 946 } catch (Exception e) { 947 Slog.w(TAG, "Could not read " + path, e); 948 return 0; 949 } 950 } 951 952 /** @removed */ 953 public @NonNull StorageVolume[] getVolumeList() { 954 return getVolumeList(mContext.getUserId(), 0); 955 } 956 957 /** {@hide} */ 958 public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) { 959 final IMountService mountService = IMountService.Stub.asInterface( 960 ServiceManager.getService("mount")); 961 try { 962 String packageName = ActivityThread.currentOpPackageName(); 963 if (packageName == null) { 964 // Package name can be null if the activity thread is running but the app 965 // hasn't bound yet. In this case we fall back to the first package in the 966 // current UID. This works for runtime permissions as permission state is 967 // per UID and permission realted app ops are updated for all UID packages. 968 String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid( 969 android.os.Process.myUid()); 970 if (packageNames == null || packageNames.length <= 0) { 971 return new StorageVolume[0]; 972 } 973 packageName = packageNames[0]; 974 } 975 final int uid = ActivityThread.getPackageManager().getPackageUid(packageName, 976 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId); 977 if (uid <= 0) { 978 return new StorageVolume[0]; 979 } 980 return mountService.getVolumeList(uid, packageName, flags); 981 } catch (RemoteException e) { 982 throw e.rethrowFromSystemServer(); 983 } 984 } 985 986 /** 987 * Returns list of paths for all mountable volumes. 988 * @hide 989 */ 990 @Deprecated 991 public @NonNull String[] getVolumePaths() { 992 StorageVolume[] volumes = getVolumeList(); 993 int count = volumes.length; 994 String[] paths = new String[count]; 995 for (int i = 0; i < count; i++) { 996 paths[i] = volumes[i].getPath(); 997 } 998 return paths; 999 } 1000 1001 /** @removed */ 1002 public @NonNull StorageVolume getPrimaryVolume() { 1003 return getPrimaryVolume(getVolumeList()); 1004 } 1005 1006 /** {@hide} */ 1007 public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) { 1008 for (StorageVolume volume : volumes) { 1009 if (volume.isPrimary()) { 1010 return volume; 1011 } 1012 } 1013 throw new IllegalStateException("Missing primary storage"); 1014 } 1015 1016 /** {@hide} */ 1017 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; 1018 private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES; 1019 private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES; 1020 1021 /** 1022 * Return the number of available bytes until the given path is considered 1023 * running low on storage. 1024 * 1025 * @hide 1026 */ 1027 public long getStorageBytesUntilLow(File path) { 1028 return path.getUsableSpace() - getStorageFullBytes(path); 1029 } 1030 1031 /** 1032 * Return the number of available bytes at which the given path is 1033 * considered running low on storage. 1034 * 1035 * @hide 1036 */ 1037 public long getStorageLowBytes(File path) { 1038 final long lowPercent = Settings.Global.getInt(mResolver, 1039 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); 1040 final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; 1041 1042 final long maxLowBytes = Settings.Global.getLong(mResolver, 1043 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); 1044 1045 return Math.min(lowBytes, maxLowBytes); 1046 } 1047 1048 /** 1049 * Return the number of available bytes at which the given path is 1050 * considered full. 1051 * 1052 * @hide 1053 */ 1054 public long getStorageFullBytes(File path) { 1055 return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, 1056 DEFAULT_FULL_THRESHOLD_BYTES); 1057 } 1058 1059 /** {@hide} */ 1060 public void createUserKey(int userId, int serialNumber, boolean ephemeral) { 1061 try { 1062 mMountService.createUserKey(userId, serialNumber, ephemeral); 1063 } catch (RemoteException e) { 1064 throw e.rethrowFromSystemServer(); 1065 } 1066 } 1067 1068 /** {@hide} */ 1069 public void destroyUserKey(int userId) { 1070 try { 1071 mMountService.destroyUserKey(userId); 1072 } catch (RemoteException e) { 1073 throw e.rethrowFromSystemServer(); 1074 } 1075 } 1076 1077 /** {@hide} */ 1078 public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) { 1079 try { 1080 mMountService.unlockUserKey(userId, serialNumber, token, secret); 1081 } catch (RemoteException e) { 1082 throw e.rethrowFromSystemServer(); 1083 } 1084 } 1085 1086 /** {@hide} */ 1087 public void lockUserKey(int userId) { 1088 try { 1089 mMountService.lockUserKey(userId); 1090 } catch (RemoteException e) { 1091 throw e.rethrowFromSystemServer(); 1092 } 1093 } 1094 1095 /** {@hide} */ 1096 public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { 1097 try { 1098 mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, flags); 1099 } catch (RemoteException e) { 1100 throw e.rethrowFromSystemServer(); 1101 } 1102 } 1103 1104 /** {@hide} */ 1105 public void destroyUserStorage(String volumeUuid, int userId, int flags) { 1106 try { 1107 mMountService.destroyUserStorage(volumeUuid, userId, flags); 1108 } catch (RemoteException e) { 1109 throw e.rethrowFromSystemServer(); 1110 } 1111 } 1112 1113 /** {@hide} */ 1114 public static boolean isUserKeyUnlocked(int userId) { 1115 if (sMountService == null) { 1116 sMountService = IMountService.Stub 1117 .asInterface(ServiceManager.getService("mount")); 1118 } 1119 if (sMountService == null) { 1120 Slog.w(TAG, "Early during boot, assuming locked"); 1121 return false; 1122 } 1123 final long token = Binder.clearCallingIdentity(); 1124 try { 1125 return sMountService.isUserKeyUnlocked(userId); 1126 } catch (RemoteException e) { 1127 throw e.rethrowAsRuntimeException(); 1128 } finally { 1129 Binder.restoreCallingIdentity(token); 1130 } 1131 } 1132 1133 /** 1134 * Return if data stored at or under the given path will be encrypted while 1135 * at rest. This can help apps avoid the overhead of double-encrypting data. 1136 */ 1137 public boolean isEncrypted(File file) { 1138 if (FileUtils.contains(Environment.getDataDirectory(), file)) { 1139 return isEncrypted(); 1140 } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) { 1141 return true; 1142 } 1143 // TODO: extend to support shared storage 1144 return false; 1145 } 1146 1147 /** {@hide} 1148 * Is this device encryptable or already encrypted? 1149 * @return true for encryptable or encrypted 1150 * false not encrypted and not encryptable 1151 */ 1152 public static boolean isEncryptable() { 1153 return RoSystemProperties.CRYPTO_ENCRYPTABLE; 1154 } 1155 1156 /** {@hide} 1157 * Is this device already encrypted? 1158 * @return true for encrypted. (Implies isEncryptable() == true) 1159 * false not encrypted 1160 */ 1161 public static boolean isEncrypted() { 1162 return RoSystemProperties.CRYPTO_ENCRYPTED; 1163 } 1164 1165 /** {@hide} 1166 * Is this device file encrypted? 1167 * @return true for file encrypted. (Implies isEncrypted() == true) 1168 * false not encrypted or block encrypted 1169 */ 1170 public static boolean isFileEncryptedNativeOnly() { 1171 if (!isEncrypted()) { 1172 return false; 1173 } 1174 return RoSystemProperties.CRYPTO_FILE_ENCRYPTED; 1175 } 1176 1177 /** {@hide} 1178 * Is this device block encrypted? 1179 * @return true for block encrypted. (Implies isEncrypted() == true) 1180 * false not encrypted or file encrypted 1181 */ 1182 public static boolean isBlockEncrypted() { 1183 if (!isEncrypted()) { 1184 return false; 1185 } 1186 return RoSystemProperties.CRYPTO_BLOCK_ENCRYPTED; 1187 } 1188 1189 /** {@hide} 1190 * Is this device block encrypted with credentials? 1191 * @return true for crediential block encrypted. 1192 * (Implies isBlockEncrypted() == true) 1193 * false not encrypted, file encrypted or default block encrypted 1194 */ 1195 public static boolean isNonDefaultBlockEncrypted() { 1196 if (!isBlockEncrypted()) { 1197 return false; 1198 } 1199 1200 try { 1201 IMountService mountService = IMountService.Stub.asInterface( 1202 ServiceManager.getService("mount")); 1203 return mountService.getPasswordType() != CRYPT_TYPE_DEFAULT; 1204 } catch (RemoteException e) { 1205 Log.e(TAG, "Error getting encryption type"); 1206 return false; 1207 } 1208 } 1209 1210 /** {@hide} 1211 * Is this device in the process of being block encrypted? 1212 * @return true for encrypting. 1213 * false otherwise 1214 * Whether device isEncrypted at this point is undefined 1215 * Note that only system services and CryptKeeper will ever see this return 1216 * true - no app will ever be launched in this state. 1217 * Also note that this state will not change without a teardown of the 1218 * framework, so no service needs to check for changes during their lifespan 1219 */ 1220 public static boolean isBlockEncrypting() { 1221 final String state = SystemProperties.get("vold.encrypt_progress", ""); 1222 return !"".equalsIgnoreCase(state); 1223 } 1224 1225 /** {@hide} 1226 * Is this device non default block encrypted and in the process of 1227 * prompting for credentials? 1228 * @return true for prompting for credentials. 1229 * (Implies isNonDefaultBlockEncrypted() == true) 1230 * false otherwise 1231 * Note that only system services and CryptKeeper will ever see this return 1232 * true - no app will ever be launched in this state. 1233 * Also note that this state will not change without a teardown of the 1234 * framework, so no service needs to check for changes during their lifespan 1235 */ 1236 public static boolean inCryptKeeperBounce() { 1237 final String status = SystemProperties.get("vold.decrypt"); 1238 return "trigger_restart_min_framework".equals(status); 1239 } 1240 1241 /** {@hide} */ 1242 public static boolean isFileEncryptedEmulatedOnly() { 1243 return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false); 1244 } 1245 1246 /** {@hide} 1247 * Is this device running in a file encrypted mode, either native or emulated? 1248 * @return true for file encrypted, false otherwise 1249 */ 1250 public static boolean isFileEncryptedNativeOrEmulated() { 1251 return isFileEncryptedNativeOnly() 1252 || isFileEncryptedEmulatedOnly(); 1253 } 1254 1255 /** {@hide} */ 1256 public static File maybeTranslateEmulatedPathToInternal(File path) { 1257 final IMountService mountService = IMountService.Stub.asInterface( 1258 ServiceManager.getService("mount")); 1259 try { 1260 final VolumeInfo[] vols = mountService.getVolumes(0); 1261 for (VolumeInfo vol : vols) { 1262 if ((vol.getType() == VolumeInfo.TYPE_EMULATED 1263 || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) { 1264 final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(), 1265 vol.getInternalPath(), path); 1266 if (internalPath != null && internalPath.exists()) { 1267 return internalPath; 1268 } 1269 } 1270 } 1271 } catch (RemoteException e) { 1272 throw e.rethrowFromSystemServer(); 1273 } 1274 return path; 1275 } 1276 1277 /** {@hide} */ 1278 public ParcelFileDescriptor mountAppFuse(String name) { 1279 try { 1280 return mMountService.mountAppFuse(name); 1281 } catch (RemoteException e) { 1282 throw e.rethrowFromSystemServer(); 1283 } 1284 } 1285 1286 /// Consts to match the password types in cryptfs.h 1287 /** @hide */ 1288 public static final int CRYPT_TYPE_PASSWORD = 0; 1289 /** @hide */ 1290 public static final int CRYPT_TYPE_DEFAULT = 1; 1291 /** @hide */ 1292 public static final int CRYPT_TYPE_PATTERN = 2; 1293 /** @hide */ 1294 public static final int CRYPT_TYPE_PIN = 3; 1295 1296 // Constants for the data available via MountService.getField. 1297 /** @hide */ 1298 public static final String SYSTEM_LOCALE_KEY = "SystemLocale"; 1299 /** @hide */ 1300 public static final String OWNER_INFO_KEY = "OwnerInfo"; 1301 /** @hide */ 1302 public static final String PATTERN_VISIBLE_KEY = "PatternVisible"; 1303 /** @hide */ 1304 public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible"; 1305} 1306