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