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