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