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