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