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