1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.os.storage; 18 19 import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE; 20 import static android.Manifest.permission.READ_EXTERNAL_STORAGE; 21 import static android.app.AppOpsManager.OP_LEGACY_STORAGE; 22 import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE; 23 import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE; 24 import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES; 25 import static android.app.PropertyInvalidatedCache.MODULE_SYSTEM; 26 import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX; 27 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 28 import static android.os.UserHandle.PER_USER_RANGE; 29 30 import android.annotation.BytesLong; 31 import android.annotation.CallbackExecutor; 32 import android.annotation.FlaggedApi; 33 import android.annotation.IntDef; 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.annotation.RequiresPermission; 37 import android.annotation.SdkConstant; 38 import android.annotation.SuppressLint; 39 import android.annotation.SystemApi; 40 import android.annotation.SystemService; 41 import android.annotation.TestApi; 42 import android.annotation.WorkerThread; 43 import android.app.Activity; 44 import android.app.ActivityThread; 45 import android.app.AppGlobals; 46 import android.app.AppOpsManager; 47 import android.app.PendingIntent; 48 import android.app.PropertyInvalidatedCache; 49 import android.compat.annotation.UnsupportedAppUsage; 50 import android.content.ContentResolver; 51 import android.content.Context; 52 import android.content.Intent; 53 import android.content.pm.ApplicationInfo; 54 import android.content.pm.IPackageMoveObserver; 55 import android.content.pm.PackageManager; 56 import android.content.res.ObbInfo; 57 import android.content.res.ObbScanner; 58 import android.database.Cursor; 59 import android.net.Uri; 60 import android.os.Binder; 61 import android.os.Build; 62 import android.os.Environment; 63 import android.os.FileUtils; 64 import android.os.Flags; 65 import android.os.Handler; 66 import android.os.IInstalld; 67 import android.os.IVold; 68 import android.os.IVoldTaskListener; 69 import android.os.Looper; 70 import android.os.Message; 71 import android.os.ParcelFileDescriptor; 72 import android.os.ParcelableException; 73 import android.os.PersistableBundle; 74 import android.os.ProxyFileDescriptorCallback; 75 import android.os.RemoteException; 76 import android.os.ServiceManager; 77 import android.os.ServiceManager.ServiceNotFoundException; 78 import android.os.SystemProperties; 79 import android.os.UserHandle; 80 import android.provider.DeviceConfig; 81 import android.provider.MediaStore; 82 import android.provider.Settings; 83 import android.system.ErrnoException; 84 import android.system.Os; 85 import android.system.OsConstants; 86 import android.text.TextUtils; 87 import android.util.DataUnit; 88 import android.util.Log; 89 import android.util.Pair; 90 import android.util.Slog; 91 import android.util.SparseArray; 92 93 import com.android.internal.annotations.GuardedBy; 94 import com.android.internal.annotations.VisibleForTesting; 95 import com.android.internal.logging.MetricsLogger; 96 import com.android.internal.os.AppFuseMount; 97 import com.android.internal.os.FuseAppLoop; 98 import com.android.internal.os.FuseUnavailableMountException; 99 import com.android.internal.os.RoSystemProperties; 100 import com.android.internal.util.Preconditions; 101 102 import dalvik.system.BlockGuard; 103 104 import java.io.File; 105 import java.io.FileDescriptor; 106 import java.io.FileNotFoundException; 107 import java.io.IOException; 108 import java.lang.annotation.Retention; 109 import java.lang.annotation.RetentionPolicy; 110 import java.lang.ref.WeakReference; 111 import java.nio.charset.StandardCharsets; 112 import java.util.ArrayList; 113 import java.util.Arrays; 114 import java.util.Collections; 115 import java.util.Iterator; 116 import java.util.List; 117 import java.util.Locale; 118 import java.util.Objects; 119 import java.util.UUID; 120 import java.util.concurrent.CompletableFuture; 121 import java.util.concurrent.Executor; 122 import java.util.concurrent.ThreadFactory; 123 import java.util.concurrent.TimeUnit; 124 import java.util.concurrent.atomic.AtomicInteger; 125 import java.util.regex.Matcher; 126 import java.util.regex.Pattern; 127 128 /** 129 * StorageManager is the interface to the systems storage service. The storage 130 * manager handles storage-related items such as Opaque Binary Blobs (OBBs). 131 * <p> 132 * OBBs contain a filesystem that maybe be encrypted on disk and mounted 133 * on-demand from an application. OBBs are a good way of providing large amounts 134 * of binary assets without packaging them into APKs as they may be multiple 135 * gigabytes in size. However, due to their size, they're most likely stored in 136 * a shared storage pool accessible from all programs. The system does not 137 * guarantee the security of the OBB file itself: if any program modifies the 138 * OBB, there is no guarantee that a read from that OBB will produce the 139 * expected output. 140 */ 141 @SystemService(Context.STORAGE_SERVICE) 142 public class StorageManager { 143 private static final String TAG = "StorageManager"; 144 private static final boolean LOCAL_LOGV = Log.isLoggable(TAG, Log.VERBOSE); 145 146 /** {@hide} */ 147 public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; 148 /** {@hide} */ 149 public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable"; 150 /** {@hide} */ 151 public static final String PROP_HAS_RESERVED = "vold.has_reserved"; 152 /** {@hide} */ 153 public static final String PROP_ADOPTABLE = "persist.sys.adoptable"; 154 /** {@hide} */ 155 public static final String PROP_SDCARDFS = "persist.sys.sdcardfs"; 156 /** {@hide} */ 157 public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk"; 158 /** {@hide} */ 159 public static final String PROP_FORCED_SCOPED_STORAGE_WHITELIST = 160 "forced_scoped_storage_whitelist"; 161 162 /** {@hide} */ 163 public static final String UUID_PRIVATE_INTERNAL = null; 164 /** {@hide} */ 165 public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; 166 /** {@hide} */ 167 public static final String UUID_SYSTEM = "system"; 168 169 // NOTE: See comments around #convert for more details. 170 private static final String FAT_UUID_PREFIX = "fafafafa-fafa-5afa-8afa-fafa"; 171 172 // NOTE: UUID constants below are namespaced 173 // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad default 174 // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad primary_physical 175 // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad system 176 177 /** 178 * UUID representing the default internal storage of this device which 179 * provides {@link Environment#getDataDirectory()}. 180 * <p> 181 * This value is constant across all devices and it will never change, and 182 * thus it cannot be used to uniquely identify a particular physical device. 183 * 184 * @see #getUuidForPath(File) 185 * @see ApplicationInfo#storageUuid 186 */ 187 public static final UUID UUID_DEFAULT = UUID 188 .fromString("41217664-9172-527a-b3d5-edabb50a7d69"); 189 190 /** {@hide} */ 191 public static final UUID UUID_PRIMARY_PHYSICAL_ = UUID 192 .fromString("0f95a519-dae7-5abf-9519-fbd6209e05fd"); 193 194 /** {@hide} */ 195 public static final UUID UUID_SYSTEM_ = UUID 196 .fromString("5d258386-e60d-59e3-826d-0089cdd42cc0"); 197 198 /** 199 * Activity Action: Allows the user to manage their storage. This activity 200 * provides the ability to free up space on the device by deleting data such 201 * as apps. 202 * <p> 203 * If the sending application has a specific storage device or allocation 204 * size in mind, they can optionally define {@link #EXTRA_UUID} or 205 * {@link #EXTRA_REQUESTED_BYTES}, respectively. 206 * <p> 207 * This intent should be launched using 208 * {@link Activity#startActivityForResult(Intent, int)} so that the user 209 * knows which app is requesting the storage space. The returned result will 210 * be {@link Activity#RESULT_OK} if the requested space was made available, 211 * or {@link Activity#RESULT_CANCELED} otherwise. 212 */ 213 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 214 public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; 215 216 /** 217 * Activity Action: Allows the user to free up space by clearing app external cache directories. 218 * The intent doesn't automatically clear cache, but shows a dialog and lets the user decide. 219 * <p> 220 * This intent should be launched using 221 * {@link Activity#startActivityForResult(Intent, int)} so that the user 222 * knows which app is requesting to clear cache. The returned result will be: 223 * {@link Activity#RESULT_OK} if the activity was launched and all cache was cleared, 224 * {@link OsConstants#EIO} if an error occurred while clearing the cache or 225 * {@link Activity#RESULT_CANCELED} otherwise. 226 */ 227 @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) 228 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 229 public static final String ACTION_CLEAR_APP_CACHE = "android.os.storage.action.CLEAR_APP_CACHE"; 230 231 /** 232 * Extra {@link UUID} used to indicate the storage volume where an 233 * application is interested in allocating or managing disk space. 234 * 235 * @see #ACTION_MANAGE_STORAGE 236 * @see #UUID_DEFAULT 237 * @see #getUuidForPath(File) 238 * @see Intent#putExtra(String, java.io.Serializable) 239 */ 240 public static final String EXTRA_UUID = "android.os.storage.extra.UUID"; 241 242 /** 243 * Extra used to indicate the total size (in bytes) that an application is 244 * interested in allocating. 245 * <p> 246 * When defined, the management UI will help guide the user to free up 247 * enough disk space to reach this requested value. 248 * 249 * @see #ACTION_MANAGE_STORAGE 250 */ 251 public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES"; 252 253 /** {@hide} */ 254 public static final int DEBUG_ADOPTABLE_FORCE_ON = 1 << 0; 255 /** {@hide} */ 256 public static final int DEBUG_ADOPTABLE_FORCE_OFF = 1 << 1; 257 /** {@hide} */ 258 public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2; 259 /** {@hide} */ 260 public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3; 261 /** {@hide} */ 262 public static final int DEBUG_VIRTUAL_DISK = 1 << 4; 263 264 /** {@hide} */ 265 public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; 266 /** {@hide} */ 267 public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE; 268 /** {@hide} */ 269 public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL; 270 /** @hide */ 271 public static final int FLAG_STORAGE_SDK = IInstalld.FLAG_STORAGE_SDK; 272 273 /** {@hide} */ 274 @IntDef(prefix = "FLAG_STORAGE_", value = { 275 FLAG_STORAGE_DE, 276 FLAG_STORAGE_CE, 277 FLAG_STORAGE_EXTERNAL, 278 FLAG_STORAGE_SDK, 279 }) 280 @Retention(RetentionPolicy.SOURCE) 281 public @interface StorageFlags { 282 } 283 284 /** {@hide} */ 285 public static final int FLAG_FOR_WRITE = 1 << 8; 286 /** {@hide} */ 287 public static final int FLAG_REAL_STATE = 1 << 9; 288 /** {@hide} */ 289 public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10; 290 /** {@hide} */ 291 public static final int FLAG_INCLUDE_RECENT = 1 << 11; 292 /** {@hide} */ 293 public static final int FLAG_INCLUDE_SHARED_PROFILE = 1 << 12; 294 295 /** {@hide} */ 296 public static final int FSTRIM_FLAG_DEEP = IVold.FSTRIM_FLAG_DEEP_TRIM; 297 298 /** @hide The volume is not encrypted. */ 299 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 300 public static final int ENCRYPTION_STATE_NONE = 1; 301 302 private static volatile IStorageManager sStorageManager = null; 303 304 private final Context mContext; 305 private final ContentResolver mResolver; 306 307 private final IStorageManager mStorageManager; 308 private final AppOpsManager mAppOps; 309 private final Looper mLooper; 310 private final AtomicInteger mNextNonce = new AtomicInteger(0); 311 312 @GuardedBy("mDelegates") 313 private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>(); 314 VolumeListQuery(int mUserId, String mPackageName, int mFlags)315 static record VolumeListQuery(int mUserId, String mPackageName, int mFlags) { 316 } 317 318 private static final PropertyInvalidatedCache.QueryHandler<VolumeListQuery, StorageVolume[]> 319 sVolumeListQuery = new PropertyInvalidatedCache.QueryHandler<>() { 320 @androidx.annotation.Nullable 321 @Override 322 public StorageVolume[] apply(@androidx.annotation.NonNull VolumeListQuery query) { 323 final IStorageManager storageManager = IStorageManager.Stub.asInterface( 324 ServiceManager.getService("mount")); 325 if (storageManager == null) { 326 // negative results won't be cached, so we will just try again next time 327 return null; 328 } 329 try { 330 return storageManager.getVolumeList( 331 query.mUserId, query.mPackageName, query.mFlags); 332 } catch (RemoteException e) { 333 throw e.rethrowFromSystemServer(); 334 } 335 } 336 }; 337 338 // Generally, the userId and packageName parameters stay pretty constant, but flags may change 339 // regularly; we have observed some processes hitting 10+ variations. 340 private static final int VOLUME_LIST_CACHE_MAX = 16; 341 342 private static final PropertyInvalidatedCache<VolumeListQuery, StorageVolume[]> 343 sVolumeListCache = new PropertyInvalidatedCache<>( 344 new PropertyInvalidatedCache.Args(MODULE_SYSTEM).cacheNulls(false) 345 .api("getVolumeList").maxEntries(VOLUME_LIST_CACHE_MAX), "getVolumeList", 346 sVolumeListQuery); 347 348 /** {@hide} */ invalidateVolumeListCache()349 public static void invalidateVolumeListCache() { 350 sVolumeListCache.invalidateCache(); 351 } 352 353 private class StorageEventListenerDelegate extends IStorageEventListener.Stub { 354 final Executor mExecutor; 355 final StorageEventListener mListener; 356 final StorageVolumeCallback mCallback; 357 StorageEventListenerDelegate(@onNull Executor executor, @NonNull StorageEventListener listener, @NonNull StorageVolumeCallback callback)358 public StorageEventListenerDelegate(@NonNull Executor executor, 359 @NonNull StorageEventListener listener, @NonNull StorageVolumeCallback callback) { 360 mExecutor = executor; 361 mListener = listener; 362 mCallback = callback; 363 } 364 365 @Override onUsbMassStorageConnectionChanged(boolean connected)366 public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException { 367 mExecutor.execute(() -> { 368 mListener.onUsbMassStorageConnectionChanged(connected); 369 }); 370 } 371 372 @Override onStorageStateChanged(String path, String oldState, String newState)373 public void onStorageStateChanged(String path, String oldState, String newState) { 374 mExecutor.execute(() -> { 375 mListener.onStorageStateChanged(path, oldState, newState); 376 377 if (path != null) { 378 for (StorageVolume sv : getStorageVolumes()) { 379 if (Objects.equals(path, sv.getPath())) { 380 mCallback.onStateChanged(sv); 381 } 382 } 383 } 384 }); 385 } 386 387 @Override onVolumeStateChanged(VolumeInfo vol, int oldState, int newState)388 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 389 mExecutor.execute(() -> { 390 mListener.onVolumeStateChanged(vol, oldState, newState); 391 392 final File path = vol.getPathForUser(UserHandle.myUserId()); 393 if (path != null) { 394 for (StorageVolume sv : getStorageVolumes()) { 395 if (Objects.equals(path.getAbsolutePath(), sv.getPath())) { 396 mCallback.onStateChanged(sv); 397 } 398 } 399 } 400 }); 401 } 402 403 @Override onVolumeRecordChanged(VolumeRecord rec)404 public void onVolumeRecordChanged(VolumeRecord rec) { 405 mExecutor.execute(() -> { 406 mListener.onVolumeRecordChanged(rec); 407 }); 408 } 409 410 @Override onVolumeForgotten(String fsUuid)411 public void onVolumeForgotten(String fsUuid) { 412 mExecutor.execute(() -> { 413 mListener.onVolumeForgotten(fsUuid); 414 }); 415 } 416 417 @Override onDiskScanned(DiskInfo disk, int volumeCount)418 public void onDiskScanned(DiskInfo disk, int volumeCount) { 419 mExecutor.execute(() -> { 420 mListener.onDiskScanned(disk, volumeCount); 421 }); 422 } 423 424 @Override onDiskDestroyed(DiskInfo disk)425 public void onDiskDestroyed(DiskInfo disk) throws RemoteException { 426 mExecutor.execute(() -> { 427 mListener.onDiskDestroyed(disk); 428 }); 429 } 430 } 431 432 /** 433 * Binder listener for OBB action results. 434 */ 435 private final ObbActionListener mObbActionListener = new ObbActionListener(); 436 437 private class ObbActionListener extends IObbActionListener.Stub { 438 @SuppressWarnings("hiding") 439 private SparseArray<ObbListenerDelegate> mListeners = 440 new SparseArray<ObbListenerDelegate>(); 441 442 @Override onObbResult(String filename, int nonce, int status)443 public void onObbResult(String filename, int nonce, int status) { 444 final ObbListenerDelegate delegate; 445 synchronized (mListeners) { 446 delegate = mListeners.get(nonce); 447 if (delegate != null) { 448 mListeners.remove(nonce); 449 } 450 } 451 452 if (delegate != null) { 453 delegate.sendObbStateChanged(filename, status); 454 } 455 } 456 addListener(OnObbStateChangeListener listener)457 public int addListener(OnObbStateChangeListener listener) { 458 final ObbListenerDelegate delegate = new ObbListenerDelegate(listener); 459 460 synchronized (mListeners) { 461 mListeners.put(delegate.nonce, delegate); 462 } 463 464 return delegate.nonce; 465 } 466 } 467 getNextNonce()468 private int getNextNonce() { 469 return mNextNonce.getAndIncrement(); 470 } 471 472 /** 473 * Private class containing sender and receiver code for StorageEvents. 474 */ 475 private class ObbListenerDelegate { 476 private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef; 477 private final Handler mHandler; 478 479 private final int nonce; 480 ObbListenerDelegate(OnObbStateChangeListener listener)481 ObbListenerDelegate(OnObbStateChangeListener listener) { 482 nonce = getNextNonce(); 483 mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener); 484 mHandler = new Handler(mLooper) { 485 @Override 486 public void handleMessage(Message msg) { 487 final OnObbStateChangeListener changeListener = getListener(); 488 if (changeListener == null) { 489 return; 490 } 491 492 changeListener.onObbStateChange((String) msg.obj, msg.arg1); 493 } 494 }; 495 } 496 getListener()497 OnObbStateChangeListener getListener() { 498 if (mObbEventListenerRef == null) { 499 return null; 500 } 501 return mObbEventListenerRef.get(); 502 } 503 sendObbStateChanged(String path, int state)504 void sendObbStateChanged(String path, int state) { 505 mHandler.obtainMessage(0, state, 0, path).sendToTarget(); 506 } 507 } 508 509 /** {@hide} */ 510 @Deprecated 511 @UnsupportedAppUsage from(Context context)512 public static StorageManager from(Context context) { 513 return context.getSystemService(StorageManager.class); 514 } 515 516 /** 517 * Constructs a StorageManager object through which an application can 518 * can communicate with the systems mount service. 519 * 520 * @param looper The {@link android.os.Looper} which events will be received on. 521 * 522 * <p>Applications can get instance of this class by calling 523 * {@link android.content.Context#getSystemService(java.lang.String)} with an 524 * argument 525 * of {@link android.content.Context#STORAGE_SERVICE}. 526 * @hide 527 */ 528 @UnsupportedAppUsage StorageManager(Context context, Looper looper)529 public StorageManager(Context context, Looper looper) throws ServiceNotFoundException { 530 mContext = context; 531 mResolver = context.getContentResolver(); 532 mLooper = looper; 533 mStorageManager = IStorageManager.Stub.asInterface( 534 ServiceManager.getServiceOrThrow("mount")); 535 mAppOps = mContext.getSystemService(AppOpsManager.class); 536 } 537 538 /** 539 * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}. 540 * 541 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} 542 * object. 543 * @hide 544 */ 545 @UnsupportedAppUsage registerListener(StorageEventListener listener)546 public void registerListener(StorageEventListener listener) { 547 synchronized (mDelegates) { 548 final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate( 549 mContext.getMainExecutor(), listener, new StorageVolumeCallback()); 550 try { 551 mStorageManager.registerListener(delegate); 552 } catch (RemoteException e) { 553 throw e.rethrowFromSystemServer(); 554 } 555 mDelegates.add(delegate); 556 } 557 } 558 559 /** 560 * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}. 561 * 562 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} 563 * object. 564 * @hide 565 */ 566 @UnsupportedAppUsage unregisterListener(StorageEventListener listener)567 public void unregisterListener(StorageEventListener listener) { 568 synchronized (mDelegates) { 569 for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext(); ) { 570 final StorageEventListenerDelegate delegate = i.next(); 571 if (delegate.mListener == listener) { 572 try { 573 mStorageManager.unregisterListener(delegate); 574 } catch (RemoteException e) { 575 throw e.rethrowFromSystemServer(); 576 } 577 i.remove(); 578 } 579 } 580 } 581 } 582 583 /** 584 * Callback that delivers {@link StorageVolume} related events. 585 * <p> 586 * For example, this can be used to detect when a volume changes to the 587 * {@link Environment#MEDIA_MOUNTED} or {@link Environment#MEDIA_UNMOUNTED} 588 * states. 589 * 590 * @see StorageManager#registerStorageVolumeCallback 591 * @see StorageManager#unregisterStorageVolumeCallback 592 */ 593 public static class StorageVolumeCallback { 594 /** 595 * Called when {@link StorageVolume#getState()} changes, such as 596 * changing to the {@link Environment#MEDIA_MOUNTED} or 597 * {@link Environment#MEDIA_UNMOUNTED} states. 598 * <p> 599 * The given argument is a snapshot in time and can be used to process 600 * events in the order they occurred, or you can call 601 * {@link StorageManager#getStorageVolumes()} to observe the latest 602 * value. 603 */ onStateChanged(@onNull StorageVolume volume)604 public void onStateChanged(@NonNull StorageVolume volume) { 605 } 606 } 607 608 /** 609 * Registers the given callback to listen for {@link StorageVolume} changes. 610 * <p> 611 * For example, this can be used to detect when a volume changes to the 612 * {@link Environment#MEDIA_MOUNTED} or {@link Environment#MEDIA_UNMOUNTED} 613 * states. 614 * 615 * @see StorageManager#unregisterStorageVolumeCallback 616 */ registerStorageVolumeCallback(@allbackExecutor @onNull Executor executor, @NonNull StorageVolumeCallback callback)617 public void registerStorageVolumeCallback(@CallbackExecutor @NonNull Executor executor, 618 @NonNull StorageVolumeCallback callback) { 619 synchronized (mDelegates) { 620 final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate( 621 executor, new StorageEventListener(), callback); 622 try { 623 mStorageManager.registerListener(delegate); 624 } catch (RemoteException e) { 625 throw e.rethrowFromSystemServer(); 626 } 627 mDelegates.add(delegate); 628 } 629 } 630 631 /** 632 * Unregisters the given callback from listening for {@link StorageVolume} 633 * changes. 634 * 635 * @see StorageManager#registerStorageVolumeCallback 636 */ unregisterStorageVolumeCallback(@onNull StorageVolumeCallback callback)637 public void unregisterStorageVolumeCallback(@NonNull StorageVolumeCallback callback) { 638 synchronized (mDelegates) { 639 for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext(); ) { 640 final StorageEventListenerDelegate delegate = i.next(); 641 if (delegate.mCallback == callback) { 642 try { 643 mStorageManager.unregisterListener(delegate); 644 } catch (RemoteException e) { 645 throw e.rethrowFromSystemServer(); 646 } 647 i.remove(); 648 } 649 } 650 } 651 } 652 653 /** 654 * Enables USB Mass Storage (UMS) on the device. 655 * 656 * @hide 657 */ 658 @Deprecated 659 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) enableUsbMassStorage()660 public void enableUsbMassStorage() { 661 } 662 663 /** 664 * Disables USB Mass Storage (UMS) on the device. 665 * 666 * @hide 667 */ 668 @Deprecated 669 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) disableUsbMassStorage()670 public void disableUsbMassStorage() { 671 } 672 673 /** 674 * Query if a USB Mass Storage (UMS) host is connected. 675 * 676 * @return true if UMS host is connected. 677 * @hide 678 */ 679 @Deprecated 680 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isUsbMassStorageConnected()681 public boolean isUsbMassStorageConnected() { 682 return false; 683 } 684 685 /** 686 * Query if a USB Mass Storage (UMS) is enabled on the device. 687 * 688 * @return true if UMS host is enabled. 689 * @hide 690 */ 691 @Deprecated 692 @UnsupportedAppUsage isUsbMassStorageEnabled()693 public boolean isUsbMassStorageEnabled() { 694 return false; 695 } 696 697 /** 698 * Mount an Opaque Binary Blob (OBB) file. 699 * <p> 700 * The OBB will remain mounted for as long as the StorageManager reference 701 * is held by the application. As soon as this reference is lost, the OBBs 702 * in use will be unmounted. The {@link OnObbStateChangeListener} registered 703 * with this call will receive the success or failure of this operation. 704 * <p> 705 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 706 * file matches a package ID that is owned by the calling program's UID. 707 * That is, shared UID applications can attempt to mount any other 708 * application's OBB that shares its UID. 709 * 710 * @param rawPath the path to the OBB file 711 * @param key must be <code>null</code>. Previously, some Android device 712 * implementations accepted a non-<code>null</code> key to mount 713 * an encrypted OBB file. However, this never worked reliably and 714 * is no longer supported. 715 * @param listener will receive the success or failure of the operation 716 * @return whether the mount call was successfully queued or not 717 */ mountObb(String rawPath, String key, OnObbStateChangeListener listener)718 public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) { 719 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 720 Preconditions.checkArgument(key == null, "mounting encrypted OBBs is no longer supported"); 721 Preconditions.checkNotNull(listener, "listener cannot be null"); 722 723 try { 724 final String canonicalPath = new File(rawPath).getCanonicalPath(); 725 final int nonce = mObbActionListener.addListener(listener); 726 mStorageManager.mountObb(rawPath, canonicalPath, mObbActionListener, nonce, 727 getObbInfo(canonicalPath)); 728 return true; 729 } catch (IOException e) { 730 throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e); 731 } catch (RemoteException e) { 732 throw e.rethrowFromSystemServer(); 733 } 734 } 735 736 /** 737 * Returns a {@link PendingIntent} that can be used by Apps with 738 * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission 739 * to launch the manageSpaceActivity for any App that implements it, irrespective of its 740 * exported status. 741 * <p> 742 * Caller has the responsibility of supplying a valid packageName which has 743 * manageSpaceActivity implemented. 744 * 745 * @param packageName package name for the App for which manageSpaceActivity is to be launched 746 * @param requestCode for launching the activity 747 * @return PendingIntent to launch the manageSpaceActivity if successful, null if the 748 * packageName doesn't have a manageSpaceActivity. 749 * @throws IllegalArgumentException an invalid packageName is supplied. 750 */ 751 @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) 752 @Nullable getManageSpaceActivityIntent( @onNull String packageName, int requestCode)753 public PendingIntent getManageSpaceActivityIntent( 754 @NonNull String packageName, int requestCode) { 755 try { 756 return mStorageManager.getManageSpaceActivityIntent(packageName, 757 requestCode); 758 } catch (RemoteException e) { 759 throw e.rethrowFromSystemServer(); 760 } 761 } 762 getObbInfo(String canonicalPath)763 private ObbInfo getObbInfo(String canonicalPath) { 764 try { 765 final ObbInfo obbInfo = ObbScanner.getObbInfo(canonicalPath); 766 return obbInfo; 767 } catch (IOException e) { 768 throw new IllegalArgumentException("Couldn't get OBB info for " + canonicalPath, e); 769 } 770 } 771 772 /** 773 * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the 774 * <code>force</code> flag is true, it will kill any application needed to 775 * unmount the given OBB (even the calling application). 776 * <p> 777 * The {@link OnObbStateChangeListener} registered with this call will 778 * receive the success or failure of this operation. 779 * <p> 780 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 781 * file matches a package ID that is owned by the calling program's UID. 782 * That is, shared UID applications can obtain access to any other 783 * application's OBB that shares its UID. 784 * <p> 785 * 786 * @param rawPath path to the OBB file 787 * @param force whether to kill any programs using this in order to unmount 788 * it 789 * @param listener will receive the success or failure of the operation 790 * @return whether the unmount call was successfully queued or not 791 */ unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener)792 public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) { 793 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 794 Preconditions.checkNotNull(listener, "listener cannot be null"); 795 796 try { 797 final int nonce = mObbActionListener.addListener(listener); 798 mStorageManager.unmountObb(rawPath, force, mObbActionListener, nonce); 799 return true; 800 } catch (RemoteException e) { 801 throw e.rethrowFromSystemServer(); 802 } 803 } 804 805 /** 806 * Check whether an Opaque Binary Blob (OBB) is mounted or not. 807 * 808 * @param rawPath path to OBB image 809 * @return true if OBB is mounted; false if not mounted or on error 810 */ isObbMounted(String rawPath)811 public boolean isObbMounted(String rawPath) { 812 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 813 814 try { 815 return mStorageManager.isObbMounted(rawPath); 816 } catch (RemoteException e) { 817 throw e.rethrowFromSystemServer(); 818 } 819 } 820 821 /** 822 * Check the mounted path of an Opaque Binary Blob (OBB) file. This will 823 * give you the path to where you can obtain access to the internals of the 824 * OBB. 825 * 826 * @param rawPath path to OBB image 827 * @return absolute path to mounted OBB image data or <code>null</code> if 828 * not mounted or exception encountered trying to read status 829 */ getMountedObbPath(String rawPath)830 public String getMountedObbPath(String rawPath) { 831 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 832 833 try { 834 return mStorageManager.getMountedObbPath(rawPath); 835 } catch (RemoteException e) { 836 throw e.rethrowFromSystemServer(); 837 } 838 } 839 840 /** {@hide} */ 841 @UnsupportedAppUsage getDisks()842 public @NonNull List<DiskInfo> getDisks() { 843 try { 844 return Arrays.asList(mStorageManager.getDisks()); 845 } catch (RemoteException e) { 846 throw e.rethrowFromSystemServer(); 847 } 848 } 849 850 /** {@hide} */ 851 @UnsupportedAppUsage findDiskById(String id)852 public @Nullable DiskInfo findDiskById(String id) { 853 Preconditions.checkNotNull(id); 854 // TODO; go directly to service to make this faster 855 for (DiskInfo disk : getDisks()) { 856 if (Objects.equals(disk.id, id)) { 857 return disk; 858 } 859 } 860 return null; 861 } 862 863 /** {@hide} */ 864 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) findVolumeById(String id)865 public @Nullable VolumeInfo findVolumeById(String id) { 866 Preconditions.checkNotNull(id); 867 // TODO; go directly to service to make this faster 868 for (VolumeInfo vol : getVolumes()) { 869 if (Objects.equals(vol.id, id)) { 870 return vol; 871 } 872 } 873 return null; 874 } 875 876 /** {@hide} */ 877 @UnsupportedAppUsage findVolumeByUuid(String fsUuid)878 public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) { 879 Preconditions.checkNotNull(fsUuid); 880 // TODO; go directly to service to make this faster 881 for (VolumeInfo vol : getVolumes()) { 882 if (Objects.equals(vol.fsUuid, fsUuid)) { 883 return vol; 884 } 885 } 886 return null; 887 } 888 889 /** {@hide} */ findRecordByUuid(String fsUuid)890 public @Nullable VolumeRecord findRecordByUuid(String fsUuid) { 891 Preconditions.checkNotNull(fsUuid); 892 // TODO; go directly to service to make this faster 893 for (VolumeRecord rec : getVolumeRecords()) { 894 if (Objects.equals(rec.fsUuid, fsUuid)) { 895 return rec; 896 } 897 } 898 return null; 899 } 900 901 /** {@hide} */ findPrivateForEmulated(VolumeInfo emulatedVol)902 public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) { 903 if (emulatedVol != null) { 904 String id = emulatedVol.getId(); 905 int idx = id.indexOf(";"); 906 if (idx != -1) { 907 id = id.substring(0, idx); 908 } 909 return findVolumeById(id.replace("emulated", "private")); 910 } else { 911 return null; 912 } 913 } 914 915 /** {@hide} */ 916 @UnsupportedAppUsage findEmulatedForPrivate(VolumeInfo privateVol)917 public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) { 918 if (privateVol != null) { 919 return findVolumeById(privateVol.getId().replace("private", "emulated") + ";" 920 + mContext.getUserId()); 921 } else { 922 return null; 923 } 924 } 925 926 /** {@hide} */ findVolumeByQualifiedUuid(String volumeUuid)927 public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) { 928 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { 929 return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); 930 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 931 return getPrimaryPhysicalVolume(); 932 } else { 933 return findVolumeByUuid(volumeUuid); 934 } 935 } 936 937 /** 938 * Return a UUID identifying the storage volume that hosts the given 939 * filesystem path. 940 * <p> 941 * If this path is hosted by the default internal storage of the device at 942 * {@link Environment#getDataDirectory()}, the returned value will be 943 * {@link #UUID_DEFAULT}. 944 * 945 * @throws IOException when the storage device hosting the given path isn't 946 * present, or when it doesn't have a valid UUID. 947 */ getUuidForPath(@onNull File path)948 public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException { 949 Preconditions.checkNotNull(path); 950 final String pathString = path.getCanonicalPath(); 951 if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) { 952 return UUID_DEFAULT; 953 } 954 try { 955 for (VolumeInfo vol : mStorageManager.getVolumes(0)) { 956 if (vol.path != null && FileUtils.contains(vol.path, pathString) 957 && vol.type != VolumeInfo.TYPE_PUBLIC && vol.type != VolumeInfo.TYPE_STUB) { 958 // TODO: verify that emulated adopted devices have UUID of 959 // underlying volume 960 try { 961 return convert(vol.fsUuid); 962 } catch (IllegalArgumentException e) { 963 continue; 964 } 965 } 966 } 967 } catch (RemoteException e) { 968 throw e.rethrowFromSystemServer(); 969 } 970 throw new FileNotFoundException("Failed to find a storage device for " + path); 971 } 972 973 /** {@hide} */ findPathForUuid(String volumeUuid)974 public @NonNull File findPathForUuid(String volumeUuid) throws FileNotFoundException { 975 final VolumeInfo vol = findVolumeByQualifiedUuid(volumeUuid); 976 if (vol != null) { 977 return vol.getPath(); 978 } 979 throw new FileNotFoundException("Failed to find a storage device for " + volumeUuid); 980 } 981 982 /** 983 * Test if the given file descriptor supports allocation of disk space using 984 * {@link #allocateBytes(FileDescriptor, long)}. 985 */ isAllocationSupported(@onNull FileDescriptor fd)986 public boolean isAllocationSupported(@NonNull FileDescriptor fd) { 987 try { 988 getUuidForPath(ParcelFileDescriptor.getFile(fd)); 989 return true; 990 } catch (IOException e) { 991 return false; 992 } 993 } 994 995 /** {@hide} */ 996 @UnsupportedAppUsage getVolumes()997 public @NonNull List<VolumeInfo> getVolumes() { 998 try { 999 return Arrays.asList(mStorageManager.getVolumes(0)); 1000 } catch (RemoteException e) { 1001 throw e.rethrowFromSystemServer(); 1002 } 1003 } 1004 1005 /** {@hide} */ getWritablePrivateVolumes()1006 public @NonNull List<VolumeInfo> getWritablePrivateVolumes() { 1007 try { 1008 final ArrayList<VolumeInfo> res = new ArrayList<>(); 1009 for (VolumeInfo vol : mStorageManager.getVolumes(0)) { 1010 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) { 1011 res.add(vol); 1012 } 1013 } 1014 return res; 1015 } catch (RemoteException e) { 1016 throw e.rethrowFromSystemServer(); 1017 } 1018 } 1019 1020 /** {@hide} */ getVolumeRecords()1021 public @NonNull List<VolumeRecord> getVolumeRecords() { 1022 try { 1023 return Arrays.asList(mStorageManager.getVolumeRecords(0)); 1024 } catch (RemoteException e) { 1025 throw e.rethrowFromSystemServer(); 1026 } 1027 } 1028 1029 /** {@hide} */ 1030 @UnsupportedAppUsage getBestVolumeDescription(VolumeInfo vol)1031 public @Nullable String getBestVolumeDescription(VolumeInfo vol) { 1032 if (vol == null) return null; 1033 1034 // Nickname always takes precedence when defined 1035 if (!TextUtils.isEmpty(vol.fsUuid)) { 1036 final VolumeRecord rec = findRecordByUuid(vol.fsUuid); 1037 if (rec != null && !TextUtils.isEmpty(rec.nickname)) { 1038 return rec.nickname; 1039 } 1040 } 1041 1042 if (!TextUtils.isEmpty(vol.getDescription())) { 1043 return vol.getDescription(); 1044 } 1045 1046 if (vol.disk != null) { 1047 return vol.disk.getDescription(); 1048 } 1049 1050 return null; 1051 } 1052 1053 /** {@hide} */ 1054 @UnsupportedAppUsage getPrimaryPhysicalVolume()1055 public @Nullable VolumeInfo getPrimaryPhysicalVolume() { 1056 final List<VolumeInfo> vols = getVolumes(); 1057 for (VolumeInfo vol : vols) { 1058 if (vol.isPrimaryPhysical()) { 1059 return vol; 1060 } 1061 } 1062 return null; 1063 } 1064 1065 /** {@hide} */ mount(String volId)1066 public void mount(String volId) { 1067 try { 1068 mStorageManager.mount(volId); 1069 } catch (RemoteException e) { 1070 throw e.rethrowFromSystemServer(); 1071 } 1072 } 1073 1074 /** {@hide} */ 1075 @UnsupportedAppUsage unmount(String volId)1076 public void unmount(String volId) { 1077 try { 1078 mStorageManager.unmount(volId); 1079 } catch (RemoteException e) { 1080 throw e.rethrowFromSystemServer(); 1081 } 1082 } 1083 1084 /** {@hide} */ 1085 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) format(String volId)1086 public void format(String volId) { 1087 try { 1088 mStorageManager.format(volId); 1089 } catch (RemoteException e) { 1090 throw e.rethrowFromSystemServer(); 1091 } 1092 } 1093 1094 /** {@hide} */ 1095 @Deprecated benchmark(String volId)1096 public long benchmark(String volId) { 1097 final CompletableFuture<PersistableBundle> result = new CompletableFuture<>(); 1098 benchmark(volId, new IVoldTaskListener.Stub() { 1099 @Override 1100 public void onStatus(int status, PersistableBundle extras) { 1101 // Ignored 1102 } 1103 1104 @Override 1105 public void onFinished(int status, PersistableBundle extras) { 1106 result.complete(extras); 1107 } 1108 }); 1109 try { 1110 // Convert ms to ns 1111 return result.get(3, TimeUnit.MINUTES).getLong("run", Long.MAX_VALUE) * 1000000; 1112 } catch (Exception e) { 1113 return Long.MAX_VALUE; 1114 } 1115 } 1116 1117 /** {@hide} */ benchmark(String volId, IVoldTaskListener listener)1118 public void benchmark(String volId, IVoldTaskListener listener) { 1119 try { 1120 mStorageManager.benchmark(volId, listener); 1121 } catch (RemoteException e) { 1122 throw e.rethrowFromSystemServer(); 1123 } 1124 } 1125 1126 /** {@hide} */ 1127 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) partitionPublic(String diskId)1128 public void partitionPublic(String diskId) { 1129 try { 1130 mStorageManager.partitionPublic(diskId); 1131 } catch (RemoteException e) { 1132 throw e.rethrowFromSystemServer(); 1133 } 1134 } 1135 1136 /** {@hide} */ partitionPrivate(String diskId)1137 public void partitionPrivate(String diskId) { 1138 try { 1139 mStorageManager.partitionPrivate(diskId); 1140 } catch (RemoteException e) { 1141 throw e.rethrowFromSystemServer(); 1142 } 1143 } 1144 1145 /** {@hide} */ partitionMixed(String diskId, int ratio)1146 public void partitionMixed(String diskId, int ratio) { 1147 try { 1148 mStorageManager.partitionMixed(diskId, ratio); 1149 } catch (RemoteException e) { 1150 throw e.rethrowFromSystemServer(); 1151 } 1152 } 1153 1154 /** {@hide} */ wipeAdoptableDisks()1155 public void wipeAdoptableDisks() { 1156 // We only wipe devices in "adoptable" locations, which are in a 1157 // long-term stable slot/location on the device, where apps have a 1158 // reasonable chance of storing sensitive data. (Apps need to go through 1159 // SAF to write to transient volumes.) 1160 final List<DiskInfo> disks = getDisks(); 1161 for (DiskInfo disk : disks) { 1162 final String diskId = disk.getId(); 1163 if (disk.isAdoptable()) { 1164 Slog.d(TAG, "Found adoptable " + diskId + "; wiping"); 1165 try { 1166 // TODO: switch to explicit wipe command when we have it, 1167 // for now rely on the fact that vfat format does a wipe 1168 mStorageManager.partitionPublic(diskId); 1169 } catch (Exception e) { 1170 Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e); 1171 } 1172 } else { 1173 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId()); 1174 } 1175 } 1176 } 1177 1178 /** {@hide} */ setVolumeNickname(String fsUuid, String nickname)1179 public void setVolumeNickname(String fsUuid, String nickname) { 1180 try { 1181 mStorageManager.setVolumeNickname(fsUuid, nickname); 1182 } catch (RemoteException e) { 1183 throw e.rethrowFromSystemServer(); 1184 } 1185 } 1186 1187 /** {@hide} */ setVolumeInited(String fsUuid, boolean inited)1188 public void setVolumeInited(String fsUuid, boolean inited) { 1189 try { 1190 mStorageManager.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0, 1191 VolumeRecord.USER_FLAG_INITED); 1192 } catch (RemoteException e) { 1193 throw e.rethrowFromSystemServer(); 1194 } 1195 } 1196 1197 /** {@hide} */ setVolumeSnoozed(String fsUuid, boolean snoozed)1198 public void setVolumeSnoozed(String fsUuid, boolean snoozed) { 1199 try { 1200 mStorageManager.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0, 1201 VolumeRecord.USER_FLAG_SNOOZED); 1202 } catch (RemoteException e) { 1203 throw e.rethrowFromSystemServer(); 1204 } 1205 } 1206 1207 /** {@hide} */ forgetVolume(String fsUuid)1208 public void forgetVolume(String fsUuid) { 1209 try { 1210 mStorageManager.forgetVolume(fsUuid); 1211 } catch (RemoteException e) { 1212 throw e.rethrowFromSystemServer(); 1213 } 1214 } 1215 1216 /** 1217 * This is not the API you're looking for. 1218 * 1219 * @hide 1220 * @see PackageManager#getPrimaryStorageCurrentVolume() 1221 */ getPrimaryStorageUuid()1222 public String getPrimaryStorageUuid() { 1223 try { 1224 return mStorageManager.getPrimaryStorageUuid(); 1225 } catch (RemoteException e) { 1226 throw e.rethrowFromSystemServer(); 1227 } 1228 } 1229 1230 /** 1231 * This is not the API you're looking for. 1232 * 1233 * @hide 1234 * @see PackageManager#movePrimaryStorage(VolumeInfo) 1235 */ setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)1236 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { 1237 try { 1238 mStorageManager.setPrimaryStorageUuid(volumeUuid, callback); 1239 } catch (RemoteException e) { 1240 throw e.rethrowFromSystemServer(); 1241 } 1242 } 1243 1244 /** 1245 * Return the {@link StorageVolume} that contains the given file, or 1246 * {@code null} if none. 1247 */ getStorageVolume(@onNull File file)1248 public @Nullable StorageVolume getStorageVolume(@NonNull File file) { 1249 return getStorageVolume(getVolumeList(), file); 1250 } 1251 1252 /** 1253 * Return the {@link StorageVolume} that contains the given 1254 * {@link MediaStore} item. 1255 */ getStorageVolume(@onNull Uri uri)1256 public @NonNull StorageVolume getStorageVolume(@NonNull Uri uri) { 1257 String volumeName = MediaStore.getVolumeName(uri); 1258 1259 // When Uri is pointing at a synthetic volume, we're willing to query to 1260 // resolve the actual volume name 1261 if (Objects.equals(volumeName, MediaStore.VOLUME_EXTERNAL)) { 1262 try (Cursor c = mContext.getContentResolver().query(uri, 1263 new String[]{MediaStore.MediaColumns.VOLUME_NAME}, null, null)) { 1264 if (c.moveToFirst()) { 1265 volumeName = c.getString(0); 1266 } 1267 } 1268 } 1269 1270 switch (volumeName) { 1271 case MediaStore.VOLUME_EXTERNAL_PRIMARY: 1272 return getPrimaryStorageVolume(); 1273 default: 1274 for (StorageVolume vol : getStorageVolumes()) { 1275 if (Objects.equals(vol.getMediaStoreVolumeName(), volumeName)) { 1276 return vol; 1277 } 1278 } 1279 } 1280 throw new IllegalStateException("Unknown volume for " + uri); 1281 } 1282 1283 /** {@hide} */ getStorageVolume(File file, int userId)1284 public static @Nullable StorageVolume getStorageVolume(File file, int userId) { 1285 return getStorageVolume(getVolumeList(userId, 0), file); 1286 } 1287 1288 /** {@hide} */ 1289 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getStorageVolume(StorageVolume[] volumes, File file)1290 private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) { 1291 if (file == null) { 1292 return null; 1293 } 1294 final String path = file.getAbsolutePath(); 1295 if (path.startsWith(DEPRECATE_DATA_PREFIX)) { 1296 final Uri uri = ContentResolver.translateDeprecatedDataPath(path); 1297 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class) 1298 .getStorageVolume(uri); 1299 } 1300 try { 1301 file = file.getCanonicalFile(); 1302 } catch (IOException ignored) { 1303 Slog.d(TAG, "Could not get canonical path for " + file); 1304 return null; 1305 } 1306 for (StorageVolume volume : volumes) { 1307 File volumeFile = volume.getPathFile(); 1308 try { 1309 volumeFile = volumeFile.getCanonicalFile(); 1310 } catch (IOException ignored) { 1311 continue; 1312 } 1313 if (FileUtils.contains(volumeFile, file)) { 1314 return volume; 1315 } 1316 } 1317 return null; 1318 } 1319 1320 /** 1321 * Gets the state of a volume via its mountpoint. 1322 * 1323 * @hide 1324 */ 1325 @Deprecated 1326 @UnsupportedAppUsage getVolumeState(String mountPoint)1327 public @NonNull String getVolumeState(String mountPoint) { 1328 final StorageVolume vol = getStorageVolume(new File(mountPoint)); 1329 if (vol != null) { 1330 return vol.getState(); 1331 } else { 1332 return Environment.MEDIA_UNKNOWN; 1333 } 1334 } 1335 1336 /** 1337 * Return the list of shared/external storage volumes currently available to 1338 * the calling user. 1339 * <p> 1340 * These storage volumes are actively attached to the device, but may be in 1341 * any mount state, as returned by {@link StorageVolume#getState()}. Returns 1342 * both the primary shared storage device and any attached external volumes, 1343 * including SD cards and USB drives. 1344 */ getStorageVolumes()1345 public @NonNull List<StorageVolume> getStorageVolumes() { 1346 final ArrayList<StorageVolume> res = new ArrayList<>(); 1347 Collections.addAll(res, 1348 getVolumeList(mContext.getUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)); 1349 return res; 1350 } 1351 1352 /** 1353 * Return the list of shared/external storage volumes currently available to 1354 * the calling user and the user it shares media with. Please refer to 1355 * <a href="https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support"> 1356 * multi-user support</a> for more details. 1357 * 1358 * <p> 1359 * This is similar to {@link StorageManager#getStorageVolumes()} except that the result also 1360 * includes the volumes belonging to any user it shares media with 1361 */ 1362 @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) getStorageVolumesIncludingSharedProfiles()1363 public @NonNull List<StorageVolume> getStorageVolumesIncludingSharedProfiles() { 1364 final ArrayList<StorageVolume> res = new ArrayList<>(); 1365 Collections.addAll(res, 1366 getVolumeList(mContext.getUserId(), 1367 FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE | FLAG_INCLUDE_SHARED_PROFILE)); 1368 return res; 1369 } 1370 1371 /** 1372 * Return the list of shared/external storage volumes both currently and 1373 * recently available to the calling user. 1374 * <p> 1375 * Recently available storage volumes are likely to reappear in the future, 1376 * so apps are encouraged to preserve any indexed metadata related to these 1377 * volumes to optimize user experiences. 1378 */ getRecentStorageVolumes()1379 public @NonNull List<StorageVolume> getRecentStorageVolumes() { 1380 final ArrayList<StorageVolume> res = new ArrayList<>(); 1381 Collections.addAll(res, 1382 getVolumeList(mContext.getUserId(), 1383 FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE | FLAG_INCLUDE_RECENT)); 1384 return res; 1385 } 1386 1387 /** 1388 * Return the primary shared/external storage volume available to the 1389 * current user. This volume is the same storage device returned by 1390 * {@link Environment#getExternalStorageDirectory()} and 1391 * {@link Context#getExternalFilesDir(String)}. 1392 */ getPrimaryStorageVolume()1393 public @NonNull StorageVolume getPrimaryStorageVolume() { 1394 return getVolumeList(mContext.getUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0]; 1395 } 1396 1397 /** {@hide} */ getPrimaryStoragePathAndSize()1398 public static Pair<String, Long> getPrimaryStoragePathAndSize() { 1399 return Pair.create(null, 1400 FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace() 1401 + Environment.getRootDirectory().getTotalSpace())); 1402 } 1403 1404 /** {@hide} */ getPrimaryStorageSize()1405 public long getPrimaryStorageSize() { 1406 return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace() 1407 + Environment.getRootDirectory().getTotalSpace()); 1408 } 1409 1410 /** {@hide} */ getInternalStorageBlockDeviceSize()1411 public long getInternalStorageBlockDeviceSize() { 1412 try { 1413 return mStorageManager.getInternalStorageBlockDeviceSize(); 1414 } catch (RemoteException e) { 1415 throw e.rethrowFromSystemServer(); 1416 } 1417 } 1418 1419 /** {@hide} */ mkdirs(File file)1420 public void mkdirs(File file) { 1421 BlockGuard.getVmPolicy().onPathAccess(file.getAbsolutePath()); 1422 try { 1423 mStorageManager.mkdirs(mContext.getOpPackageName(), file.getAbsolutePath()); 1424 } catch (RemoteException e) { 1425 throw e.rethrowFromSystemServer(); 1426 } 1427 } 1428 1429 /** @removed */ getVolumeList()1430 public @NonNull StorageVolume[] getVolumeList() { 1431 return getVolumeList(mContext.getUserId(), 0); 1432 } 1433 1434 /** {@hide} */ 1435 @UnsupportedAppUsage getVolumeList(int userId, int flags)1436 public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) { 1437 try { 1438 String packageName = ActivityThread.currentOpPackageName(); 1439 if (packageName == null) { 1440 // Package name can be null if the activity thread is running but the app 1441 // hasn't bound yet. In this case we fall back to the first package in the 1442 // current UID. This works for runtime permissions as permission state is 1443 // per UID and permission related app ops are updated for all UID packages. 1444 String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid( 1445 android.os.Process.myUid()); 1446 if (packageNames == null || packageNames.length <= 0) { 1447 Log.w(TAG, "Missing package names; no storage volumes available"); 1448 return new StorageVolume[0]; 1449 } 1450 packageName = packageNames[0]; 1451 } 1452 return sVolumeListCache.query(new VolumeListQuery(userId, packageName, flags)); 1453 } catch (RemoteException e) { 1454 throw e.rethrowFromSystemServer(); 1455 } 1456 } 1457 1458 /** 1459 * Returns list of paths for all mountable volumes. 1460 * 1461 * @hide 1462 */ 1463 @Deprecated 1464 @UnsupportedAppUsage getVolumePaths()1465 public @NonNull String[] getVolumePaths() { 1466 StorageVolume[] volumes = getVolumeList(); 1467 int count = volumes.length; 1468 String[] paths = new String[count]; 1469 for (int i = 0; i < count; i++) { 1470 paths[i] = volumes[i].getPath(); 1471 } 1472 return paths; 1473 } 1474 1475 /** @removed */ getPrimaryVolume()1476 public @NonNull StorageVolume getPrimaryVolume() { 1477 return getPrimaryVolume(getVolumeList()); 1478 } 1479 1480 /** {@hide} */ getPrimaryVolume(StorageVolume[] volumes)1481 public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) { 1482 for (StorageVolume volume : volumes) { 1483 if (volume.isPrimary()) { 1484 return volume; 1485 } 1486 } 1487 throw new IllegalStateException("Missing primary storage"); 1488 } 1489 1490 /** 1491 * Devices having above STORAGE_THRESHOLD_PERCENT_HIGH of total space free are considered to be 1492 * in high free space category. 1493 * 1494 * @hide 1495 */ 1496 public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH = 20; 1497 /** {@hide} */ 1498 @TestApi 1499 public static final String 1500 STORAGE_THRESHOLD_PERCENT_HIGH_KEY = "storage_threshold_percent_high"; 1501 /** 1502 * Devices having below STORAGE_THRESHOLD_PERCENT_LOW of total space free are considered to be 1503 * in low free space category and can be configured via 1504 * Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE. 1505 * 1506 * @hide 1507 */ 1508 public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW = 5; 1509 /** 1510 * For devices in high free space category, CACHE_RESERVE_PERCENT_HIGH percent of total space is 1511 * allocated for cache. 1512 * 1513 * @hide 1514 */ 1515 public static final int DEFAULT_CACHE_RESERVE_PERCENT_HIGH = 10; 1516 /** {@hide} */ 1517 @TestApi 1518 public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high"; 1519 /** 1520 * For devices in low free space category, CACHE_RESERVE_PERCENT_LOW percent of total space is 1521 * allocated for cache. 1522 * 1523 * @hide 1524 */ 1525 public static final int DEFAULT_CACHE_RESERVE_PERCENT_LOW = 2; 1526 /** {@hide} */ 1527 @TestApi 1528 public static final String CACHE_RESERVE_PERCENT_LOW_KEY = "cache_reserve_percent_low"; 1529 1530 private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500); 1531 1532 private static final long DEFAULT_FULL_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(1); 1533 1534 /** 1535 * Return the number of available bytes until the given path is considered 1536 * running low on storage. 1537 * 1538 * @hide 1539 */ 1540 @UnsupportedAppUsage getStorageBytesUntilLow(File path)1541 public long getStorageBytesUntilLow(File path) { 1542 return path.getUsableSpace() - getStorageFullBytes(path); 1543 } 1544 1545 /** 1546 * Return the number of available bytes at which the given path is 1547 * considered running low on storage. 1548 * 1549 * @hide 1550 */ 1551 @UnsupportedAppUsage getStorageLowBytes(File path)1552 public long getStorageLowBytes(File path) { 1553 final long lowPercent = Settings.Global.getInt(mResolver, 1554 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, 1555 DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW); 1556 final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; 1557 1558 final long maxLowBytes = Settings.Global.getLong(mResolver, 1559 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); 1560 1561 return Math.min(lowBytes, maxLowBytes); 1562 } 1563 1564 /** 1565 * Compute the minimum number of bytes of storage on the device that could 1566 * be reserved for cached data depending on the device state which is then passed on 1567 * to getStorageCacheBytes. 1568 * 1569 * Input File path must point to a storage volume. 1570 * 1571 * @hide 1572 */ 1573 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) 1574 @TestApi 1575 @SuppressLint("StreamFiles") computeStorageCacheBytes(@onNull File path)1576 public long computeStorageCacheBytes(@NonNull File path) { 1577 final int storageThresholdPercentHigh = DeviceConfig.getInt( 1578 DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, 1579 STORAGE_THRESHOLD_PERCENT_HIGH_KEY, DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH); 1580 final int cacheReservePercentHigh = DeviceConfig.getInt( 1581 DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, 1582 CACHE_RESERVE_PERCENT_HIGH_KEY, DEFAULT_CACHE_RESERVE_PERCENT_HIGH); 1583 final int cacheReservePercentLow = DeviceConfig.getInt( 1584 DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, 1585 CACHE_RESERVE_PERCENT_LOW_KEY, DEFAULT_CACHE_RESERVE_PERCENT_LOW); 1586 final long totalBytes = path.getTotalSpace(); 1587 final long usableBytes = path.getUsableSpace(); 1588 final long storageThresholdHighBytes = totalBytes * storageThresholdPercentHigh / 100; 1589 final long storageThresholdLowBytes = getStorageLowBytes(path); 1590 long result; 1591 if (usableBytes > storageThresholdHighBytes) { 1592 // If free space is >storageThresholdPercentHigh of total space, 1593 // reserve cacheReservePercentHigh of total space 1594 result = totalBytes * cacheReservePercentHigh / 100; 1595 } else if (usableBytes < storageThresholdLowBytes) { 1596 // If free space is <min(storageThresholdPercentLow of total space, 500MB), 1597 // reserve cacheReservePercentLow of total space 1598 result = totalBytes * cacheReservePercentLow / 100; 1599 } else { 1600 // Else, linearly interpolate the amount of space to reserve 1601 double slope = (cacheReservePercentHigh - cacheReservePercentLow) * totalBytes 1602 / (100.0 * (storageThresholdHighBytes - storageThresholdLowBytes)); 1603 double intercept = totalBytes * cacheReservePercentLow / 100.0 1604 - storageThresholdLowBytes * slope; 1605 result = Math.round(slope * usableBytes + intercept); 1606 } 1607 return result; 1608 } 1609 1610 /** 1611 * Return the minimum number of bytes of storage on the device that should 1612 * be reserved for cached data. 1613 * 1614 * @hide 1615 */ getStorageCacheBytes(@onNull File path, @AllocateFlags int flags)1616 public long getStorageCacheBytes(@NonNull File path, @AllocateFlags int flags) { 1617 if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) { 1618 return 0; 1619 } else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED) != 0) { 1620 return 0; 1621 } else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED) != 0) { 1622 return computeStorageCacheBytes(path) / 2; 1623 } else { 1624 return computeStorageCacheBytes(path); 1625 } 1626 } 1627 1628 /** 1629 * Return the number of available bytes at which the given path is 1630 * considered full. 1631 * 1632 * @hide 1633 */ 1634 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getStorageFullBytes(File path)1635 public long getStorageFullBytes(File path) { 1636 return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, 1637 DEFAULT_FULL_THRESHOLD_BYTES); 1638 } 1639 1640 /** 1641 * Creates the keys for a user's credential-encrypted (CE) and device-encrypted (DE) storage. 1642 * <p> 1643 * This creates the user's CE key and DE key for internal storage, then adds them to the kernel. 1644 * Then, if the user is not ephemeral, this stores the DE key (encrypted) on flash. (The CE key 1645 * is not stored until {@link IStorageManager#setCeStorageProtection()}.) 1646 * <p> 1647 * This does not create the CE and DE directories themselves. For that, see {@link 1648 * #prepareUserStorage()}. 1649 * <p> 1650 * This is only intended to be called by UserManagerService, as part of creating a user. 1651 * 1652 * @param userId ID of the user 1653 * @param ephemeral whether the user is ephemeral 1654 * @throws RuntimeException on error. The user's keys already existing is considered an error. 1655 * @hide 1656 */ createUserStorageKeys(int userId, boolean ephemeral)1657 public void createUserStorageKeys(int userId, boolean ephemeral) { 1658 try { 1659 mStorageManager.createUserStorageKeys(userId, ephemeral); 1660 } catch (RemoteException e) { 1661 throw e.rethrowFromSystemServer(); 1662 } 1663 } 1664 1665 /** 1666 * Destroys the keys for a user's credential-encrypted (CE) and device-encrypted (DE) storage. 1667 * <p> 1668 * This evicts the keys from the kernel (if present), which "locks" the corresponding 1669 * directories. Then, this deletes the encrypted keys from flash. This operates on all the 1670 * user's CE and DE keys, for both internal and adoptable storage. 1671 * <p> 1672 * This does not destroy the CE and DE directories themselves. For that, see {@link 1673 * #destroyUserStorage()}. 1674 * <p> 1675 * This is only intended to be called by UserManagerService, as part of removing a user. 1676 * 1677 * @param userId ID of the user 1678 * @throws RuntimeException on error. On error, as many things as possible are still destroyed. 1679 * @hide 1680 */ destroyUserStorageKeys(int userId)1681 public void destroyUserStorageKeys(int userId) { 1682 try { 1683 mStorageManager.destroyUserStorageKeys(userId); 1684 } catch (RemoteException e) { 1685 throw e.rethrowFromSystemServer(); 1686 } 1687 } 1688 1689 /** 1690 * Locks the user's credential-encrypted (CE) storage. 1691 * 1692 * @hide 1693 */ lockCeStorage(int userId)1694 public void lockCeStorage(int userId) { 1695 try { 1696 mStorageManager.lockCeStorage(userId); 1697 } catch (RemoteException e) { 1698 throw e.rethrowFromSystemServer(); 1699 } 1700 } 1701 1702 /** {@hide} */ prepareUserStorage(String volumeUuid, int userId, int flags)1703 public void prepareUserStorage(String volumeUuid, int userId, int flags) { 1704 try { 1705 mStorageManager.prepareUserStorage(volumeUuid, userId, flags); 1706 } catch (RemoteException e) { 1707 throw e.rethrowFromSystemServer(); 1708 } 1709 } 1710 1711 /** {@hide} */ destroyUserStorage(String volumeUuid, int userId, int flags)1712 public void destroyUserStorage(String volumeUuid, int userId, int flags) { 1713 try { 1714 mStorageManager.destroyUserStorage(volumeUuid, userId, flags); 1715 } catch (RemoteException e) { 1716 throw e.rethrowFromSystemServer(); 1717 } 1718 } 1719 1720 /** 1721 * Returns true if the user's credential-encrypted (CE) storage is unlocked. 1722 * 1723 * @hide 1724 */ isCeStorageUnlocked(int userId)1725 public static boolean isCeStorageUnlocked(int userId) { 1726 if (sStorageManager == null) { 1727 sStorageManager = IStorageManager.Stub 1728 .asInterface(ServiceManager.getService("mount")); 1729 } 1730 if (sStorageManager == null) { 1731 Slog.w(TAG, "Early during boot, assuming CE storage is locked"); 1732 return false; 1733 } 1734 final long token = Binder.clearCallingIdentity(); 1735 try { 1736 return sStorageManager.isCeStorageUnlocked(userId); 1737 } catch (RemoteException e) { 1738 throw e.rethrowAsRuntimeException(); 1739 } finally { 1740 Binder.restoreCallingIdentity(token); 1741 } 1742 } 1743 1744 /** 1745 * Return if data stored at or under the given path will be encrypted while 1746 * at rest. This can help apps avoid the overhead of double-encrypting data. 1747 */ isEncrypted(File file)1748 public boolean isEncrypted(File file) { 1749 if (FileUtils.contains(Environment.getDataDirectory(), file)) { 1750 return isEncrypted(); 1751 } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) { 1752 return true; 1753 } 1754 // TODO: extend to support shared storage 1755 return false; 1756 } 1757 1758 /** 1759 * {@hide} 1760 * Is this device encrypted? 1761 * <p> 1762 * Note: all devices launching with Android 10 (API level 29) or later are 1763 * required to be encrypted. This should only ever return false for 1764 * in-development devices on which encryption has not yet been configured. 1765 * 1766 * @return true if encrypted, false if not encrypted 1767 */ isEncrypted()1768 public static boolean isEncrypted() { 1769 return RoSystemProperties.CRYPTO_ENCRYPTED; 1770 } 1771 1772 /** 1773 * {@hide} 1774 * Does this device have file-based encryption (FBE) enabled? 1775 * 1776 * @return true if the device has file-based encryption enabled. 1777 */ isFileEncrypted()1778 public static boolean isFileEncrypted() { 1779 if (!isEncrypted()) { 1780 return false; 1781 } 1782 return RoSystemProperties.CRYPTO_FILE_ENCRYPTED; 1783 } 1784 1785 /** {@hide} */ hasAdoptable()1786 public static boolean hasAdoptable() { 1787 switch (SystemProperties.get(PROP_ADOPTABLE)) { 1788 case "force_on": 1789 return true; 1790 case "force_off": 1791 return false; 1792 default: 1793 return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false); 1794 } 1795 } 1796 1797 /** 1798 * Return if the currently booted device has the "isolated storage" feature 1799 * flag enabled. 1800 * 1801 * @hide 1802 */ 1803 @SystemApi hasIsolatedStorage()1804 public static boolean hasIsolatedStorage() { 1805 return false; 1806 } 1807 1808 /** 1809 * @hide 1810 * @deprecated disabled now that FUSE has been replaced by sdcardfs 1811 */ 1812 @Deprecated maybeTranslateEmulatedPathToInternal(File path)1813 public static File maybeTranslateEmulatedPathToInternal(File path) { 1814 // Disabled now that FUSE has been replaced by sdcardfs 1815 return path; 1816 } 1817 1818 /** 1819 * Translate given shared storage path from a path in an app sandbox 1820 * namespace to a path in the system namespace. 1821 * 1822 * @hide 1823 */ translateAppToSystem(File file, int pid, int uid)1824 public File translateAppToSystem(File file, int pid, int uid) { 1825 return file; 1826 } 1827 1828 /** 1829 * Translate given shared storage path from a path in the system namespace 1830 * to a path in an app sandbox namespace. 1831 * 1832 * @hide 1833 */ translateSystemToApp(File file, int pid, int uid)1834 public File translateSystemToApp(File file, int pid, int uid) { 1835 return file; 1836 } 1837 1838 /** 1839 * Check that given app holds both permission and appop. 1840 * 1841 * @hide 1842 */ checkPermissionAndAppOp(Context context, boolean enforce, int pid, int uid, String packageName, @NonNull String featureId, String permission, int op)1843 public static boolean checkPermissionAndAppOp(Context context, boolean enforce, int pid, 1844 int uid, String packageName, @NonNull String featureId, String permission, int op) { 1845 return checkPermissionAndAppOp(context, enforce, pid, uid, packageName, featureId, 1846 permission, op, true); 1847 } 1848 1849 /** 1850 * Check that given app holds both permission and appop but do not noteOp. 1851 * 1852 * @hide 1853 */ checkPermissionAndCheckOp(Context context, boolean enforce, int pid, int uid, String packageName, String permission, int op)1854 public static boolean checkPermissionAndCheckOp(Context context, boolean enforce, 1855 int pid, int uid, String packageName, String permission, int op) { 1856 return checkPermissionAndAppOp(context, enforce, pid, uid, packageName, 1857 null /* featureId is not needed when not noting */, permission, op, false); 1858 } 1859 1860 /** 1861 * Check that given app holds both permission and appop. 1862 * 1863 * @hide 1864 */ checkPermissionAndAppOp(Context context, boolean enforce, int pid, int uid, String packageName, @Nullable String featureId, String permission, int op, boolean note)1865 private static boolean checkPermissionAndAppOp(Context context, boolean enforce, int pid, 1866 int uid, String packageName, @Nullable String featureId, String permission, int op, 1867 boolean note) { 1868 if (context.checkPermission(permission, pid, uid) != PERMISSION_GRANTED) { 1869 if (enforce) { 1870 throw new SecurityException( 1871 "Permission " + permission + " denied for package " + packageName); 1872 } else { 1873 return false; 1874 } 1875 } 1876 1877 AppOpsManager appOps = context.getSystemService(AppOpsManager.class); 1878 final int mode; 1879 if (note) { 1880 mode = appOps.noteOpNoThrow(op, uid, packageName, featureId, null); 1881 } else { 1882 try { 1883 appOps.checkPackage(uid, packageName); 1884 } catch (SecurityException e) { 1885 if (enforce) { 1886 throw e; 1887 } else { 1888 return false; 1889 } 1890 } 1891 mode = appOps.checkOpNoThrow(op, uid, packageName); 1892 } 1893 switch (mode) { 1894 case AppOpsManager.MODE_ALLOWED: 1895 return true; 1896 case AppOpsManager.MODE_DEFAULT: 1897 case AppOpsManager.MODE_IGNORED: 1898 case AppOpsManager.MODE_ERRORED: 1899 if (enforce) { 1900 throw new SecurityException("Op " + AppOpsManager.opToName(op) + " " 1901 + AppOpsManager.modeToName(mode) + " for package " + packageName); 1902 } else { 1903 return false; 1904 } 1905 default: 1906 throw new IllegalStateException( 1907 AppOpsManager.opToName(op) + " has unknown mode " 1908 + AppOpsManager.modeToName(mode)); 1909 } 1910 } 1911 checkPermissionAndAppOp(boolean enforce, int pid, int uid, String packageName, @Nullable String featureId, String permission, int op)1912 private boolean checkPermissionAndAppOp(boolean enforce, int pid, int uid, String packageName, 1913 @Nullable String featureId, String permission, int op) { 1914 return checkPermissionAndAppOp(mContext, enforce, pid, uid, packageName, featureId, 1915 permission, op); 1916 } 1917 noteAppOpAllowingLegacy(boolean enforce, int pid, int uid, String packageName, @Nullable String featureId, int op)1918 private boolean noteAppOpAllowingLegacy(boolean enforce, 1919 int pid, int uid, String packageName, @Nullable String featureId, int op) { 1920 final int mode = mAppOps.noteOpNoThrow(op, uid, packageName, featureId, null); 1921 switch (mode) { 1922 case AppOpsManager.MODE_ALLOWED: 1923 return true; 1924 case AppOpsManager.MODE_DEFAULT: 1925 case AppOpsManager.MODE_IGNORED: 1926 case AppOpsManager.MODE_ERRORED: 1927 // Legacy apps technically have the access granted by this op, 1928 // even when the op is denied 1929 if ((mAppOps.checkOpNoThrow(OP_LEGACY_STORAGE, uid, 1930 packageName) == AppOpsManager.MODE_ALLOWED)) { 1931 return true; 1932 } 1933 1934 if (enforce) { 1935 throw new SecurityException("Op " + AppOpsManager.opToName(op) + " " 1936 + AppOpsManager.modeToName(mode) + " for package " + packageName); 1937 } else { 1938 return false; 1939 } 1940 default: 1941 throw new IllegalStateException( 1942 AppOpsManager.opToName(op) + " has unknown mode " 1943 + AppOpsManager.modeToName(mode)); 1944 } 1945 } 1946 1947 // Callers must hold both the old and new permissions, so that we can 1948 // handle obscure cases like when an app targets Q but was installed on 1949 // a device that was originally running on P before being upgraded to Q. 1950 1951 /** 1952 * @deprecated This method should not be used since it check slegacy permissions, 1953 * no longer valid. Clients should check the appropriate permissions directly 1954 * instead (e.g. READ_MEDIA_IMAGES). 1955 * 1956 * {@hide} 1957 */ 1958 @Deprecated checkPermissionReadImages(boolean enforce, int pid, int uid, String packageName, @Nullable String featureId)1959 public boolean checkPermissionReadImages(boolean enforce, 1960 int pid, int uid, String packageName, @Nullable String featureId) { 1961 if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId, 1962 READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) { 1963 return false; 1964 } 1965 return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId, 1966 OP_READ_MEDIA_IMAGES); 1967 } 1968 checkExternalStoragePermissionAndAppOp(boolean enforce, int pid, int uid, String packageName, @Nullable String featureId, String permission, int op)1969 private boolean checkExternalStoragePermissionAndAppOp(boolean enforce, 1970 int pid, int uid, String packageName, @Nullable String featureId, String permission, 1971 int op) { 1972 // First check if app has MANAGE_EXTERNAL_STORAGE. 1973 final int mode = mAppOps.noteOpNoThrow(OP_MANAGE_EXTERNAL_STORAGE, uid, packageName, 1974 featureId, null); 1975 if (mode == AppOpsManager.MODE_ALLOWED) { 1976 return true; 1977 } 1978 if (mode == AppOpsManager.MODE_DEFAULT && mContext.checkPermission( 1979 MANAGE_EXTERNAL_STORAGE, pid, uid) == PERMISSION_GRANTED) { 1980 return true; 1981 } 1982 // If app doesn't have MANAGE_EXTERNAL_STORAGE, then check if it has requested granular 1983 // permission. 1984 return checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, permission, op); 1985 } 1986 1987 /** {@hide} */ 1988 @VisibleForTesting openProxyFileDescriptor( int mode, ProxyFileDescriptorCallback callback, Handler handler, ThreadFactory factory)1989 public @NonNull ParcelFileDescriptor openProxyFileDescriptor( 1990 int mode, ProxyFileDescriptorCallback callback, Handler handler, ThreadFactory factory) 1991 throws IOException { 1992 Preconditions.checkNotNull(callback); 1993 MetricsLogger.count(mContext, "storage_open_proxy_file_descriptor", 1); 1994 // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before 1995 // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount 1996 // the bridge by calling mountProxyFileDescriptorBridge. 1997 while (true) { 1998 try { 1999 synchronized (mFuseAppLoopLock) { 2000 boolean newlyCreated = false; 2001 if (mFuseAppLoop == null) { 2002 final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge(); 2003 if (mount == null) { 2004 throw new IOException("Failed to mount proxy bridge"); 2005 } 2006 mFuseAppLoop = new FuseAppLoop(mount.mountPointId, mount.fd, factory); 2007 newlyCreated = true; 2008 } 2009 if (handler == null) { 2010 handler = new Handler(Looper.getMainLooper()); 2011 } 2012 try { 2013 final int fileId = mFuseAppLoop.registerCallback(callback, handler); 2014 final ParcelFileDescriptor pfd = mStorageManager.openProxyFileDescriptor( 2015 mFuseAppLoop.getMountPointId(), fileId, mode); 2016 if (pfd == null) { 2017 mFuseAppLoop.unregisterCallback(fileId); 2018 throw new FuseUnavailableMountException( 2019 mFuseAppLoop.getMountPointId()); 2020 } 2021 return pfd; 2022 } catch (FuseUnavailableMountException exception) { 2023 // The bridge is being unmounted. Tried to recreate it unless the bridge was 2024 // just created. 2025 if (newlyCreated) { 2026 throw new IOException(exception); 2027 } 2028 mFuseAppLoop = null; 2029 continue; 2030 } 2031 } 2032 } catch (RemoteException e) { 2033 // Cannot recover from remote exception. 2034 throw new IOException(e); 2035 } 2036 } 2037 } 2038 2039 /** {@hide} */ openProxyFileDescriptor( int mode, ProxyFileDescriptorCallback callback)2040 public @NonNull ParcelFileDescriptor openProxyFileDescriptor( 2041 int mode, ProxyFileDescriptorCallback callback) 2042 throws IOException { 2043 return openProxyFileDescriptor(mode, callback, null, null); 2044 } 2045 2046 /** 2047 * Opens a seekable {@link ParcelFileDescriptor} that proxies all low-level 2048 * I/O requests back to the given {@link ProxyFileDescriptorCallback}. 2049 * <p> 2050 * This can be useful when you want to provide quick access to a large file 2051 * that isn't backed by a real file on disk, such as a file on a network 2052 * share, cloud storage service, etc. As an example, you could respond to a 2053 * {@link ContentResolver#openFileDescriptor(android.net.Uri, String)} 2054 * request by returning a {@link ParcelFileDescriptor} created with this 2055 * method, and then stream the content on-demand as requested. 2056 * <p> 2057 * Another useful example might be where you have an encrypted file that 2058 * you're willing to decrypt on-demand, but where you want to avoid 2059 * persisting the cleartext version. 2060 * 2061 * @param mode The desired access mode, must be one of 2062 * {@link ParcelFileDescriptor#MODE_READ_ONLY}, 2063 * {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or 2064 * {@link ParcelFileDescriptor#MODE_READ_WRITE} 2065 * @param callback Callback to process file operation requests issued on 2066 * returned file descriptor. 2067 * @param handler Handler that invokes callback methods. 2068 * @return Seekable ParcelFileDescriptor. 2069 */ openProxyFileDescriptor( int mode, ProxyFileDescriptorCallback callback, Handler handler)2070 public @NonNull ParcelFileDescriptor openProxyFileDescriptor( 2071 int mode, ProxyFileDescriptorCallback callback, Handler handler) 2072 throws IOException { 2073 Preconditions.checkNotNull(handler); 2074 return openProxyFileDescriptor(mode, callback, handler, null); 2075 } 2076 2077 /** {@hide} */ 2078 @VisibleForTesting getProxyFileDescriptorMountPointId()2079 public int getProxyFileDescriptorMountPointId() { 2080 synchronized (mFuseAppLoopLock) { 2081 return mFuseAppLoop != null ? mFuseAppLoop.getMountPointId() : -1; 2082 } 2083 } 2084 2085 /** 2086 * Return quota size in bytes for all cached data belonging to the calling 2087 * app on the given storage volume. 2088 * <p> 2089 * If your app goes above this quota, your cached files will be some of the 2090 * first to be deleted when additional disk space is needed. Conversely, if 2091 * your app stays under this quota, your cached files will be some of the 2092 * last to be deleted when additional disk space is needed. 2093 * <p> 2094 * This quota will change over time depending on how frequently the user 2095 * interacts with your app, and depending on how much system-wide disk space 2096 * is used. 2097 * <p class="note"> 2098 * Note: if your app uses the {@code android:sharedUserId} manifest feature, 2099 * then cached data for all packages in your shared UID is tracked together 2100 * as a single unit. 2101 * </p> 2102 * 2103 * @param storageUuid the UUID of the storage volume that you're interested 2104 * in. The UUID for a specific path can be obtained using 2105 * {@link #getUuidForPath(File)}. 2106 * @throws IOException when the storage device isn't present, or when it 2107 * doesn't support cache quotas. 2108 * @see #getCacheSizeBytes(UUID) 2109 */ 2110 @WorkerThread getCacheQuotaBytes(@onNull UUID storageUuid)2111 public @BytesLong long getCacheQuotaBytes(@NonNull UUID storageUuid) throws IOException { 2112 try { 2113 final ApplicationInfo app = mContext.getApplicationInfo(); 2114 return mStorageManager.getCacheQuotaBytes(convert(storageUuid), app.uid); 2115 } catch (ParcelableException e) { 2116 e.maybeRethrow(IOException.class); 2117 throw new RuntimeException(e); 2118 } catch (RemoteException e) { 2119 throw e.rethrowFromSystemServer(); 2120 } 2121 } 2122 2123 /** 2124 * Return total size in bytes of all cached data belonging to the calling 2125 * app on the given storage volume. 2126 * <p> 2127 * Cached data tracked by this method always includes 2128 * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and 2129 * it also includes {@link Context#getExternalCacheDir()} if the primary 2130 * shared/external storage is hosted on the same storage device as your 2131 * private data. 2132 * <p class="note"> 2133 * Note: if your app uses the {@code android:sharedUserId} manifest feature, 2134 * then cached data for all packages in your shared UID is tracked together 2135 * as a single unit. 2136 * </p> 2137 * 2138 * @param storageUuid the UUID of the storage volume that you're interested 2139 * in. The UUID for a specific path can be obtained using 2140 * {@link #getUuidForPath(File)}. 2141 * @throws IOException when the storage device isn't present, or when it 2142 * doesn't support cache quotas. 2143 * @see #getCacheQuotaBytes(UUID) 2144 */ 2145 @WorkerThread getCacheSizeBytes(@onNull UUID storageUuid)2146 public @BytesLong long getCacheSizeBytes(@NonNull UUID storageUuid) throws IOException { 2147 try { 2148 final ApplicationInfo app = mContext.getApplicationInfo(); 2149 return mStorageManager.getCacheSizeBytes(convert(storageUuid), app.uid); 2150 } catch (ParcelableException e) { 2151 e.maybeRethrow(IOException.class); 2152 throw new RuntimeException(e); 2153 } catch (RemoteException e) { 2154 throw e.rethrowFromSystemServer(); 2155 } 2156 } 2157 2158 2159 /** @hide */ 2160 @IntDef(prefix = {"MOUNT_MODE_"}, value = { 2161 MOUNT_MODE_EXTERNAL_NONE, 2162 MOUNT_MODE_EXTERNAL_DEFAULT, 2163 MOUNT_MODE_EXTERNAL_INSTALLER, 2164 MOUNT_MODE_EXTERNAL_PASS_THROUGH, 2165 MOUNT_MODE_EXTERNAL_ANDROID_WRITABLE 2166 }) 2167 @Retention(RetentionPolicy.SOURCE) 2168 /** @hide */ 2169 public @interface MountMode { 2170 } 2171 2172 /** 2173 * No external storage should be mounted. 2174 * 2175 * @hide 2176 */ 2177 @SystemApi 2178 public static final int MOUNT_MODE_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; 2179 /** 2180 * Default external storage should be mounted. 2181 * 2182 * @hide 2183 */ 2184 @SystemApi 2185 public static final int MOUNT_MODE_EXTERNAL_DEFAULT = IVold.REMOUNT_MODE_DEFAULT; 2186 /** 2187 * Mount mode for package installers which should give them access to 2188 * all obb dirs in addition to their package sandboxes 2189 * 2190 * @hide 2191 */ 2192 @SystemApi 2193 public static final int MOUNT_MODE_EXTERNAL_INSTALLER = IVold.REMOUNT_MODE_INSTALLER; 2194 /** 2195 * The lower file system should be bind mounted directly on external storage 2196 * 2197 * @hide 2198 */ 2199 @SystemApi 2200 public static final int MOUNT_MODE_EXTERNAL_PASS_THROUGH = IVold.REMOUNT_MODE_PASS_THROUGH; 2201 2202 /** 2203 * Use the regular scoped storage filesystem, but Android/ should be writable. 2204 * Used to support the applications hosting DownloadManager and the MTP server. 2205 * 2206 * @hide 2207 */ 2208 @SystemApi 2209 public static final int MOUNT_MODE_EXTERNAL_ANDROID_WRITABLE = 2210 IVold.REMOUNT_MODE_ANDROID_WRITABLE; 2211 /** 2212 * Flag indicating that a disk space allocation request should operate in an 2213 * aggressive mode. This flag should only be rarely used in situations that 2214 * are critical to system health or security. 2215 * <p> 2216 * When set, the system is more aggressive about the data that it considers 2217 * for possible deletion when allocating disk space. 2218 * <p class="note"> 2219 * Note: your app must hold the 2220 * {@link android.Manifest.permission#ALLOCATE_AGGRESSIVE} permission for 2221 * this flag to take effect. 2222 * </p> 2223 * 2224 * @hide 2225 * @see #getAllocatableBytes(UUID, int) 2226 * @see #allocateBytes(UUID, long, int) 2227 * @see #allocateBytes(FileDescriptor, long, int) 2228 */ 2229 @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) 2230 @SystemApi 2231 public static final int FLAG_ALLOCATE_AGGRESSIVE = 1 << 0; 2232 2233 /** 2234 * Flag indicating that a disk space allocation request should be allowed to 2235 * clear up to all reserved disk space. 2236 * 2237 * @hide 2238 */ 2239 public static final int FLAG_ALLOCATE_DEFY_ALL_RESERVED = 1 << 1; 2240 2241 /** 2242 * Flag indicating that a disk space allocation request should be allowed to 2243 * clear up to half of all reserved disk space. 2244 * 2245 * @hide 2246 */ 2247 public static final int FLAG_ALLOCATE_DEFY_HALF_RESERVED = 1 << 2; 2248 2249 /** 2250 * Flag indicating that a disk space check should not take into account 2251 * freeable cached space when determining allocatable space. 2252 * 2253 * Intended for use with {@link #getAllocatableBytes()}. 2254 * 2255 * @hide 2256 */ 2257 public static final int FLAG_ALLOCATE_NON_CACHE_ONLY = 1 << 3; 2258 2259 /** 2260 * Flag indicating that a disk space check should only return freeable 2261 * cached space when determining allocatable space. 2262 * 2263 * Intended for use with {@link #getAllocatableBytes()}. 2264 * 2265 * @hide 2266 */ 2267 public static final int FLAG_ALLOCATE_CACHE_ONLY = 1 << 4; 2268 2269 /** @hide */ 2270 @IntDef(flag = true, prefix = {"FLAG_ALLOCATE_"}, value = { 2271 FLAG_ALLOCATE_AGGRESSIVE, 2272 FLAG_ALLOCATE_DEFY_ALL_RESERVED, 2273 FLAG_ALLOCATE_DEFY_HALF_RESERVED, 2274 FLAG_ALLOCATE_NON_CACHE_ONLY, 2275 FLAG_ALLOCATE_CACHE_ONLY, 2276 }) 2277 @Retention(RetentionPolicy.SOURCE) 2278 public @interface AllocateFlags { 2279 } 2280 2281 /** 2282 * Return the maximum number of new bytes that your app can allocate for 2283 * itself on the given storage volume. This value is typically larger than 2284 * {@link File#getUsableSpace()}, since the system may be willing to delete 2285 * cached files to satisfy an allocation request. You can then allocate 2286 * space for yourself using {@link #allocateBytes(UUID, long)} or 2287 * {@link #allocateBytes(FileDescriptor, long)}. 2288 * <p> 2289 * This method is best used as a pre-flight check, such as deciding if there 2290 * is enough space to store an entire music album before you allocate space 2291 * for each audio file in the album. Attempts to allocate disk space beyond 2292 * the returned value will fail. 2293 * <p> 2294 * If the returned value is not large enough for the data you'd like to 2295 * persist, you can launch {@link #ACTION_MANAGE_STORAGE} with the 2296 * {@link #EXTRA_UUID} and {@link #EXTRA_REQUESTED_BYTES} options to help 2297 * involve the user in freeing up disk space. 2298 * <p> 2299 * If you're progressively allocating an unbounded amount of storage space 2300 * (such as when recording a video) you should avoid calling this method 2301 * more than once every 30 seconds. 2302 * <p class="note"> 2303 * Note: if your app uses the {@code android:sharedUserId} manifest feature, 2304 * then allocatable space for all packages in your shared UID is tracked 2305 * together as a single unit. 2306 * </p> 2307 * 2308 * @param storageUuid the UUID of the storage volume where you're 2309 * considering allocating disk space, since allocatable space can 2310 * vary widely depending on the underlying storage device. The 2311 * UUID for a specific path can be obtained using 2312 * {@link #getUuidForPath(File)}. 2313 * @return the maximum number of new bytes that the calling app can allocate 2314 * using {@link #allocateBytes(UUID, long)} or 2315 * {@link #allocateBytes(FileDescriptor, long)}. 2316 * @throws IOException when the storage device isn't present, or when it 2317 * doesn't support allocating space. 2318 */ 2319 @WorkerThread getAllocatableBytes(@onNull UUID storageUuid)2320 public @BytesLong long getAllocatableBytes(@NonNull UUID storageUuid) 2321 throws IOException { 2322 return getAllocatableBytes(storageUuid, 0); 2323 } 2324 2325 /** @hide */ 2326 @SystemApi 2327 @WorkerThread 2328 @SuppressLint("RequiresPermission") getAllocatableBytes(@onNull UUID storageUuid, @RequiresPermission @AllocateFlags int flags)2329 public long getAllocatableBytes(@NonNull UUID storageUuid, 2330 @RequiresPermission @AllocateFlags int flags) throws IOException { 2331 try { 2332 return mStorageManager.getAllocatableBytes(convert(storageUuid), flags, 2333 mContext.getOpPackageName()); 2334 } catch (ParcelableException e) { 2335 e.maybeRethrow(IOException.class); 2336 throw new RuntimeException(e); 2337 } catch (RemoteException e) { 2338 throw e.rethrowFromSystemServer(); 2339 } 2340 } 2341 2342 /** 2343 * Allocate the requested number of bytes for your application to use on the 2344 * given storage volume. This will cause the system to delete any cached 2345 * files necessary to satisfy your request. 2346 * <p> 2347 * Attempts to allocate disk space beyond the value returned by 2348 * {@link #getAllocatableBytes(UUID)} will fail. 2349 * <p> 2350 * Since multiple apps can be running simultaneously, this method may be 2351 * subject to race conditions. If possible, consider using 2352 * {@link #allocateBytes(FileDescriptor, long)} which will guarantee 2353 * that bytes are allocated to an opened file. 2354 * <p> 2355 * If you're progressively allocating an unbounded amount of storage space 2356 * (such as when recording a video) you should avoid calling this method 2357 * more than once every 60 seconds. 2358 * 2359 * @param storageUuid the UUID of the storage volume where you'd like to 2360 * allocate disk space. The UUID for a specific path can be 2361 * obtained using {@link #getUuidForPath(File)}. 2362 * @param bytes the number of bytes to allocate. 2363 * @throws IOException when the storage device isn't present, or when it 2364 * doesn't support allocating space, or if the device had 2365 * trouble allocating the requested space. 2366 * @see #getAllocatableBytes(UUID) 2367 */ 2368 @WorkerThread allocateBytes(@onNull UUID storageUuid, @BytesLong long bytes)2369 public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes) 2370 throws IOException { 2371 allocateBytes(storageUuid, bytes, 0); 2372 } 2373 2374 /** @hide */ 2375 @SystemApi 2376 @WorkerThread 2377 @SuppressLint("RequiresPermission") allocateBytes(@onNull UUID storageUuid, @BytesLong long bytes, @RequiresPermission @AllocateFlags int flags)2378 public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes, 2379 @RequiresPermission @AllocateFlags int flags) throws IOException { 2380 try { 2381 mStorageManager.allocateBytes(convert(storageUuid), bytes, flags, 2382 mContext.getOpPackageName()); 2383 } catch (ParcelableException e) { 2384 e.maybeRethrow(IOException.class); 2385 } catch (RemoteException e) { 2386 throw e.rethrowFromSystemServer(); 2387 } 2388 } 2389 2390 /** 2391 * Returns the External Storage mount mode corresponding to the given uid and packageName. 2392 * These mount modes specify different views and access levels for 2393 * different apps on external storage. 2394 * 2395 * @return {@code MountMode} for the given uid and packageName. 2396 * @params uid UID of the application 2397 * @params packageName name of the package 2398 * @hide 2399 */ 2400 @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) 2401 @SystemApi 2402 @MountMode getExternalStorageMountMode(int uid, @NonNull String packageName)2403 public int getExternalStorageMountMode(int uid, @NonNull String packageName) { 2404 try { 2405 return mStorageManager.getExternalStorageMountMode(uid, packageName); 2406 } catch (RemoteException e) { 2407 throw e.rethrowFromSystemServer(); 2408 } 2409 } 2410 2411 /** 2412 * Allocate the requested number of bytes for your application to use in the 2413 * given open file. This will cause the system to delete any cached files 2414 * necessary to satisfy your request. 2415 * <p> 2416 * Attempts to allocate disk space beyond the value returned by 2417 * {@link #getAllocatableBytes(UUID)} will fail. 2418 * <p> 2419 * This method guarantees that bytes have been allocated to the opened file, 2420 * otherwise it will throw if fast allocation is not possible. Fast 2421 * allocation is typically only supported in private app data directories, 2422 * and on shared/external storage devices which are emulated. 2423 * <p> 2424 * If you're progressively allocating an unbounded amount of storage space 2425 * (such as when recording a video) you should avoid calling this method 2426 * more than once every 60 seconds. 2427 * 2428 * @param fd the open file that you'd like to allocate disk space for. 2429 * @param bytes the number of bytes to allocate. This is the desired final 2430 * size of the open file. If the open file is smaller than this 2431 * requested size, it will be extended without modifying any 2432 * existing contents. If the open file is larger than this 2433 * requested size, it will be truncated. 2434 * @throws IOException when the storage device isn't present, or when it 2435 * doesn't support allocating space, or if the device had 2436 * trouble allocating the requested space. 2437 * @see #isAllocationSupported(FileDescriptor) 2438 * @see Environment#isExternalStorageEmulated(File) 2439 */ 2440 @WorkerThread allocateBytes(FileDescriptor fd, @BytesLong long bytes)2441 public void allocateBytes(FileDescriptor fd, @BytesLong long bytes) throws IOException { 2442 allocateBytes(fd, bytes, 0); 2443 } 2444 2445 /** @hide */ 2446 @SystemApi 2447 @WorkerThread 2448 @SuppressLint("RequiresPermission") allocateBytes(FileDescriptor fd, @BytesLong long bytes, @RequiresPermission @AllocateFlags int flags)2449 public void allocateBytes(FileDescriptor fd, @BytesLong long bytes, 2450 @RequiresPermission @AllocateFlags int flags) throws IOException { 2451 final File file = ParcelFileDescriptor.getFile(fd); 2452 final UUID uuid = getUuidForPath(file); 2453 for (int i = 0; i < 3; i++) { 2454 try { 2455 final long haveBytes = Os.fstat(fd).st_blocks * 512; 2456 final long needBytes = bytes - haveBytes; 2457 2458 if (needBytes > 0) { 2459 allocateBytes(uuid, needBytes, flags); 2460 } 2461 2462 try { 2463 Os.posix_fallocate(fd, 0, bytes); 2464 return; 2465 } catch (ErrnoException e) { 2466 if (e.errno == OsConstants.ENOSYS || e.errno == OsConstants.ENOTSUP) { 2467 Log.w(TAG, "fallocate() not supported; falling back to ftruncate()"); 2468 Os.ftruncate(fd, bytes); 2469 return; 2470 } else { 2471 throw e; 2472 } 2473 } 2474 } catch (ErrnoException e) { 2475 if (e.errno == OsConstants.ENOSPC) { 2476 Log.w(TAG, "Odd, not enough space; let's try again?"); 2477 continue; 2478 } 2479 throw e.rethrowAsIOException(); 2480 } 2481 } 2482 throw new IOException( 2483 "Well this is embarassing; we can't allocate " + bytes + " for " + file); 2484 } 2485 2486 private static final String XATTR_CACHE_GROUP = "user.cache_group"; 2487 private static final String XATTR_CACHE_TOMBSTONE = "user.cache_tombstone"; 2488 2489 2490 // Project IDs below must match android_projectid_config.h 2491 /** 2492 * Default project ID for files on external storage 2493 * 2494 * {@hide} 2495 */ 2496 public static final int PROJECT_ID_EXT_DEFAULT = 1000; 2497 2498 /** 2499 * project ID for audio files on external storage 2500 * 2501 * {@hide} 2502 */ 2503 public static final int PROJECT_ID_EXT_MEDIA_AUDIO = 1001; 2504 2505 /** 2506 * project ID for video files on external storage 2507 * 2508 * {@hide} 2509 */ 2510 public static final int PROJECT_ID_EXT_MEDIA_VIDEO = 1002; 2511 2512 /** 2513 * project ID for image files on external storage 2514 * 2515 * {@hide} 2516 */ 2517 public static final int PROJECT_ID_EXT_MEDIA_IMAGE = 1003; 2518 2519 /** 2520 * Constant for use with 2521 * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file 2522 * is not a media file. 2523 * 2524 * @hide 2525 */ 2526 @SystemApi 2527 public static final int QUOTA_TYPE_MEDIA_NONE = 0; 2528 2529 /** 2530 * Constant for use with 2531 * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file 2532 * is an image file. 2533 * 2534 * @hide 2535 */ 2536 @SystemApi 2537 public static final int QUOTA_TYPE_MEDIA_IMAGE = 1; 2538 2539 /** 2540 * Constant for use with 2541 * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file 2542 * is an audio file. 2543 * 2544 * @hide 2545 */ 2546 @SystemApi 2547 public static final int QUOTA_TYPE_MEDIA_AUDIO = 2; 2548 2549 /** 2550 * Constant for use with 2551 * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file 2552 * is a video file. 2553 * 2554 * @hide 2555 */ 2556 @SystemApi 2557 public static final int QUOTA_TYPE_MEDIA_VIDEO = 3; 2558 2559 /** @hide */ 2560 @Retention(RetentionPolicy.SOURCE) 2561 @IntDef(prefix = {"QUOTA_TYPE_"}, value = { 2562 QUOTA_TYPE_MEDIA_NONE, 2563 QUOTA_TYPE_MEDIA_AUDIO, 2564 QUOTA_TYPE_MEDIA_VIDEO, 2565 QUOTA_TYPE_MEDIA_IMAGE, 2566 }) 2567 public @interface QuotaType { 2568 } 2569 setQuotaProjectId(String path, long projectId)2570 private static native boolean setQuotaProjectId(String path, long projectId); 2571 getProjectIdForUser(int userId, int projectId)2572 private static long getProjectIdForUser(int userId, int projectId) { 2573 // Much like UserHandle.getUid(), store the user ID in the upper bits 2574 return userId * PER_USER_RANGE + projectId; 2575 } 2576 2577 private static final Pattern PATTERN_USER_ID = Pattern.compile( 2578 "(?i)^/storage/emulated/([0-9]+)"); 2579 2580 /** 2581 * Let StorageManager know that the quota type for a file on external storage should 2582 * be updated. Android tracks quotas for various media types. Consequently, this should be 2583 * called on first creation of a new file on external storage, and whenever the 2584 * media type of the file is updated later. 2585 * 2586 * This API doesn't require any special permissions, though typical implementations 2587 * will require being called from an SELinux domain that allows setting file attributes 2588 * related to quota (eg the GID or project ID). 2589 * If the calling user has MANAGE_EXTERNAL_STORAGE permissions, quota for shared profile's 2590 * volumes is also updated. 2591 * 2592 * The default platform user of this API is the MediaProvider process, which is 2593 * responsible for managing all of external storage. 2594 * 2595 * @param path the path to the file for which we should update the quota type 2596 * @param quotaType the quota type of the file; this is based on the 2597 * {@code QuotaType} constants, eg 2598 * {@code StorageManager.QUOTA_TYPE_MEDIA_AUDIO} 2599 * @throws IllegalArgumentException if {@code quotaType} does not correspond to a valid 2600 * quota type. 2601 * @throws IOException if the quota type could not be updated. 2602 * @hide 2603 */ 2604 @SystemApi updateExternalStorageFileQuotaType(@onNull File path, @QuotaType int quotaType)2605 public void updateExternalStorageFileQuotaType(@NonNull File path, 2606 @QuotaType int quotaType) throws IOException { 2607 if (!path.exists()) return; 2608 2609 long projectId; 2610 final String filePath = path.getCanonicalPath(); 2611 2612 final int userId; 2613 final Matcher matcher = PATTERN_USER_ID.matcher(filePath); 2614 if (matcher.find()) { 2615 userId = Integer.parseInt(matcher.group(1)); 2616 } else { // fallback 2617 int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE; 2618 // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are 2619 // also returned by enabling FLAG_INCLUDE_SHARED_PROFILE. 2620 if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) { 2621 volFlags |= FLAG_INCLUDE_SHARED_PROFILE; 2622 } 2623 final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags); 2624 final StorageVolume volume = getStorageVolume(availableVolumes, path); 2625 if (volume == null) { 2626 Log.w(TAG, "Failed to update quota type for " + filePath); 2627 return; 2628 } 2629 if (!volume.isEmulated()) { 2630 // We only support quota tracking on emulated filesystems 2631 return; 2632 } 2633 userId = volume.getOwner().getIdentifier(); 2634 } 2635 2636 if (userId < 0) { 2637 throw new IllegalStateException("Failed to update quota type for " + filePath); 2638 } 2639 switch (quotaType) { 2640 case QUOTA_TYPE_MEDIA_NONE: 2641 projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_DEFAULT); 2642 break; 2643 case QUOTA_TYPE_MEDIA_AUDIO: 2644 projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_AUDIO); 2645 break; 2646 case QUOTA_TYPE_MEDIA_VIDEO: 2647 projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_VIDEO); 2648 break; 2649 case QUOTA_TYPE_MEDIA_IMAGE: 2650 projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_IMAGE); 2651 break; 2652 default: 2653 throw new IllegalArgumentException("Invalid quota type: " + quotaType); 2654 } 2655 if (!setQuotaProjectId(filePath, projectId)) { 2656 throw new IOException("Failed to update quota type for " + filePath); 2657 } 2658 } 2659 2660 /** 2661 * Asks StorageManager to fixup the permissions of an application-private directory. 2662 * 2663 * On devices without sdcardfs, filesystem permissions aren't magically fixed up. This 2664 * is problematic mostly in application-private directories, which are owned by the 2665 * application itself; if another process with elevated permissions creates a file 2666 * in these directories, the UID will be wrong, and the owning package won't be able 2667 * to access the files. 2668 * 2669 * This API can be used to recursively fix up the permissions on the passed in path. 2670 * The default platform user of this API is the DownloadProvider, which can download 2671 * things in application-private directories on their behalf. 2672 * 2673 * This API doesn't require any special permissions, because it merely changes the 2674 * permissions of a directory to what they should anyway be. 2675 * 2676 * @param path the path for which we should fix up the permissions 2677 * @hide 2678 */ fixupAppDir(@onNull File path)2679 public void fixupAppDir(@NonNull File path) { 2680 try { 2681 mStorageManager.fixupAppDir(path.getCanonicalPath()); 2682 } catch (IOException e) { 2683 Log.e(TAG, "Failed to get canonical path for " + path.getPath(), e); 2684 } catch (RemoteException e) { 2685 throw e.rethrowFromSystemServer(); 2686 } 2687 } 2688 2689 /** {@hide} */ setCacheBehavior(File path, String name, boolean enabled)2690 private static void setCacheBehavior(File path, String name, boolean enabled) 2691 throws IOException { 2692 if (!path.isDirectory()) { 2693 throw new IOException("Cache behavior can only be set on directories"); 2694 } 2695 if (enabled) { 2696 try { 2697 Os.setxattr(path.getAbsolutePath(), name, 2698 "1".getBytes(StandardCharsets.UTF_8), 0); 2699 } catch (ErrnoException e) { 2700 throw e.rethrowAsIOException(); 2701 } 2702 } else { 2703 try { 2704 Os.removexattr(path.getAbsolutePath(), name); 2705 } catch (ErrnoException e) { 2706 if (e.errno != OsConstants.ENODATA) { 2707 throw e.rethrowAsIOException(); 2708 } 2709 } 2710 } 2711 } 2712 2713 /** {@hide} */ isCacheBehavior(File path, String name)2714 private static boolean isCacheBehavior(File path, String name) throws IOException { 2715 try { 2716 Os.getxattr(path.getAbsolutePath(), name); 2717 return true; 2718 } catch (ErrnoException e) { 2719 if (e.errno != OsConstants.ENODATA) { 2720 throw e.rethrowAsIOException(); 2721 } else { 2722 return false; 2723 } 2724 } 2725 } 2726 2727 /** 2728 * Enable or disable special cache behavior that treats this directory and 2729 * its contents as an entire group. 2730 * <p> 2731 * When enabled and this directory is considered for automatic deletion by 2732 * the OS, all contained files will either be deleted together, or not at 2733 * all. This is useful when you have a directory that contains several 2734 * related metadata files that depend on each other, such as movie file and 2735 * a subtitle file. 2736 * <p> 2737 * When enabled, the <em>newest</em> {@link File#lastModified()} value of 2738 * any contained files is considered the modified time of the entire 2739 * directory. 2740 * <p> 2741 * This behavior can only be set on a directory, and it applies recursively 2742 * to all contained files and directories. 2743 */ setCacheBehaviorGroup(File path, boolean group)2744 public void setCacheBehaviorGroup(File path, boolean group) throws IOException { 2745 setCacheBehavior(path, XATTR_CACHE_GROUP, group); 2746 } 2747 2748 /** 2749 * Read the current value set by 2750 * {@link #setCacheBehaviorGroup(File, boolean)}. 2751 */ isCacheBehaviorGroup(File path)2752 public boolean isCacheBehaviorGroup(File path) throws IOException { 2753 return isCacheBehavior(path, XATTR_CACHE_GROUP); 2754 } 2755 2756 /** 2757 * Enable or disable special cache behavior that leaves deleted cache files 2758 * intact as tombstones. 2759 * <p> 2760 * When enabled and a file contained in this directory is automatically 2761 * deleted by the OS, the file will be truncated to have a length of 0 bytes 2762 * instead of being fully deleted. This is useful if you need to distinguish 2763 * between a file that was deleted versus one that never existed. 2764 * <p> 2765 * This behavior can only be set on a directory, and it applies recursively 2766 * to all contained files and directories. 2767 * <p class="note"> 2768 * Note: this behavior is ignored completely if the user explicitly requests 2769 * that all cached data be cleared. 2770 * </p> 2771 */ setCacheBehaviorTombstone(File path, boolean tombstone)2772 public void setCacheBehaviorTombstone(File path, boolean tombstone) throws IOException { 2773 setCacheBehavior(path, XATTR_CACHE_TOMBSTONE, tombstone); 2774 } 2775 2776 /** 2777 * Read the current value set by 2778 * {@link #setCacheBehaviorTombstone(File, boolean)}. 2779 */ isCacheBehaviorTombstone(File path)2780 public boolean isCacheBehaviorTombstone(File path) throws IOException { 2781 return isCacheBehavior(path, XATTR_CACHE_TOMBSTONE); 2782 } 2783 2784 /** 2785 * Returns true if {@code uuid} is a FAT volume identifier. FAT Volume identifiers 2786 * are 32 randomly generated bits that are represented in string form as AAAA-AAAA. 2787 */ isFatVolumeIdentifier(String uuid)2788 private static boolean isFatVolumeIdentifier(String uuid) { 2789 return uuid.length() == 9 && uuid.charAt(4) == '-'; 2790 } 2791 2792 /** {@hide} */ 2793 @TestApi convert(@ullable String uuid)2794 public static @NonNull UUID convert(@Nullable String uuid) { 2795 // UUID_PRIVATE_INTERNAL is null, so this accepts nullable input 2796 if (Objects.equals(uuid, UUID_PRIVATE_INTERNAL)) { 2797 return UUID_DEFAULT; 2798 } else if (Objects.equals(uuid, UUID_PRIMARY_PHYSICAL)) { 2799 return UUID_PRIMARY_PHYSICAL_; 2800 } else if (Objects.equals(uuid, UUID_SYSTEM)) { 2801 return UUID_SYSTEM_; 2802 } else if (isFatVolumeIdentifier(uuid)) { 2803 // FAT volume identifiers are not UUIDs but we need to coerce them into 2804 // UUIDs in order to satisfy apis that take java.util.UUID arguments. 2805 // 2806 // We coerce a 32 bit fat volume identifier of the form XXXX-YYYY into 2807 // a UUID of form "fafafafa-fafa-5afa-8afa-fafaXXXXYYYY". This is an 2808 // RFC-422 UUID with Version 5, which is a namespaced UUID. The UUIDs we 2809 // coerce into are not true namespace UUIDs; although FAT storage volume 2810 // identifiers are unique names within a fixed namespace, this UUID is not 2811 // based on an SHA-1 hash of the name. We avoid the SHA-1 hash because 2812 // (a) we need this transform to be reversible (b) it's pointless to generate 2813 // a 128 bit hash from a 32 bit value. 2814 return UUID.fromString(FAT_UUID_PREFIX + uuid.replace("-", "")); 2815 } else { 2816 return UUID.fromString(uuid); 2817 } 2818 } 2819 2820 /** {@hide} */ 2821 @TestApi convert(@onNull UUID storageUuid)2822 public static @NonNull String convert(@NonNull UUID storageUuid) { 2823 if (UUID_DEFAULT.equals(storageUuid)) { 2824 return UUID_PRIVATE_INTERNAL; 2825 } else if (UUID_PRIMARY_PHYSICAL_.equals(storageUuid)) { 2826 return UUID_PRIMARY_PHYSICAL; 2827 } else if (UUID_SYSTEM_.equals(storageUuid)) { 2828 return UUID_SYSTEM; 2829 } else { 2830 String uuidString = storageUuid.toString(); 2831 // This prefix match will exclude fsUuids from private volumes because 2832 // (a) linux fsUuids are generally Version 4 (random) UUIDs so the prefix 2833 // will contain 4xxx instead of 5xxx and (b) we've already matched against 2834 // known namespace (Version 5) UUIDs above. 2835 if (uuidString.startsWith(FAT_UUID_PREFIX)) { 2836 String fatStr = uuidString.substring(FAT_UUID_PREFIX.length()) 2837 .toUpperCase(Locale.US); 2838 return fatStr.substring(0, 4) + "-" + fatStr.substring(4); 2839 } 2840 2841 return storageUuid.toString(); 2842 } 2843 } 2844 2845 /** 2846 * Check whether the device supports filesystem checkpoint. 2847 * 2848 * @return true if the device supports filesystem checkpoint, false otherwise. 2849 */ isCheckpointSupported()2850 public boolean isCheckpointSupported() { 2851 try { 2852 return mStorageManager.supportsCheckpoint(); 2853 } catch (RemoteException e) { 2854 throw e.rethrowFromSystemServer(); 2855 } 2856 } 2857 2858 /** 2859 * Reason to provide if app IO is blocked/resumed for unknown reasons 2860 * 2861 * @hide 2862 */ 2863 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) 2864 public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0; 2865 2866 /** 2867 * Reason to provide if app IO is blocked/resumed because of transcoding 2868 * 2869 * @hide 2870 */ 2871 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) 2872 public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1; 2873 2874 /** 2875 * Constants for use with 2876 * {@link #notifyAppIoBlocked} and {@link notifyAppIoResumed}, to specify the reason an app's 2877 * IO is blocked/resumed. 2878 * 2879 * @hide 2880 */ 2881 @Retention(RetentionPolicy.SOURCE) 2882 @IntDef(prefix = {"APP_IO_BLOCKED_REASON_"}, value = { 2883 APP_IO_BLOCKED_REASON_TRANSCODING, 2884 APP_IO_BLOCKED_REASON_UNKNOWN, 2885 }) 2886 public @interface AppIoBlockedReason { 2887 } 2888 2889 /** 2890 * Notify the system that an app with {@code uid} and {@code tid} is blocked on an IO request on 2891 * {@code volumeUuid} for {@code reason}. 2892 * 2893 * This blocked state can be used to modify the ANR behavior for the app while it's blocked. 2894 * For example during transcoding. 2895 * 2896 * This can only be called by the {@link ExternalStorageService} holding the 2897 * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission. 2898 * 2899 * @param volumeUuid the UUID of the storage volume that the app IO is blocked on 2900 * @param uid the UID of the app blocked on IO 2901 * @param tid the tid of the app blocked on IO 2902 * @param reason the reason the app is blocked on IO 2903 * @hide 2904 */ 2905 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) notifyAppIoBlocked(@onNull UUID volumeUuid, int uid, int tid, @AppIoBlockedReason int reason)2906 public void notifyAppIoBlocked(@NonNull UUID volumeUuid, int uid, int tid, 2907 @AppIoBlockedReason int reason) { 2908 Objects.requireNonNull(volumeUuid); 2909 try { 2910 mStorageManager.notifyAppIoBlocked(convert(volumeUuid), uid, tid, reason); 2911 } catch (RemoteException e) { 2912 throw e.rethrowFromSystemServer(); 2913 } 2914 } 2915 2916 /** 2917 * Notify the system that an app with {@code uid} and {@code tid} has resmued a previously 2918 * blocked IO request on {@code volumeUuid} for {@code reason}. 2919 * 2920 * All app IO will be automatically marked as unblocked if {@code volumeUuid} is unmounted. 2921 * 2922 * This can only be called by the {@link ExternalStorageService} holding the 2923 * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission. 2924 * 2925 * @param volumeUuid the UUID of the storage volume that the app IO is resumed on 2926 * @param uid the UID of the app resuming IO 2927 * @param tid the tid of the app resuming IO 2928 * @param reason the reason the app is resuming IO 2929 * @hide 2930 */ 2931 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) notifyAppIoResumed(@onNull UUID volumeUuid, int uid, int tid, @AppIoBlockedReason int reason)2932 public void notifyAppIoResumed(@NonNull UUID volumeUuid, int uid, int tid, 2933 @AppIoBlockedReason int reason) { 2934 Objects.requireNonNull(volumeUuid); 2935 try { 2936 mStorageManager.notifyAppIoResumed(convert(volumeUuid), uid, tid, reason); 2937 } catch (RemoteException e) { 2938 throw e.rethrowFromSystemServer(); 2939 } 2940 } 2941 2942 /** 2943 * Check if {@code uid} with {@code tid} is blocked on IO for {@code reason}. 2944 * 2945 * This requires {@link ExternalStorageService} the 2946 * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission. 2947 * 2948 * @param volumeUuid the UUID of the storage volume to check IO blocked status 2949 * @param uid the UID of the app to check IO blocked status 2950 * @param tid the tid of the app to check IO blocked status 2951 * @param reason the reason to check IO blocked status for 2952 * @hide 2953 */ 2954 @TestApi isAppIoBlocked(@onNull UUID volumeUuid, int uid, int tid, @AppIoBlockedReason int reason)2955 public boolean isAppIoBlocked(@NonNull UUID volumeUuid, int uid, int tid, 2956 @AppIoBlockedReason int reason) { 2957 Objects.requireNonNull(volumeUuid); 2958 try { 2959 return mStorageManager.isAppIoBlocked(convert(volumeUuid), uid, tid, reason); 2960 } catch (RemoteException e) { 2961 throw e.rethrowFromSystemServer(); 2962 } 2963 } 2964 2965 /** 2966 * Notify the system of the current cloud media provider. 2967 * 2968 * This can only be called by the {@link android.service.storage.ExternalStorageService} 2969 * holding the {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission. 2970 * 2971 * @param authority the authority of the content provider 2972 * @hide 2973 */ 2974 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) setCloudMediaProvider(@ullable String authority)2975 public void setCloudMediaProvider(@Nullable String authority) { 2976 try { 2977 mStorageManager.setCloudMediaProvider(authority); 2978 } catch (RemoteException e) { 2979 throw e.rethrowFromSystemServer(); 2980 } 2981 } 2982 2983 /** 2984 * Returns the authority of the current cloud media provider that was set by the 2985 * {@link android.service.storage.ExternalStorageService} holding the 2986 * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission via 2987 * {@link #setCloudMediaProvider(String)}. 2988 * 2989 * @hide 2990 */ 2991 @Nullable 2992 @TestApi 2993 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) getCloudMediaProvider()2994 public String getCloudMediaProvider() { 2995 try { 2996 return mStorageManager.getCloudMediaProvider(); 2997 } catch (RemoteException e) { 2998 throw e.rethrowFromSystemServer(); 2999 } 3000 } 3001 3002 private final Object mFuseAppLoopLock = new Object(); 3003 3004 @GuardedBy("mFuseAppLoopLock") 3005 private @Nullable FuseAppLoop mFuseAppLoop = null; 3006 3007 /** @hide */ 3008 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 3009 public static final int CRYPT_TYPE_PASSWORD = 0; 3010 /** @hide */ 3011 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 3012 public static final int CRYPT_TYPE_DEFAULT = 1; 3013 3014 /** 3015 * Returns the remaining lifetime of the internal storage device, as an integer percentage. For 3016 * example, 90 indicates that 90% of the storage device's useful lifetime remains. If no 3017 * information is available, -1 is returned. 3018 * 3019 * @return Percentage of the remaining useful lifetime of the internal storage device. 3020 * @hide 3021 */ 3022 @FlaggedApi(Flags.FLAG_STORAGE_LIFETIME_API) 3023 @SystemApi 3024 @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) getInternalStorageRemainingLifetime()3025 public int getInternalStorageRemainingLifetime() { 3026 try { 3027 return mStorageManager.getInternalStorageRemainingLifetime(); 3028 } catch (RemoteException e) { 3029 throw e.rethrowFromSystemServer(); 3030 } 3031 } 3032 } 3033