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