StorageManager.java revision d5d5e926eb8ff2541a845d6a5657cee5e5c6e7b6
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.GB_IN_BYTES; 20import static android.net.TrafficStats.MB_IN_BYTES; 21 22import android.annotation.IntDef; 23import android.annotation.NonNull; 24import android.annotation.Nullable; 25import android.annotation.RequiresPermission; 26import android.annotation.SdkConstant; 27import android.app.ActivityThread; 28import android.content.ContentResolver; 29import android.content.Context; 30import android.content.pm.ApplicationInfo; 31import android.content.pm.IPackageMoveObserver; 32import android.content.pm.PackageManager; 33import android.os.Binder; 34import android.os.Environment; 35import android.os.FileUtils; 36import android.os.Handler; 37import android.os.Looper; 38import android.os.Message; 39import android.os.ParcelFileDescriptor; 40import android.os.ParcelableException; 41import android.os.ProxyFileDescriptorCallback; 42import android.os.RemoteException; 43import android.os.ServiceManager; 44import android.os.ServiceManager.ServiceNotFoundException; 45import android.os.SystemProperties; 46import android.os.UserHandle; 47import android.provider.Settings; 48import android.system.ErrnoException; 49import android.system.Os; 50import android.system.OsConstants; 51import android.text.TextUtils; 52import android.util.Log; 53import android.util.Pair; 54import android.util.Slog; 55import android.util.SparseArray; 56 57import com.android.internal.annotations.GuardedBy; 58import com.android.internal.annotations.VisibleForTesting; 59import com.android.internal.os.AppFuseMount; 60import com.android.internal.os.FuseAppLoop; 61import com.android.internal.os.RoSystemProperties; 62import com.android.internal.os.SomeArgs; 63import com.android.internal.util.Preconditions; 64 65import java.io.BufferedReader; 66import java.io.File; 67import java.io.FileDescriptor; 68import java.io.FileInputStream; 69import java.io.FileNotFoundException; 70import java.io.IOException; 71import java.io.InputStreamReader; 72import java.lang.annotation.Retention; 73import java.lang.annotation.RetentionPolicy; 74import java.lang.ref.WeakReference; 75import java.nio.charset.StandardCharsets; 76import java.util.ArrayList; 77import java.util.Arrays; 78import java.util.Collections; 79import java.util.Iterator; 80import java.util.List; 81import java.util.Objects; 82import java.util.concurrent.ThreadFactory; 83import java.util.concurrent.atomic.AtomicInteger; 84 85/** 86 * StorageManager is the interface to the systems storage service. The storage 87 * manager handles storage-related items such as Opaque Binary Blobs (OBBs). 88 * <p> 89 * OBBs contain a filesystem that maybe be encrypted on disk and mounted 90 * on-demand from an application. OBBs are a good way of providing large amounts 91 * of binary assets without packaging them into APKs as they may be multiple 92 * gigabytes in size. However, due to their size, they're most likely stored in 93 * a shared storage pool accessible from all programs. The system does not 94 * guarantee the security of the OBB file itself: if any program modifies the 95 * OBB, there is no guarantee that a read from that OBB will produce the 96 * expected output. 97 * <p> 98 * Get an instance of this class by calling 99 * {@link android.content.Context#getSystemService(java.lang.String)} with an 100 * argument of {@link android.content.Context#STORAGE_SERVICE}. 101 */ 102public class StorageManager { 103 private static final String TAG = "StorageManager"; 104 105 /** {@hide} */ 106 public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; 107 /** {@hide} */ 108 public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable"; 109 /** {@hide} */ 110 public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; 111 /** {@hide} */ 112 public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe"; 113 /** {@hide} */ 114 public static final String PROP_SDCARDFS = "persist.sys.sdcardfs"; 115 116 /** {@hide} */ 117 public static final String UUID_PRIVATE_INTERNAL = null; 118 /** {@hide} */ 119 public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; 120 121 122 /** 123 * Activity Action: Allows the user to manage their storage. This activity provides the ability 124 * to free up space on the device by deleting data such as apps. 125 * <p> 126 * Input: Nothing. 127 * <p> 128 * Output: Nothing. 129 */ 130 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 131 public static final String ACTION_MANAGE_STORAGE 132 = "android.os.storage.action.MANAGE_STORAGE"; 133 134 /** {@hide} */ 135 public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0; 136 /** {@hide} */ 137 public static final int DEBUG_EMULATE_FBE = 1 << 1; 138 /** {@hide} */ 139 public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2; 140 /** {@hide} */ 141 public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3; 142 143 // NOTE: keep in sync with installd 144 /** {@hide} */ 145 public static final int FLAG_STORAGE_DE = 1 << 0; 146 /** {@hide} */ 147 public static final int FLAG_STORAGE_CE = 1 << 1; 148 149 /** {@hide} */ 150 public static final int FLAG_FOR_WRITE = 1 << 8; 151 /** {@hide} */ 152 public static final int FLAG_REAL_STATE = 1 << 9; 153 /** {@hide} */ 154 public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10; 155 156 /** {@hide} */ 157 public static final int FSTRIM_FLAG_DEEP = 1 << 0; 158 /** {@hide} */ 159 public static final int FSTRIM_FLAG_BENCHMARK = 1 << 1; 160 161 /** @hide The volume is not encrypted. */ 162 public static final int ENCRYPTION_STATE_NONE = 1; 163 164 /** @hide The volume has been encrypted succesfully. */ 165 public static final int ENCRYPTION_STATE_OK = 0; 166 167 /** @hide The volume is in a bad state.*/ 168 public static final int ENCRYPTION_STATE_ERROR_UNKNOWN = -1; 169 170 /** @hide Encryption is incomplete */ 171 public static final int ENCRYPTION_STATE_ERROR_INCOMPLETE = -2; 172 173 /** @hide Encryption is incomplete and irrecoverable */ 174 public static final int ENCRYPTION_STATE_ERROR_INCONSISTENT = -3; 175 176 /** @hide Underlying data is corrupt */ 177 public static final int ENCRYPTION_STATE_ERROR_CORRUPT = -4; 178 179 private static volatile IStorageManager sStorageManager = null; 180 181 // TODO: the location of the primary storage block varies from device to device, so we need to 182 // try the most likely candidates - a long-term solution would be a device-specific vold 183 // function that returns the calculated size. 184 private static final String[] INTERNAL_STORAGE_SIZE_PATHS = { 185 "/sys/block/mmcblk0/size", 186 "/sys/block/sda/size" 187 }; 188 private static final int INTERNAL_STORAGE_SECTOR_SIZE = 512; 189 190 private final Context mContext; 191 private final ContentResolver mResolver; 192 193 private final IStorageManager mStorageManager; 194 private final Looper mLooper; 195 private final AtomicInteger mNextNonce = new AtomicInteger(0); 196 197 private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>(); 198 199 private static class StorageEventListenerDelegate extends IStorageEventListener.Stub implements 200 Handler.Callback { 201 private static final int MSG_STORAGE_STATE_CHANGED = 1; 202 private static final int MSG_VOLUME_STATE_CHANGED = 2; 203 private static final int MSG_VOLUME_RECORD_CHANGED = 3; 204 private static final int MSG_VOLUME_FORGOTTEN = 4; 205 private static final int MSG_DISK_SCANNED = 5; 206 private static final int MSG_DISK_DESTROYED = 6; 207 208 final StorageEventListener mCallback; 209 final Handler mHandler; 210 211 public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) { 212 mCallback = callback; 213 mHandler = new Handler(looper, this); 214 } 215 216 @Override 217 public boolean handleMessage(Message msg) { 218 final SomeArgs args = (SomeArgs) msg.obj; 219 switch (msg.what) { 220 case MSG_STORAGE_STATE_CHANGED: 221 mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2, 222 (String) args.arg3); 223 args.recycle(); 224 return true; 225 case MSG_VOLUME_STATE_CHANGED: 226 mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); 227 args.recycle(); 228 return true; 229 case MSG_VOLUME_RECORD_CHANGED: 230 mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1); 231 args.recycle(); 232 return true; 233 case MSG_VOLUME_FORGOTTEN: 234 mCallback.onVolumeForgotten((String) args.arg1); 235 args.recycle(); 236 return true; 237 case MSG_DISK_SCANNED: 238 mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2); 239 args.recycle(); 240 return true; 241 case MSG_DISK_DESTROYED: 242 mCallback.onDiskDestroyed((DiskInfo) args.arg1); 243 args.recycle(); 244 return true; 245 } 246 args.recycle(); 247 return false; 248 } 249 250 @Override 251 public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException { 252 // Ignored 253 } 254 255 @Override 256 public void onStorageStateChanged(String path, String oldState, String newState) { 257 final SomeArgs args = SomeArgs.obtain(); 258 args.arg1 = path; 259 args.arg2 = oldState; 260 args.arg3 = newState; 261 mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); 262 } 263 264 @Override 265 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 266 final SomeArgs args = SomeArgs.obtain(); 267 args.arg1 = vol; 268 args.argi2 = oldState; 269 args.argi3 = newState; 270 mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); 271 } 272 273 @Override 274 public void onVolumeRecordChanged(VolumeRecord rec) { 275 final SomeArgs args = SomeArgs.obtain(); 276 args.arg1 = rec; 277 mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); 278 } 279 280 @Override 281 public void onVolumeForgotten(String fsUuid) { 282 final SomeArgs args = SomeArgs.obtain(); 283 args.arg1 = fsUuid; 284 mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); 285 } 286 287 @Override 288 public void onDiskScanned(DiskInfo disk, int volumeCount) { 289 final SomeArgs args = SomeArgs.obtain(); 290 args.arg1 = disk; 291 args.argi2 = volumeCount; 292 mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); 293 } 294 295 @Override 296 public void onDiskDestroyed(DiskInfo disk) throws RemoteException { 297 final SomeArgs args = SomeArgs.obtain(); 298 args.arg1 = disk; 299 mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget(); 300 } 301 } 302 303 /** 304 * Binder listener for OBB action results. 305 */ 306 private final ObbActionListener mObbActionListener = new ObbActionListener(); 307 308 private class ObbActionListener extends IObbActionListener.Stub { 309 @SuppressWarnings("hiding") 310 private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>(); 311 312 @Override 313 public void onObbResult(String filename, int nonce, int status) { 314 final ObbListenerDelegate delegate; 315 synchronized (mListeners) { 316 delegate = mListeners.get(nonce); 317 if (delegate != null) { 318 mListeners.remove(nonce); 319 } 320 } 321 322 if (delegate != null) { 323 delegate.sendObbStateChanged(filename, status); 324 } 325 } 326 327 public int addListener(OnObbStateChangeListener listener) { 328 final ObbListenerDelegate delegate = new ObbListenerDelegate(listener); 329 330 synchronized (mListeners) { 331 mListeners.put(delegate.nonce, delegate); 332 } 333 334 return delegate.nonce; 335 } 336 } 337 338 private int getNextNonce() { 339 return mNextNonce.getAndIncrement(); 340 } 341 342 /** 343 * Private class containing sender and receiver code for StorageEvents. 344 */ 345 private class ObbListenerDelegate { 346 private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef; 347 private final Handler mHandler; 348 349 private final int nonce; 350 351 ObbListenerDelegate(OnObbStateChangeListener listener) { 352 nonce = getNextNonce(); 353 mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener); 354 mHandler = new Handler(mLooper) { 355 @Override 356 public void handleMessage(Message msg) { 357 final OnObbStateChangeListener changeListener = getListener(); 358 if (changeListener == null) { 359 return; 360 } 361 362 changeListener.onObbStateChange((String) msg.obj, msg.arg1); 363 } 364 }; 365 } 366 367 OnObbStateChangeListener getListener() { 368 if (mObbEventListenerRef == null) { 369 return null; 370 } 371 return mObbEventListenerRef.get(); 372 } 373 374 void sendObbStateChanged(String path, int state) { 375 mHandler.obtainMessage(0, state, 0, path).sendToTarget(); 376 } 377 } 378 379 /** {@hide} */ 380 @Deprecated 381 public static StorageManager from(Context context) { 382 return context.getSystemService(StorageManager.class); 383 } 384 385 /** 386 * Constructs a StorageManager object through which an application can 387 * can communicate with the systems mount service. 388 * 389 * @param looper The {@link android.os.Looper} which events will be received on. 390 * 391 * <p>Applications can get instance of this class by calling 392 * {@link android.content.Context#getSystemService(java.lang.String)} with an argument 393 * of {@link android.content.Context#STORAGE_SERVICE}. 394 * 395 * @hide 396 */ 397 public StorageManager(Context context, Looper looper) throws ServiceNotFoundException { 398 mContext = context; 399 mResolver = context.getContentResolver(); 400 mLooper = looper; 401 mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount")); 402 } 403 404 /** 405 * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}. 406 * 407 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 408 * 409 * @hide 410 */ 411 public void registerListener(StorageEventListener listener) { 412 synchronized (mDelegates) { 413 final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener, 414 mLooper); 415 try { 416 mStorageManager.registerListener(delegate); 417 } catch (RemoteException e) { 418 throw e.rethrowFromSystemServer(); 419 } 420 mDelegates.add(delegate); 421 } 422 } 423 424 /** 425 * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}. 426 * 427 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 428 * 429 * @hide 430 */ 431 public void unregisterListener(StorageEventListener listener) { 432 synchronized (mDelegates) { 433 for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) { 434 final StorageEventListenerDelegate delegate = i.next(); 435 if (delegate.mCallback == listener) { 436 try { 437 mStorageManager.unregisterListener(delegate); 438 } catch (RemoteException e) { 439 throw e.rethrowFromSystemServer(); 440 } 441 i.remove(); 442 } 443 } 444 } 445 } 446 447 /** 448 * Enables USB Mass Storage (UMS) on the device. 449 * 450 * @hide 451 */ 452 @Deprecated 453 public void enableUsbMassStorage() { 454 } 455 456 /** 457 * Disables USB Mass Storage (UMS) on the device. 458 * 459 * @hide 460 */ 461 @Deprecated 462 public void disableUsbMassStorage() { 463 } 464 465 /** 466 * Query if a USB Mass Storage (UMS) host is connected. 467 * @return true if UMS host is connected. 468 * 469 * @hide 470 */ 471 @Deprecated 472 public boolean isUsbMassStorageConnected() { 473 return false; 474 } 475 476 /** 477 * Query if a USB Mass Storage (UMS) is enabled on the device. 478 * @return true if UMS host is enabled. 479 * 480 * @hide 481 */ 482 @Deprecated 483 public boolean isUsbMassStorageEnabled() { 484 return false; 485 } 486 487 /** 488 * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is 489 * specified, it is supplied to the mounting process to be used in any 490 * encryption used in the OBB. 491 * <p> 492 * The OBB will remain mounted for as long as the StorageManager reference 493 * is held by the application. As soon as this reference is lost, the OBBs 494 * in use will be unmounted. The {@link OnObbStateChangeListener} registered 495 * with this call will receive the success or failure of this operation. 496 * <p> 497 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 498 * file matches a package ID that is owned by the calling program's UID. 499 * That is, shared UID applications can attempt to mount any other 500 * application's OBB that shares its UID. 501 * 502 * @param rawPath the path to the OBB file 503 * @param key secret used to encrypt the OBB; may be <code>null</code> if no 504 * encryption was used on the OBB. 505 * @param listener will receive the success or failure of the operation 506 * @return whether the mount call was successfully queued or not 507 */ 508 public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) { 509 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 510 Preconditions.checkNotNull(listener, "listener cannot be null"); 511 512 try { 513 final String canonicalPath = new File(rawPath).getCanonicalPath(); 514 final int nonce = mObbActionListener.addListener(listener); 515 mStorageManager.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce); 516 return true; 517 } catch (IOException e) { 518 throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e); 519 } catch (RemoteException e) { 520 throw e.rethrowFromSystemServer(); 521 } 522 } 523 524 /** 525 * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the 526 * <code>force</code> flag is true, it will kill any application needed to 527 * unmount the given OBB (even the calling application). 528 * <p> 529 * The {@link OnObbStateChangeListener} registered with this call will 530 * receive the success or failure of this operation. 531 * <p> 532 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 533 * file matches a package ID that is owned by the calling program's UID. 534 * That is, shared UID applications can obtain access to any other 535 * application's OBB that shares its UID. 536 * <p> 537 * 538 * @param rawPath path to the OBB file 539 * @param force whether to kill any programs using this in order to unmount 540 * it 541 * @param listener will receive the success or failure of the operation 542 * @return whether the unmount call was successfully queued or not 543 */ 544 public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) { 545 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 546 Preconditions.checkNotNull(listener, "listener cannot be null"); 547 548 try { 549 final int nonce = mObbActionListener.addListener(listener); 550 mStorageManager.unmountObb(rawPath, force, mObbActionListener, nonce); 551 return true; 552 } catch (RemoteException e) { 553 throw e.rethrowFromSystemServer(); 554 } 555 } 556 557 /** 558 * Check whether an Opaque Binary Blob (OBB) is mounted or not. 559 * 560 * @param rawPath path to OBB image 561 * @return true if OBB is mounted; false if not mounted or on error 562 */ 563 public boolean isObbMounted(String rawPath) { 564 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 565 566 try { 567 return mStorageManager.isObbMounted(rawPath); 568 } catch (RemoteException e) { 569 throw e.rethrowFromSystemServer(); 570 } 571 } 572 573 /** 574 * Check the mounted path of an Opaque Binary Blob (OBB) file. This will 575 * give you the path to where you can obtain access to the internals of the 576 * OBB. 577 * 578 * @param rawPath path to OBB image 579 * @return absolute path to mounted OBB image data or <code>null</code> if 580 * not mounted or exception encountered trying to read status 581 */ 582 public String getMountedObbPath(String rawPath) { 583 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 584 585 try { 586 return mStorageManager.getMountedObbPath(rawPath); 587 } catch (RemoteException e) { 588 throw e.rethrowFromSystemServer(); 589 } 590 } 591 592 /** {@hide} */ 593 public @NonNull List<DiskInfo> getDisks() { 594 try { 595 return Arrays.asList(mStorageManager.getDisks()); 596 } catch (RemoteException e) { 597 throw e.rethrowFromSystemServer(); 598 } 599 } 600 601 /** {@hide} */ 602 public @Nullable DiskInfo findDiskById(String id) { 603 Preconditions.checkNotNull(id); 604 // TODO; go directly to service to make this faster 605 for (DiskInfo disk : getDisks()) { 606 if (Objects.equals(disk.id, id)) { 607 return disk; 608 } 609 } 610 return null; 611 } 612 613 /** {@hide} */ 614 public @Nullable VolumeInfo findVolumeById(String id) { 615 Preconditions.checkNotNull(id); 616 // TODO; go directly to service to make this faster 617 for (VolumeInfo vol : getVolumes()) { 618 if (Objects.equals(vol.id, id)) { 619 return vol; 620 } 621 } 622 return null; 623 } 624 625 /** {@hide} */ 626 public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) { 627 Preconditions.checkNotNull(fsUuid); 628 // TODO; go directly to service to make this faster 629 for (VolumeInfo vol : getVolumes()) { 630 if (Objects.equals(vol.fsUuid, fsUuid)) { 631 return vol; 632 } 633 } 634 return null; 635 } 636 637 /** {@hide} */ 638 public @Nullable VolumeRecord findRecordByUuid(String fsUuid) { 639 Preconditions.checkNotNull(fsUuid); 640 // TODO; go directly to service to make this faster 641 for (VolumeRecord rec : getVolumeRecords()) { 642 if (Objects.equals(rec.fsUuid, fsUuid)) { 643 return rec; 644 } 645 } 646 return null; 647 } 648 649 /** {@hide} */ 650 public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) { 651 if (emulatedVol != null) { 652 return findVolumeById(emulatedVol.getId().replace("emulated", "private")); 653 } else { 654 return null; 655 } 656 } 657 658 /** {@hide} */ 659 public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) { 660 if (privateVol != null) { 661 return findVolumeById(privateVol.getId().replace("private", "emulated")); 662 } else { 663 return null; 664 } 665 } 666 667 /** {@hide} */ 668 public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) { 669 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { 670 return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); 671 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 672 return getPrimaryPhysicalVolume(); 673 } else { 674 return findVolumeByUuid(volumeUuid); 675 } 676 } 677 678 /** {@hide} */ 679 public @Nullable String findUuidForPath(File path) { 680 Preconditions.checkNotNull(path); 681 final String pathString = path.getAbsolutePath(); 682 if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) { 683 return StorageManager.UUID_PRIVATE_INTERNAL; 684 } 685 try { 686 for (VolumeInfo vol : mStorageManager.getVolumes(0)) { 687 if (vol.path != null && FileUtils.contains(vol.path, pathString)) { 688 // TODO: verify that emulated adopted devices have UUID of 689 // underlying volume 690 return vol.fsUuid; 691 } 692 } 693 } catch (RemoteException e) { 694 throw e.rethrowFromSystemServer(); 695 } 696 throw new IllegalStateException("Failed to find a storage device for " + path); 697 } 698 699 /** {@hide} */ 700 public @Nullable File findPathForUuid(String volumeUuid) { 701 final VolumeInfo vol = findVolumeByQualifiedUuid(volumeUuid); 702 if (vol != null) { 703 return vol.getPath(); 704 } 705 throw new IllegalStateException("Failed to find a storage device for " + volumeUuid); 706 } 707 708 /** {@hide} */ 709 public @NonNull List<VolumeInfo> getVolumes() { 710 try { 711 return Arrays.asList(mStorageManager.getVolumes(0)); 712 } catch (RemoteException e) { 713 throw e.rethrowFromSystemServer(); 714 } 715 } 716 717 /** {@hide} */ 718 public @NonNull List<VolumeInfo> getWritablePrivateVolumes() { 719 try { 720 final ArrayList<VolumeInfo> res = new ArrayList<>(); 721 for (VolumeInfo vol : mStorageManager.getVolumes(0)) { 722 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) { 723 res.add(vol); 724 } 725 } 726 return res; 727 } catch (RemoteException e) { 728 throw e.rethrowFromSystemServer(); 729 } 730 } 731 732 /** {@hide} */ 733 public @NonNull List<VolumeRecord> getVolumeRecords() { 734 try { 735 return Arrays.asList(mStorageManager.getVolumeRecords(0)); 736 } catch (RemoteException e) { 737 throw e.rethrowFromSystemServer(); 738 } 739 } 740 741 /** {@hide} */ 742 public @Nullable String getBestVolumeDescription(VolumeInfo vol) { 743 if (vol == null) return null; 744 745 // Nickname always takes precedence when defined 746 if (!TextUtils.isEmpty(vol.fsUuid)) { 747 final VolumeRecord rec = findRecordByUuid(vol.fsUuid); 748 if (rec != null && !TextUtils.isEmpty(rec.nickname)) { 749 return rec.nickname; 750 } 751 } 752 753 if (!TextUtils.isEmpty(vol.getDescription())) { 754 return vol.getDescription(); 755 } 756 757 if (vol.disk != null) { 758 return vol.disk.getDescription(); 759 } 760 761 return null; 762 } 763 764 /** {@hide} */ 765 public @Nullable VolumeInfo getPrimaryPhysicalVolume() { 766 final List<VolumeInfo> vols = getVolumes(); 767 for (VolumeInfo vol : vols) { 768 if (vol.isPrimaryPhysical()) { 769 return vol; 770 } 771 } 772 return null; 773 } 774 775 /** {@hide} */ 776 public void mount(String volId) { 777 try { 778 mStorageManager.mount(volId); 779 } catch (RemoteException e) { 780 throw e.rethrowFromSystemServer(); 781 } 782 } 783 784 /** {@hide} */ 785 public void unmount(String volId) { 786 try { 787 mStorageManager.unmount(volId); 788 } catch (RemoteException e) { 789 throw e.rethrowFromSystemServer(); 790 } 791 } 792 793 /** {@hide} */ 794 public void format(String volId) { 795 try { 796 mStorageManager.format(volId); 797 } catch (RemoteException e) { 798 throw e.rethrowFromSystemServer(); 799 } 800 } 801 802 /** {@hide} */ 803 public long benchmark(String volId) { 804 try { 805 return mStorageManager.benchmark(volId); 806 } catch (RemoteException e) { 807 throw e.rethrowFromSystemServer(); 808 } 809 } 810 811 /** {@hide} */ 812 public void partitionPublic(String diskId) { 813 try { 814 mStorageManager.partitionPublic(diskId); 815 } catch (RemoteException e) { 816 throw e.rethrowFromSystemServer(); 817 } 818 } 819 820 /** {@hide} */ 821 public void partitionPrivate(String diskId) { 822 try { 823 mStorageManager.partitionPrivate(diskId); 824 } catch (RemoteException e) { 825 throw e.rethrowFromSystemServer(); 826 } 827 } 828 829 /** {@hide} */ 830 public void partitionMixed(String diskId, int ratio) { 831 try { 832 mStorageManager.partitionMixed(diskId, ratio); 833 } catch (RemoteException e) { 834 throw e.rethrowFromSystemServer(); 835 } 836 } 837 838 /** {@hide} */ 839 public void wipeAdoptableDisks() { 840 // We only wipe devices in "adoptable" locations, which are in a 841 // long-term stable slot/location on the device, where apps have a 842 // reasonable chance of storing sensitive data. (Apps need to go through 843 // SAF to write to transient volumes.) 844 final List<DiskInfo> disks = getDisks(); 845 for (DiskInfo disk : disks) { 846 final String diskId = disk.getId(); 847 if (disk.isAdoptable()) { 848 Slog.d(TAG, "Found adoptable " + diskId + "; wiping"); 849 try { 850 // TODO: switch to explicit wipe command when we have it, 851 // for now rely on the fact that vfat format does a wipe 852 mStorageManager.partitionPublic(diskId); 853 } catch (Exception e) { 854 Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e); 855 } 856 } else { 857 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId()); 858 } 859 } 860 } 861 862 /** {@hide} */ 863 public void setVolumeNickname(String fsUuid, String nickname) { 864 try { 865 mStorageManager.setVolumeNickname(fsUuid, nickname); 866 } catch (RemoteException e) { 867 throw e.rethrowFromSystemServer(); 868 } 869 } 870 871 /** {@hide} */ 872 public void setVolumeInited(String fsUuid, boolean inited) { 873 try { 874 mStorageManager.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0, 875 VolumeRecord.USER_FLAG_INITED); 876 } catch (RemoteException e) { 877 throw e.rethrowFromSystemServer(); 878 } 879 } 880 881 /** {@hide} */ 882 public void setVolumeSnoozed(String fsUuid, boolean snoozed) { 883 try { 884 mStorageManager.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0, 885 VolumeRecord.USER_FLAG_SNOOZED); 886 } catch (RemoteException e) { 887 throw e.rethrowFromSystemServer(); 888 } 889 } 890 891 /** {@hide} */ 892 public void forgetVolume(String fsUuid) { 893 try { 894 mStorageManager.forgetVolume(fsUuid); 895 } catch (RemoteException e) { 896 throw e.rethrowFromSystemServer(); 897 } 898 } 899 900 /** 901 * This is not the API you're looking for. 902 * 903 * @see PackageManager#getPrimaryStorageCurrentVolume() 904 * @hide 905 */ 906 public String getPrimaryStorageUuid() { 907 try { 908 return mStorageManager.getPrimaryStorageUuid(); 909 } catch (RemoteException e) { 910 throw e.rethrowFromSystemServer(); 911 } 912 } 913 914 /** 915 * This is not the API you're looking for. 916 * 917 * @see PackageManager#movePrimaryStorage(VolumeInfo) 918 * @hide 919 */ 920 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { 921 try { 922 mStorageManager.setPrimaryStorageUuid(volumeUuid, callback); 923 } catch (RemoteException e) { 924 throw e.rethrowFromSystemServer(); 925 } 926 } 927 928 /** 929 * Return the {@link StorageVolume} that contains the given file, or {@code null} if none. 930 */ 931 public @Nullable StorageVolume getStorageVolume(File file) { 932 return getStorageVolume(getVolumeList(), file); 933 } 934 935 /** {@hide} */ 936 public static @Nullable StorageVolume getStorageVolume(File file, int userId) { 937 return getStorageVolume(getVolumeList(userId, 0), file); 938 } 939 940 /** {@hide} */ 941 private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) { 942 if (file == null) { 943 return null; 944 } 945 try { 946 file = file.getCanonicalFile(); 947 } catch (IOException ignored) { 948 Slog.d(TAG, "Could not get canonical path for " + file); 949 return null; 950 } 951 for (StorageVolume volume : volumes) { 952 File volumeFile = volume.getPathFile(); 953 try { 954 volumeFile = volumeFile.getCanonicalFile(); 955 } catch (IOException ignored) { 956 continue; 957 } 958 if (FileUtils.contains(volumeFile, file)) { 959 return volume; 960 } 961 } 962 return null; 963 } 964 965 /** 966 * Gets the state of a volume via its mountpoint. 967 * @hide 968 */ 969 @Deprecated 970 public @NonNull String getVolumeState(String mountPoint) { 971 final StorageVolume vol = getStorageVolume(new File(mountPoint)); 972 if (vol != null) { 973 return vol.getState(); 974 } else { 975 return Environment.MEDIA_UNKNOWN; 976 } 977 } 978 979 /** 980 * Return the list of shared/external storage volumes available to the 981 * current user. This includes both the primary shared storage device and 982 * any attached external volumes including SD cards and USB drives. 983 * 984 * @see Environment#getExternalStorageDirectory() 985 * @see StorageVolume#createAccessIntent(String) 986 */ 987 public @NonNull List<StorageVolume> getStorageVolumes() { 988 final ArrayList<StorageVolume> res = new ArrayList<>(); 989 Collections.addAll(res, 990 getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)); 991 return res; 992 } 993 994 /** 995 * Return the primary shared/external storage volume available to the 996 * current user. This volume is the same storage device returned by 997 * {@link Environment#getExternalStorageDirectory()} and 998 * {@link Context#getExternalFilesDir(String)}. 999 */ 1000 public @NonNull StorageVolume getPrimaryStorageVolume() { 1001 return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0]; 1002 } 1003 1004 /** {@hide} */ 1005 public static Pair<String, Long> getPrimaryStoragePathAndSize() { 1006 for (String path : INTERNAL_STORAGE_SIZE_PATHS) { 1007 final long numberBlocks = readLong(path); 1008 if (numberBlocks > 0) { 1009 return new Pair<>(path, Long.valueOf(numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE)); 1010 } 1011 } 1012 return null; 1013 } 1014 1015 1016 /** {@hide} */ 1017 public long getPrimaryStorageSize() { 1018 final Pair<String, Long> pair = getPrimaryStoragePathAndSize(); 1019 return pair == null ? 0 : pair.second.longValue(); 1020 } 1021 1022 private static long readLong(String path) { 1023 try (final FileInputStream fis = new FileInputStream(path); 1024 final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) { 1025 return Long.parseLong(reader.readLine()); 1026 } catch (FileNotFoundException e) { 1027 // This is expected since we are trying to parse multiple paths. 1028 Slog.i(TAG, "readLong(): Path doesn't exist: " + path + ": " + e); 1029 return 0; 1030 } catch (NumberFormatException e) { 1031 Slog.e(TAG, "readLong(): Could not parse " + path + ": " + e); 1032 return 0; 1033 } catch (Exception e) { 1034 Slog.e(TAG, "readLong(): Unknown exception while opening " + path + ": " + e); 1035 return 0; 1036 } 1037 } 1038 1039 /** @removed */ 1040 public @NonNull StorageVolume[] getVolumeList() { 1041 return getVolumeList(mContext.getUserId(), 0); 1042 } 1043 1044 /** {@hide} */ 1045 public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) { 1046 final IStorageManager storageManager = IStorageManager.Stub.asInterface( 1047 ServiceManager.getService("mount")); 1048 try { 1049 String packageName = ActivityThread.currentOpPackageName(); 1050 if (packageName == null) { 1051 // Package name can be null if the activity thread is running but the app 1052 // hasn't bound yet. In this case we fall back to the first package in the 1053 // current UID. This works for runtime permissions as permission state is 1054 // per UID and permission realted app ops are updated for all UID packages. 1055 String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid( 1056 android.os.Process.myUid()); 1057 if (packageNames == null || packageNames.length <= 0) { 1058 return new StorageVolume[0]; 1059 } 1060 packageName = packageNames[0]; 1061 } 1062 final int uid = ActivityThread.getPackageManager().getPackageUid(packageName, 1063 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId); 1064 if (uid <= 0) { 1065 return new StorageVolume[0]; 1066 } 1067 return storageManager.getVolumeList(uid, packageName, flags); 1068 } catch (RemoteException e) { 1069 throw e.rethrowFromSystemServer(); 1070 } 1071 } 1072 1073 /** 1074 * Returns list of paths for all mountable volumes. 1075 * @hide 1076 */ 1077 @Deprecated 1078 public @NonNull String[] getVolumePaths() { 1079 StorageVolume[] volumes = getVolumeList(); 1080 int count = volumes.length; 1081 String[] paths = new String[count]; 1082 for (int i = 0; i < count; i++) { 1083 paths[i] = volumes[i].getPath(); 1084 } 1085 return paths; 1086 } 1087 1088 /** @removed */ 1089 public @NonNull StorageVolume getPrimaryVolume() { 1090 return getPrimaryVolume(getVolumeList()); 1091 } 1092 1093 /** {@hide} */ 1094 public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) { 1095 for (StorageVolume volume : volumes) { 1096 if (volume.isPrimary()) { 1097 return volume; 1098 } 1099 } 1100 throw new IllegalStateException("Missing primary storage"); 1101 } 1102 1103 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5; 1104 private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES; 1105 1106 private static final int DEFAULT_CACHE_PERCENTAGE = 10; 1107 private static final long DEFAULT_CACHE_MAX_BYTES = 5 * GB_IN_BYTES; 1108 1109 private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES; 1110 1111 /** 1112 * Return the number of available bytes until the given path is considered 1113 * running low on storage. 1114 * 1115 * @hide 1116 */ 1117 public long getStorageBytesUntilLow(File path) { 1118 return path.getUsableSpace() - getStorageFullBytes(path); 1119 } 1120 1121 /** 1122 * Return the number of available bytes at which the given path is 1123 * considered running low on storage. 1124 * 1125 * @hide 1126 */ 1127 public long getStorageLowBytes(File path) { 1128 final long lowPercent = Settings.Global.getInt(mResolver, 1129 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); 1130 final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; 1131 1132 final long maxLowBytes = Settings.Global.getLong(mResolver, 1133 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); 1134 1135 return Math.min(lowBytes, maxLowBytes); 1136 } 1137 1138 /** 1139 * Return the minimum number of bytes of storage on the device that should 1140 * be reserved for cached data. 1141 * 1142 * @hide 1143 */ 1144 public long getStorageCacheBytes(File path) { 1145 final long cachePercent = Settings.Global.getInt(mResolver, 1146 Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE); 1147 final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100; 1148 1149 final long maxCacheBytes = Settings.Global.getLong(mResolver, 1150 Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES); 1151 1152 return Math.min(cacheBytes, maxCacheBytes); 1153 } 1154 1155 /** 1156 * Return the number of available bytes at which the given path is 1157 * considered full. 1158 * 1159 * @hide 1160 */ 1161 public long getStorageFullBytes(File path) { 1162 return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, 1163 DEFAULT_FULL_THRESHOLD_BYTES); 1164 } 1165 1166 /** {@hide} */ 1167 public void createUserKey(int userId, int serialNumber, boolean ephemeral) { 1168 try { 1169 mStorageManager.createUserKey(userId, serialNumber, ephemeral); 1170 } catch (RemoteException e) { 1171 throw e.rethrowFromSystemServer(); 1172 } 1173 } 1174 1175 /** {@hide} */ 1176 public void destroyUserKey(int userId) { 1177 try { 1178 mStorageManager.destroyUserKey(userId); 1179 } catch (RemoteException e) { 1180 throw e.rethrowFromSystemServer(); 1181 } 1182 } 1183 1184 /** {@hide} */ 1185 public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) { 1186 try { 1187 mStorageManager.unlockUserKey(userId, serialNumber, token, secret); 1188 } catch (RemoteException e) { 1189 throw e.rethrowFromSystemServer(); 1190 } 1191 } 1192 1193 /** {@hide} */ 1194 public void lockUserKey(int userId) { 1195 try { 1196 mStorageManager.lockUserKey(userId); 1197 } catch (RemoteException e) { 1198 throw e.rethrowFromSystemServer(); 1199 } 1200 } 1201 1202 /** {@hide} */ 1203 public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { 1204 try { 1205 mStorageManager.prepareUserStorage(volumeUuid, userId, serialNumber, flags); 1206 } catch (RemoteException e) { 1207 throw e.rethrowFromSystemServer(); 1208 } 1209 } 1210 1211 /** {@hide} */ 1212 public void destroyUserStorage(String volumeUuid, int userId, int flags) { 1213 try { 1214 mStorageManager.destroyUserStorage(volumeUuid, userId, flags); 1215 } catch (RemoteException e) { 1216 throw e.rethrowFromSystemServer(); 1217 } 1218 } 1219 1220 /** {@hide} */ 1221 public static boolean isUserKeyUnlocked(int userId) { 1222 if (sStorageManager == null) { 1223 sStorageManager = IStorageManager.Stub 1224 .asInterface(ServiceManager.getService("mount")); 1225 } 1226 if (sStorageManager == null) { 1227 Slog.w(TAG, "Early during boot, assuming locked"); 1228 return false; 1229 } 1230 final long token = Binder.clearCallingIdentity(); 1231 try { 1232 return sStorageManager.isUserKeyUnlocked(userId); 1233 } catch (RemoteException e) { 1234 throw e.rethrowAsRuntimeException(); 1235 } finally { 1236 Binder.restoreCallingIdentity(token); 1237 } 1238 } 1239 1240 /** 1241 * Return if data stored at or under the given path will be encrypted while 1242 * at rest. This can help apps avoid the overhead of double-encrypting data. 1243 */ 1244 public boolean isEncrypted(File file) { 1245 if (FileUtils.contains(Environment.getDataDirectory(), file)) { 1246 return isEncrypted(); 1247 } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) { 1248 return true; 1249 } 1250 // TODO: extend to support shared storage 1251 return false; 1252 } 1253 1254 /** {@hide} 1255 * Is this device encryptable or already encrypted? 1256 * @return true for encryptable or encrypted 1257 * false not encrypted and not encryptable 1258 */ 1259 public static boolean isEncryptable() { 1260 return RoSystemProperties.CRYPTO_ENCRYPTABLE; 1261 } 1262 1263 /** {@hide} 1264 * Is this device already encrypted? 1265 * @return true for encrypted. (Implies isEncryptable() == true) 1266 * false not encrypted 1267 */ 1268 public static boolean isEncrypted() { 1269 return RoSystemProperties.CRYPTO_ENCRYPTED; 1270 } 1271 1272 /** {@hide} 1273 * Is this device file encrypted? 1274 * @return true for file encrypted. (Implies isEncrypted() == true) 1275 * false not encrypted or block encrypted 1276 */ 1277 public static boolean isFileEncryptedNativeOnly() { 1278 if (!isEncrypted()) { 1279 return false; 1280 } 1281 return RoSystemProperties.CRYPTO_FILE_ENCRYPTED; 1282 } 1283 1284 /** {@hide} 1285 * Is this device block encrypted? 1286 * @return true for block encrypted. (Implies isEncrypted() == true) 1287 * false not encrypted or file encrypted 1288 */ 1289 public static boolean isBlockEncrypted() { 1290 if (!isEncrypted()) { 1291 return false; 1292 } 1293 return RoSystemProperties.CRYPTO_BLOCK_ENCRYPTED; 1294 } 1295 1296 /** {@hide} 1297 * Is this device block encrypted with credentials? 1298 * @return true for crediential block encrypted. 1299 * (Implies isBlockEncrypted() == true) 1300 * false not encrypted, file encrypted or default block encrypted 1301 */ 1302 public static boolean isNonDefaultBlockEncrypted() { 1303 if (!isBlockEncrypted()) { 1304 return false; 1305 } 1306 1307 try { 1308 IStorageManager storageManager = IStorageManager.Stub.asInterface( 1309 ServiceManager.getService("mount")); 1310 return storageManager.getPasswordType() != CRYPT_TYPE_DEFAULT; 1311 } catch (RemoteException e) { 1312 Log.e(TAG, "Error getting encryption type"); 1313 return false; 1314 } 1315 } 1316 1317 /** {@hide} 1318 * Is this device in the process of being block encrypted? 1319 * @return true for encrypting. 1320 * false otherwise 1321 * Whether device isEncrypted at this point is undefined 1322 * Note that only system services and CryptKeeper will ever see this return 1323 * true - no app will ever be launched in this state. 1324 * Also note that this state will not change without a teardown of the 1325 * framework, so no service needs to check for changes during their lifespan 1326 */ 1327 public static boolean isBlockEncrypting() { 1328 final String state = SystemProperties.get("vold.encrypt_progress", ""); 1329 return !"".equalsIgnoreCase(state); 1330 } 1331 1332 /** {@hide} 1333 * Is this device non default block encrypted and in the process of 1334 * prompting for credentials? 1335 * @return true for prompting for credentials. 1336 * (Implies isNonDefaultBlockEncrypted() == true) 1337 * false otherwise 1338 * Note that only system services and CryptKeeper will ever see this return 1339 * true - no app will ever be launched in this state. 1340 * Also note that this state will not change without a teardown of the 1341 * framework, so no service needs to check for changes during their lifespan 1342 */ 1343 public static boolean inCryptKeeperBounce() { 1344 final String status = SystemProperties.get("vold.decrypt"); 1345 return "trigger_restart_min_framework".equals(status); 1346 } 1347 1348 /** {@hide} */ 1349 public static boolean isFileEncryptedEmulatedOnly() { 1350 return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false); 1351 } 1352 1353 /** {@hide} 1354 * Is this device running in a file encrypted mode, either native or emulated? 1355 * @return true for file encrypted, false otherwise 1356 */ 1357 public static boolean isFileEncryptedNativeOrEmulated() { 1358 return isFileEncryptedNativeOnly() 1359 || isFileEncryptedEmulatedOnly(); 1360 } 1361 1362 /** {@hide} */ 1363 public static File maybeTranslateEmulatedPathToInternal(File path) { 1364 final IStorageManager storageManager = IStorageManager.Stub.asInterface( 1365 ServiceManager.getService("mount")); 1366 try { 1367 final VolumeInfo[] vols = storageManager.getVolumes(0); 1368 for (VolumeInfo vol : vols) { 1369 if ((vol.getType() == VolumeInfo.TYPE_EMULATED 1370 || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) { 1371 final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(), 1372 vol.getInternalPath(), path); 1373 if (internalPath != null && internalPath.exists()) { 1374 return internalPath; 1375 } 1376 } 1377 } 1378 } catch (RemoteException e) { 1379 throw e.rethrowFromSystemServer(); 1380 } 1381 return path; 1382 } 1383 1384 /** {@hide} */ 1385 @VisibleForTesting 1386 public @NonNull ParcelFileDescriptor openProxyFileDescriptor( 1387 int mode, ProxyFileDescriptorCallback callback, ThreadFactory factory) 1388 throws IOException { 1389 // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before 1390 // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount 1391 // the bridge by calling mountProxyFileDescriptorBridge. 1392 int retry = 3; 1393 while (retry-- > 0) { 1394 try { 1395 synchronized (mFuseAppLoopLock) { 1396 if (mFuseAppLoop == null) { 1397 final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge(); 1398 if (mount == null) { 1399 Log.e(TAG, "Failed to open proxy file bridge."); 1400 throw new IOException("Failed to open proxy file bridge."); 1401 } 1402 mFuseAppLoop = FuseAppLoop.open(mount.mountPointId, mount.fd, factory); 1403 } 1404 1405 try { 1406 final int fileId = mFuseAppLoop.registerCallback(callback); 1407 final ParcelFileDescriptor pfd = 1408 mStorageManager.openProxyFileDescriptor( 1409 mFuseAppLoop.getMountPointId(), fileId, mode); 1410 if (pfd != null) { 1411 return pfd; 1412 } 1413 // Probably the bridge is being unmounted but mFuseAppLoop has not been 1414 // noticed it yet. 1415 mFuseAppLoop.unregisterCallback(fileId); 1416 } catch (FuseAppLoop.UnmountedException error) { 1417 Log.d(TAG, "mFuseAppLoop has been already unmounted."); 1418 mFuseAppLoop = null; 1419 continue; 1420 } 1421 } 1422 try { 1423 Thread.sleep(100); 1424 } catch (InterruptedException e) { 1425 break; 1426 } 1427 } catch (RemoteException e) { 1428 e.rethrowFromSystemServer(); 1429 } 1430 } 1431 1432 throw new IOException("Failed to mount bridge."); 1433 } 1434 1435 /** 1436 * Opens seekable ParcelFileDescriptor that routes file operation requests to 1437 * ProxyFileDescriptorCallback. 1438 * 1439 * @param mode The desired access mode, must be one of 1440 * {@link ParcelFileDescriptor#MODE_READ_ONLY}, 1441 * {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or 1442 * {@link ParcelFileDescriptor#MODE_READ_WRITE} 1443 * @param callback Callback to process file operation requests issued on returned file 1444 * descriptor. The callback is invoked on a thread managed by the framework. 1445 * @return Seekable ParcelFileDescriptor. 1446 * @throws IOException 1447 */ 1448 public @NonNull ParcelFileDescriptor openProxyFileDescriptor( 1449 int mode, ProxyFileDescriptorCallback callback) 1450 throws IOException { 1451 return openProxyFileDescriptor(mode, callback, null); 1452 } 1453 1454 /** {@hide} */ 1455 @VisibleForTesting 1456 public int getProxyFileDescriptorMountPointId() { 1457 synchronized (mFuseAppLoopLock) { 1458 return mFuseAppLoop != null ? mFuseAppLoop.getMountPointId() : -1; 1459 } 1460 } 1461 1462 /** 1463 * Return quota size in bytes for all cached data belonging to the calling 1464 * app on the filesystem that hosts the given path. 1465 * <p> 1466 * If your app goes above this quota, your cached files will be some of the 1467 * first to be deleted when additional disk space is needed. Conversely, if 1468 * your app stays under this quota, your cached files will be some of the 1469 * last to be deleted when additional disk space is needed. 1470 * <p> 1471 * This quota will change over time depending on how frequently the user 1472 * interacts with your app, and depending on how much disk space is used. 1473 * <p class="note"> 1474 * Note: if your app uses the {@code android:sharedUserId} manifest feature, 1475 * then cached data for all packages in your shared UID is tracked together 1476 * as a single unit. 1477 * </p> 1478 * 1479 * @see #getCacheSizeBytes(File) 1480 */ 1481 public long getCacheQuotaBytes(File path) { 1482 try { 1483 final String volumeUuid = findUuidForPath(path); 1484 final ApplicationInfo app = mContext.getApplicationInfo(); 1485 return mStorageManager.getCacheQuotaBytes(volumeUuid, app.uid); 1486 } catch (RemoteException e) { 1487 throw e.rethrowFromSystemServer(); 1488 } 1489 } 1490 1491 /** 1492 * Return total size in bytes of all cached data belonging to the calling 1493 * app on the filesystem that hosts the given path. 1494 * <p> 1495 * Cached data tracked by this method always includes 1496 * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and 1497 * it also includes {@link Context#getExternalCacheDir()} if the primary 1498 * shared/external storage is hosted on the same storage device as your 1499 * private data. 1500 * <p class="note"> 1501 * Note: if your app uses the {@code android:sharedUserId} manifest feature, 1502 * then cached data for all packages in your shared UID is tracked together 1503 * as a single unit. 1504 * </p> 1505 * 1506 * @see #getCacheQuotaBytes() 1507 */ 1508 public long getCacheSizeBytes(File path) { 1509 try { 1510 final String volumeUuid = findUuidForPath(path); 1511 final ApplicationInfo app = mContext.getApplicationInfo(); 1512 return mStorageManager.getCacheSizeBytes(volumeUuid, app.uid); 1513 } catch (RemoteException e) { 1514 throw e.rethrowFromSystemServer(); 1515 } 1516 } 1517 1518 /** @removed */ 1519 @Deprecated 1520 public long getCacheQuotaBytes() { 1521 return getCacheQuotaBytes(mContext.getCacheDir()); 1522 } 1523 1524 /** @removed */ 1525 @Deprecated 1526 public long getCacheSizeBytes() { 1527 return getCacheSizeBytes(mContext.getCacheDir()); 1528 } 1529 1530 /** @removed */ 1531 @Deprecated 1532 public long getExternalCacheQuotaBytes() { 1533 return getCacheQuotaBytes(mContext.getExternalCacheDir()); 1534 } 1535 1536 /** @removed */ 1537 @Deprecated 1538 public long getExternalCacheSizeBytes() { 1539 return getCacheSizeBytes(mContext.getExternalCacheDir()); 1540 } 1541 1542 /** 1543 * Flag indicating that a disk space allocation request should operate in an 1544 * aggressive mode. This flag should only be rarely used in situations that 1545 * are critical to system health or security. 1546 * <p> 1547 * When set, the system is more aggressive about the data that it considers 1548 * for possible deletion when allocating disk space. 1549 * <p class="note"> 1550 * Note: your app must hold the 1551 * {@link android.Manifest.permission#ALLOCATE_AGGRESSIVE} permission for 1552 * this flag to take effect. 1553 * </p> 1554 * 1555 * @see #getAllocatableBytes(File, int) 1556 * @see #allocateBytes(File, long, int) 1557 * @see #allocateBytes(FileDescriptor, long, int) 1558 */ 1559 @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) 1560 public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; 1561 1562 /** @hide */ 1563 @IntDef(flag = true, value = { 1564 FLAG_ALLOCATE_AGGRESSIVE, 1565 }) 1566 @Retention(RetentionPolicy.SOURCE) 1567 public @interface AllocateFlags {} 1568 1569 /** 1570 * Return the maximum number of new bytes that your app can allocate for 1571 * itself using {@link #allocateBytes(File, long, int)} at the given path. 1572 * This value is typically larger than {@link File#getUsableSpace()}, since 1573 * the system may be willing to delete cached files to satisfy an allocation 1574 * request. 1575 * <p> 1576 * This method is best used as a pre-flight check, such as deciding if there 1577 * is enough space to store an entire music album before you allocate space 1578 * for each audio file in the album. Attempts to allocate disk space beyond 1579 * the returned value will fail. 1580 * <p class="note"> 1581 * Note: if your app uses the {@code android:sharedUserId} manifest feature, 1582 * then allocatable space for all packages in your shared UID is tracked 1583 * together as a single unit. 1584 * </p> 1585 * 1586 * @param path the path where you're considering allocating disk space, 1587 * since allocatable space can vary widely depending on the 1588 * underlying storage device. 1589 * @param flags to apply to the request. 1590 * @return the maximum number of new bytes that the calling app can allocate 1591 * using {@link #allocateBytes(File, long, int)}. 1592 */ 1593 public long getAllocatableBytes(File path, @AllocateFlags int flags) throws IOException { 1594 try { 1595 final String volumeUuid = findUuidForPath(path); 1596 return mStorageManager.getAllocatableBytes(volumeUuid, flags); 1597 } catch (ParcelableException e) { 1598 e.maybeRethrow(IOException.class); 1599 throw new RuntimeException(e); 1600 } catch (RemoteException e) { 1601 throw e.rethrowFromSystemServer(); 1602 } 1603 } 1604 1605 /** 1606 * Allocate the requested number of bytes for your application to use at the 1607 * given path. This will cause the system to delete any cached files 1608 * necessary to satisfy your request. 1609 * <p> 1610 * Attempts to allocate disk space beyond the value returned by 1611 * {@link #getAllocatableBytes(File, int)} will fail. 1612 * <p> 1613 * Since multiple apps can be running simultaneously, this method may be 1614 * subject to race conditions. If possible, consider using 1615 * {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee 1616 * that bytes are allocated to an opened file. 1617 * 1618 * @param path the path where you'd like to allocate disk space. 1619 * @param bytes the number of bytes to allocate. 1620 * @param flags to apply to the request. 1621 * @see #getAllocatableBytes(File, int) 1622 */ 1623 public void allocateBytes(File path, long bytes, @AllocateFlags int flags) throws IOException { 1624 try { 1625 final String volumeUuid = findUuidForPath(path); 1626 mStorageManager.allocateBytes(volumeUuid, bytes, flags); 1627 } catch (ParcelableException e) { 1628 e.maybeRethrow(IOException.class); 1629 } catch (RemoteException e) { 1630 throw e.rethrowFromSystemServer(); 1631 } 1632 } 1633 1634 /** 1635 * Allocate the requested number of bytes for your application to use in the 1636 * given open file. This will cause the system to delete any cached files 1637 * necessary to satisfy your request. 1638 * <p> 1639 * Attempts to allocate disk space beyond the value returned by 1640 * {@link #getAllocatableBytes(File, int)} will fail. 1641 * <p> 1642 * This method guarantees that bytes have been allocated to the opened file, 1643 * otherwise it will throw if fast allocation is not possible. Fast 1644 * allocation is typically only supported in private app data directories, 1645 * and on shared/external storage devices which are emulated. 1646 * 1647 * @param fd the open file that you'd like to allocate disk space for. 1648 * @param bytes the number of bytes to allocate. This is the desired final 1649 * size of the open file. 1650 * @param flags to apply to the request. 1651 * @see #getAllocatableBytes(File, int) 1652 * @see Environment#isExternalStorageEmulated(File) 1653 */ 1654 public void allocateBytes(FileDescriptor fd, long bytes, @AllocateFlags int flags) 1655 throws IOException { 1656 final File file = ParcelFileDescriptor.getFile(fd); 1657 for (int i = 0; i < 3; i++) { 1658 try { 1659 final long haveBytes = Os.fstat(fd).st_blocks * 512; 1660 final long needBytes = bytes - haveBytes; 1661 1662 if (needBytes > 0) { 1663 allocateBytes(file, needBytes, flags); 1664 } 1665 1666 Os.posix_fallocate(fd, 0, bytes); 1667 return; 1668 } catch (ErrnoException e) { 1669 if (e.errno == OsConstants.ENOSPC) { 1670 Log.w(TAG, "Odd, not enough space; let's try again?"); 1671 continue; 1672 } 1673 throw e.rethrowAsIOException(); 1674 } 1675 } 1676 throw new IOException( 1677 "Well this is embarassing; we can't allocate " + bytes + " for " + file); 1678 } 1679 1680 private static final String XATTR_ATOMIC = "user.atomic"; 1681 private static final String XATTR_TOMBSTONE = "user.tombstone"; 1682 1683 /** {@hide} */ 1684 private static void setCacheBehavior(File path, String name, boolean enabled) 1685 throws IOException { 1686 if (!path.isDirectory()) { 1687 throw new IOException("Cache behavior can only be set on directories"); 1688 } 1689 if (enabled) { 1690 try { 1691 Os.setxattr(path.getAbsolutePath(), name, 1692 "1".getBytes(StandardCharsets.UTF_8), 0); 1693 } catch (ErrnoException e) { 1694 throw e.rethrowAsIOException(); 1695 } 1696 } else { 1697 try { 1698 Os.removexattr(path.getAbsolutePath(), name); 1699 } catch (ErrnoException e) { 1700 if (e.errno != OsConstants.ENODATA) { 1701 throw e.rethrowAsIOException(); 1702 } 1703 } 1704 } 1705 } 1706 1707 /** {@hide} */ 1708 private static boolean isCacheBehavior(File path, String name) throws IOException { 1709 try { 1710 Os.getxattr(path.getAbsolutePath(), name); 1711 return true; 1712 } catch (ErrnoException e) { 1713 if (e.errno != OsConstants.ENODATA) { 1714 throw e.rethrowAsIOException(); 1715 } else { 1716 return false; 1717 } 1718 } 1719 } 1720 1721 /** 1722 * Enable or disable special cache behavior that treats this directory and 1723 * its contents as an atomic unit. 1724 * <p> 1725 * When enabled and this directory is considered for automatic deletion by 1726 * the OS, all contained files will either be deleted together, or not at 1727 * all. This is useful when you have a directory that contains several 1728 * related metadata files that depend on each other, such as movie file and 1729 * a subtitle file. 1730 * <p> 1731 * When enabled, the <em>newest</em> {@link File#lastModified()} value of 1732 * any contained files is considered the modified time of the entire 1733 * directory. 1734 * <p> 1735 * This behavior can only be set on a directory, and it applies recursively 1736 * to all contained files and directories. 1737 */ 1738 public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException { 1739 setCacheBehavior(path, XATTR_ATOMIC, atomic); 1740 } 1741 1742 /** 1743 * Read the current value set by 1744 * {@link #setCacheBehaviorAtomic(File, boolean)}. 1745 */ 1746 public boolean isCacheBehaviorAtomic(File path) throws IOException { 1747 return isCacheBehavior(path, XATTR_ATOMIC); 1748 } 1749 1750 /** 1751 * Enable or disable special cache behavior that leaves deleted cache files 1752 * intact as tombstones. 1753 * <p> 1754 * When enabled and a file contained in this directory is automatically 1755 * deleted by the OS, the file will be truncated to have a length of 0 bytes 1756 * instead of being fully deleted. This is useful if you need to distinguish 1757 * between a file that was deleted versus one that never existed. 1758 * <p> 1759 * This behavior can only be set on a directory, and it applies recursively 1760 * to all contained files and directories. 1761 * <p class="note"> 1762 * Note: this behavior is ignored completely if the user explicitly requests 1763 * that all cached data be cleared. 1764 * </p> 1765 */ 1766 public void setCacheBehaviorTombstone(File path, boolean tombstone) throws IOException { 1767 setCacheBehavior(path, XATTR_TOMBSTONE, tombstone); 1768 } 1769 1770 /** 1771 * Read the current value set by 1772 * {@link #setCacheBehaviorTombstone(File, boolean)}. 1773 */ 1774 public boolean isCacheBehaviorTombstone(File path) throws IOException { 1775 return isCacheBehavior(path, XATTR_TOMBSTONE); 1776 } 1777 1778 private final Object mFuseAppLoopLock = new Object(); 1779 1780 @GuardedBy("mFuseAppLoopLock") 1781 private @Nullable FuseAppLoop mFuseAppLoop = null; 1782 1783 /// Consts to match the password types in cryptfs.h 1784 /** @hide */ 1785 public static final int CRYPT_TYPE_PASSWORD = 0; 1786 /** @hide */ 1787 public static final int CRYPT_TYPE_DEFAULT = 1; 1788 /** @hide */ 1789 public static final int CRYPT_TYPE_PATTERN = 2; 1790 /** @hide */ 1791 public static final int CRYPT_TYPE_PIN = 3; 1792 1793 // Constants for the data available via StorageManagerService.getField. 1794 /** @hide */ 1795 public static final String SYSTEM_LOCALE_KEY = "SystemLocale"; 1796 /** @hide */ 1797 public static final String OWNER_INFO_KEY = "OwnerInfo"; 1798 /** @hide */ 1799 public static final String PATTERN_VISIBLE_KEY = "PatternVisible"; 1800 /** @hide */ 1801 public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible"; 1802} 1803