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