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