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