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
17package android.os.storage;
18
19import static android.net.TrafficStats.GB_IN_BYTES;
20import static android.net.TrafficStats.MB_IN_BYTES;
21
22import android.annotation.BytesLong;
23import android.annotation.IntDef;
24import android.annotation.NonNull;
25import android.annotation.Nullable;
26import android.annotation.RequiresPermission;
27import android.annotation.SdkConstant;
28import android.annotation.SuppressLint;
29import android.annotation.SystemApi;
30import android.annotation.SystemService;
31import android.annotation.WorkerThread;
32import android.app.Activity;
33import android.app.ActivityThread;
34import android.content.ContentResolver;
35import android.content.Context;
36import android.content.Intent;
37import android.content.pm.ApplicationInfo;
38import android.content.pm.IPackageMoveObserver;
39import android.content.pm.PackageManager;
40import android.os.Binder;
41import android.os.Environment;
42import android.os.FileUtils;
43import android.os.Handler;
44import android.os.Looper;
45import android.os.Message;
46import android.os.ParcelFileDescriptor;
47import android.os.ParcelableException;
48import android.os.ProxyFileDescriptorCallback;
49import android.os.RemoteException;
50import android.os.ServiceManager;
51import android.os.ServiceManager.ServiceNotFoundException;
52import android.os.SystemProperties;
53import android.os.UserHandle;
54import android.provider.Settings;
55import android.system.ErrnoException;
56import android.system.Os;
57import android.system.OsConstants;
58import android.text.TextUtils;
59import android.util.Log;
60import android.util.Pair;
61import android.util.Slog;
62import android.util.SparseArray;
63
64import com.android.internal.annotations.GuardedBy;
65import com.android.internal.annotations.VisibleForTesting;
66import com.android.internal.logging.MetricsLogger;
67import com.android.internal.os.AppFuseMount;
68import com.android.internal.os.FuseAppLoop;
69import com.android.internal.os.FuseUnavailableMountException;
70import com.android.internal.os.RoSystemProperties;
71import com.android.internal.os.SomeArgs;
72import com.android.internal.util.Preconditions;
73
74import java.io.File;
75import java.io.FileDescriptor;
76import java.io.FileNotFoundException;
77import java.io.IOException;
78import java.lang.annotation.Retention;
79import java.lang.annotation.RetentionPolicy;
80import java.lang.ref.WeakReference;
81import java.nio.charset.StandardCharsets;
82import java.util.ArrayList;
83import java.util.Arrays;
84import java.util.Collections;
85import java.util.Iterator;
86import java.util.List;
87import java.util.Objects;
88import java.util.UUID;
89import java.util.concurrent.ThreadFactory;
90import java.util.concurrent.atomic.AtomicInteger;
91
92/**
93 * StorageManager is the interface to the systems storage service. The storage
94 * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
95 * <p>
96 * OBBs contain a filesystem that maybe be encrypted on disk and mounted
97 * on-demand from an application. OBBs are a good way of providing large amounts
98 * of binary assets without packaging them into APKs as they may be multiple
99 * gigabytes in size. However, due to their size, they're most likely stored in
100 * a shared storage pool accessible from all programs. The system does not
101 * guarantee the security of the OBB file itself: if any program modifies the
102 * OBB, there is no guarantee that a read from that OBB will produce the
103 * expected output.
104 */
105@SystemService(Context.STORAGE_SERVICE)
106public class StorageManager {
107    private static final String TAG = "StorageManager";
108
109    /** {@hide} */
110    public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
111    /** {@hide} */
112    public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
113    /** {@hide} */
114    public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
115    /** {@hide} */
116    public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe";
117    /** {@hide} */
118    public static final String PROP_SDCARDFS = "persist.sys.sdcardfs";
119    /** {@hide} */
120    public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk";
121
122    /** {@hide} */
123    public static final String UUID_PRIVATE_INTERNAL = null;
124    /** {@hide} */
125    public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
126    /** {@hide} */
127    public static final String UUID_SYSTEM = "system";
128
129    // NOTE: UUID constants below are namespaced
130    // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad default
131    // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad primary_physical
132    // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad system
133
134    /**
135     * UUID representing the default internal storage of this device which
136     * provides {@link Environment#getDataDirectory()}.
137     * <p>
138     * This value is constant across all devices and it will never change, and
139     * thus it cannot be used to uniquely identify a particular physical device.
140     *
141     * @see #getUuidForPath(File)
142     * @see ApplicationInfo#storageUuid
143     */
144    public static final UUID UUID_DEFAULT = UUID
145            .fromString("41217664-9172-527a-b3d5-edabb50a7d69");
146
147    /** {@hide} */
148    public static final UUID UUID_PRIMARY_PHYSICAL_ = UUID
149            .fromString("0f95a519-dae7-5abf-9519-fbd6209e05fd");
150
151    /** {@hide} */
152    public static final UUID UUID_SYSTEM_ = UUID
153            .fromString("5d258386-e60d-59e3-826d-0089cdd42cc0");
154
155    /**
156     * Activity Action: Allows the user to manage their storage. This activity
157     * provides the ability to free up space on the device by deleting data such
158     * as apps.
159     * <p>
160     * If the sending application has a specific storage device or allocation
161     * size in mind, they can optionally define {@link #EXTRA_UUID} or
162     * {@link #EXTRA_REQUESTED_BYTES}, respectively.
163     * <p>
164     * This intent should be launched using
165     * {@link Activity#startActivityForResult(Intent, int)} so that the user
166     * knows which app is requesting the storage space. The returned result will
167     * be {@link Activity#RESULT_OK} if the requested space was made available,
168     * or {@link Activity#RESULT_CANCELED} otherwise.
169     */
170    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
171    public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
172
173    /**
174     * Extra {@link UUID} used to indicate the storage volume where an
175     * application is interested in allocating or managing disk space.
176     *
177     * @see #ACTION_MANAGE_STORAGE
178     * @see #UUID_DEFAULT
179     * @see #getUuidForPath(File)
180     * @see Intent#putExtra(String, java.io.Serializable)
181     */
182    public static final String EXTRA_UUID = "android.os.storage.extra.UUID";
183
184    /**
185     * Extra used to indicate the total size (in bytes) that an application is
186     * interested in allocating.
187     * <p>
188     * When defined, the management UI will help guide the user to free up
189     * enough disk space to reach this requested value.
190     *
191     * @see #ACTION_MANAGE_STORAGE
192     */
193    public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
194
195    /** {@hide} */
196    public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
197    /** {@hide} */
198    public static final int DEBUG_EMULATE_FBE = 1 << 1;
199    /** {@hide} */
200    public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2;
201    /** {@hide} */
202    public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3;
203    /** {@hide} */
204    public static final int DEBUG_VIRTUAL_DISK = 1 << 4;
205
206    // NOTE: keep in sync with installd
207    /** {@hide} */
208    public static final int FLAG_STORAGE_DE = 1 << 0;
209    /** {@hide} */
210    public static final int FLAG_STORAGE_CE = 1 << 1;
211
212    /** {@hide} */
213    public static final int FLAG_FOR_WRITE = 1 << 8;
214    /** {@hide} */
215    public static final int FLAG_REAL_STATE = 1 << 9;
216    /** {@hide} */
217    public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
218
219    /** {@hide} */
220    public static final int FSTRIM_FLAG_DEEP = 1 << 0;
221    /** {@hide} */
222    public static final int FSTRIM_FLAG_BENCHMARK = 1 << 1;
223
224    /** @hide The volume is not encrypted. */
225    public static final int ENCRYPTION_STATE_NONE = 1;
226
227    /** @hide The volume has been encrypted succesfully. */
228    public static final int ENCRYPTION_STATE_OK = 0;
229
230    /** @hide The volume is in a bad state.*/
231    public static final int ENCRYPTION_STATE_ERROR_UNKNOWN = -1;
232
233    /** @hide Encryption is incomplete */
234    public static final int ENCRYPTION_STATE_ERROR_INCOMPLETE = -2;
235
236    /** @hide Encryption is incomplete and irrecoverable */
237    public static final int ENCRYPTION_STATE_ERROR_INCONSISTENT = -3;
238
239    /** @hide Underlying data is corrupt */
240    public static final int ENCRYPTION_STATE_ERROR_CORRUPT = -4;
241
242    private static volatile IStorageManager sStorageManager = null;
243
244    private final Context mContext;
245    private final ContentResolver mResolver;
246
247    private final IStorageManager mStorageManager;
248    private final Looper mLooper;
249    private final AtomicInteger mNextNonce = new AtomicInteger(0);
250
251    private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>();
252
253    private static class StorageEventListenerDelegate extends IStorageEventListener.Stub implements
254            Handler.Callback {
255        private static final int MSG_STORAGE_STATE_CHANGED = 1;
256        private static final int MSG_VOLUME_STATE_CHANGED = 2;
257        private static final int MSG_VOLUME_RECORD_CHANGED = 3;
258        private static final int MSG_VOLUME_FORGOTTEN = 4;
259        private static final int MSG_DISK_SCANNED = 5;
260        private static final int MSG_DISK_DESTROYED = 6;
261
262        final StorageEventListener mCallback;
263        final Handler mHandler;
264
265        public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
266            mCallback = callback;
267            mHandler = new Handler(looper, this);
268        }
269
270        @Override
271        public boolean handleMessage(Message msg) {
272            final SomeArgs args = (SomeArgs) msg.obj;
273            switch (msg.what) {
274                case MSG_STORAGE_STATE_CHANGED:
275                    mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
276                            (String) args.arg3);
277                    args.recycle();
278                    return true;
279                case MSG_VOLUME_STATE_CHANGED:
280                    mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
281                    args.recycle();
282                    return true;
283                case MSG_VOLUME_RECORD_CHANGED:
284                    mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1);
285                    args.recycle();
286                    return true;
287                case MSG_VOLUME_FORGOTTEN:
288                    mCallback.onVolumeForgotten((String) args.arg1);
289                    args.recycle();
290                    return true;
291                case MSG_DISK_SCANNED:
292                    mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
293                    args.recycle();
294                    return true;
295                case MSG_DISK_DESTROYED:
296                    mCallback.onDiskDestroyed((DiskInfo) args.arg1);
297                    args.recycle();
298                    return true;
299            }
300            args.recycle();
301            return false;
302        }
303
304        @Override
305        public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
306            // Ignored
307        }
308
309        @Override
310        public void onStorageStateChanged(String path, String oldState, String newState) {
311            final SomeArgs args = SomeArgs.obtain();
312            args.arg1 = path;
313            args.arg2 = oldState;
314            args.arg3 = newState;
315            mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
316        }
317
318        @Override
319        public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
320            final SomeArgs args = SomeArgs.obtain();
321            args.arg1 = vol;
322            args.argi2 = oldState;
323            args.argi3 = newState;
324            mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
325        }
326
327        @Override
328        public void onVolumeRecordChanged(VolumeRecord rec) {
329            final SomeArgs args = SomeArgs.obtain();
330            args.arg1 = rec;
331            mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
332        }
333
334        @Override
335        public void onVolumeForgotten(String fsUuid) {
336            final SomeArgs args = SomeArgs.obtain();
337            args.arg1 = fsUuid;
338            mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
339        }
340
341        @Override
342        public void onDiskScanned(DiskInfo disk, int volumeCount) {
343            final SomeArgs args = SomeArgs.obtain();
344            args.arg1 = disk;
345            args.argi2 = volumeCount;
346            mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
347        }
348
349        @Override
350        public void onDiskDestroyed(DiskInfo disk) throws RemoteException {
351            final SomeArgs args = SomeArgs.obtain();
352            args.arg1 = disk;
353            mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
354        }
355    }
356
357    /**
358     * Binder listener for OBB action results.
359     */
360    private final ObbActionListener mObbActionListener = new ObbActionListener();
361
362    private class ObbActionListener extends IObbActionListener.Stub {
363        @SuppressWarnings("hiding")
364        private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
365
366        @Override
367        public void onObbResult(String filename, int nonce, int status) {
368            final ObbListenerDelegate delegate;
369            synchronized (mListeners) {
370                delegate = mListeners.get(nonce);
371                if (delegate != null) {
372                    mListeners.remove(nonce);
373                }
374            }
375
376            if (delegate != null) {
377                delegate.sendObbStateChanged(filename, status);
378            }
379        }
380
381        public int addListener(OnObbStateChangeListener listener) {
382            final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
383
384            synchronized (mListeners) {
385                mListeners.put(delegate.nonce, delegate);
386            }
387
388            return delegate.nonce;
389        }
390    }
391
392    private int getNextNonce() {
393        return mNextNonce.getAndIncrement();
394    }
395
396    /**
397     * Private class containing sender and receiver code for StorageEvents.
398     */
399    private class ObbListenerDelegate {
400        private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
401        private final Handler mHandler;
402
403        private final int nonce;
404
405        ObbListenerDelegate(OnObbStateChangeListener listener) {
406            nonce = getNextNonce();
407            mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
408            mHandler = new Handler(mLooper) {
409                @Override
410                public void handleMessage(Message msg) {
411                    final OnObbStateChangeListener changeListener = getListener();
412                    if (changeListener == null) {
413                        return;
414                    }
415
416                    changeListener.onObbStateChange((String) msg.obj, msg.arg1);
417                }
418            };
419        }
420
421        OnObbStateChangeListener getListener() {
422            if (mObbEventListenerRef == null) {
423                return null;
424            }
425            return mObbEventListenerRef.get();
426        }
427
428        void sendObbStateChanged(String path, int state) {
429            mHandler.obtainMessage(0, state, 0, path).sendToTarget();
430        }
431    }
432
433    /** {@hide} */
434    @Deprecated
435    public static StorageManager from(Context context) {
436        return context.getSystemService(StorageManager.class);
437    }
438
439    /**
440     * Constructs a StorageManager object through which an application can
441     * can communicate with the systems mount service.
442     *
443     * @param looper The {@link android.os.Looper} which events will be received on.
444     *
445     * <p>Applications can get instance of this class by calling
446     * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
447     * of {@link android.content.Context#STORAGE_SERVICE}.
448     *
449     * @hide
450     */
451    public StorageManager(Context context, Looper looper) throws ServiceNotFoundException {
452        mContext = context;
453        mResolver = context.getContentResolver();
454        mLooper = looper;
455        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount"));
456    }
457
458    /**
459     * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
460     *
461     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
462     *
463     * @hide
464     */
465    public void registerListener(StorageEventListener listener) {
466        synchronized (mDelegates) {
467            final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener,
468                    mLooper);
469            try {
470                mStorageManager.registerListener(delegate);
471            } catch (RemoteException e) {
472                throw e.rethrowFromSystemServer();
473            }
474            mDelegates.add(delegate);
475        }
476    }
477
478    /**
479     * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
480     *
481     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
482     *
483     * @hide
484     */
485    public void unregisterListener(StorageEventListener listener) {
486        synchronized (mDelegates) {
487            for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
488                final StorageEventListenerDelegate delegate = i.next();
489                if (delegate.mCallback == listener) {
490                    try {
491                        mStorageManager.unregisterListener(delegate);
492                    } catch (RemoteException e) {
493                        throw e.rethrowFromSystemServer();
494                    }
495                    i.remove();
496                }
497            }
498        }
499    }
500
501    /**
502     * Enables USB Mass Storage (UMS) on the device.
503     *
504     * @hide
505     */
506    @Deprecated
507    public void enableUsbMassStorage() {
508    }
509
510    /**
511     * Disables USB Mass Storage (UMS) on the device.
512     *
513     * @hide
514     */
515    @Deprecated
516    public void disableUsbMassStorage() {
517    }
518
519    /**
520     * Query if a USB Mass Storage (UMS) host is connected.
521     * @return true if UMS host is connected.
522     *
523     * @hide
524     */
525    @Deprecated
526    public boolean isUsbMassStorageConnected() {
527        return false;
528    }
529
530    /**
531     * Query if a USB Mass Storage (UMS) is enabled on the device.
532     * @return true if UMS host is enabled.
533     *
534     * @hide
535     */
536    @Deprecated
537    public boolean isUsbMassStorageEnabled() {
538        return false;
539    }
540
541    /**
542     * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
543     * specified, it is supplied to the mounting process to be used in any
544     * encryption used in the OBB.
545     * <p>
546     * The OBB will remain mounted for as long as the StorageManager reference
547     * is held by the application. As soon as this reference is lost, the OBBs
548     * in use will be unmounted. The {@link OnObbStateChangeListener} registered
549     * with this call will receive the success or failure of this operation.
550     * <p>
551     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
552     * file matches a package ID that is owned by the calling program's UID.
553     * That is, shared UID applications can attempt to mount any other
554     * application's OBB that shares its UID.
555     *
556     * @param rawPath the path to the OBB file
557     * @param key secret used to encrypt the OBB; may be <code>null</code> if no
558     *            encryption was used on the OBB.
559     * @param listener will receive the success or failure of the operation
560     * @return whether the mount call was successfully queued or not
561     */
562    public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
563        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
564        Preconditions.checkNotNull(listener, "listener cannot be null");
565
566        try {
567            final String canonicalPath = new File(rawPath).getCanonicalPath();
568            final int nonce = mObbActionListener.addListener(listener);
569            mStorageManager.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce);
570            return true;
571        } catch (IOException e) {
572            throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e);
573        } catch (RemoteException e) {
574            throw e.rethrowFromSystemServer();
575        }
576    }
577
578    /**
579     * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
580     * <code>force</code> flag is true, it will kill any application needed to
581     * unmount the given OBB (even the calling application).
582     * <p>
583     * The {@link OnObbStateChangeListener} registered with this call will
584     * receive the success or failure of this operation.
585     * <p>
586     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
587     * file matches a package ID that is owned by the calling program's UID.
588     * That is, shared UID applications can obtain access to any other
589     * application's OBB that shares its UID.
590     * <p>
591     *
592     * @param rawPath path to the OBB file
593     * @param force whether to kill any programs using this in order to unmount
594     *            it
595     * @param listener will receive the success or failure of the operation
596     * @return whether the unmount call was successfully queued or not
597     */
598    public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
599        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
600        Preconditions.checkNotNull(listener, "listener cannot be null");
601
602        try {
603            final int nonce = mObbActionListener.addListener(listener);
604            mStorageManager.unmountObb(rawPath, force, mObbActionListener, nonce);
605            return true;
606        } catch (RemoteException e) {
607            throw e.rethrowFromSystemServer();
608        }
609    }
610
611    /**
612     * Check whether an Opaque Binary Blob (OBB) is mounted or not.
613     *
614     * @param rawPath path to OBB image
615     * @return true if OBB is mounted; false if not mounted or on error
616     */
617    public boolean isObbMounted(String rawPath) {
618        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
619
620        try {
621            return mStorageManager.isObbMounted(rawPath);
622        } catch (RemoteException e) {
623            throw e.rethrowFromSystemServer();
624        }
625    }
626
627    /**
628     * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
629     * give you the path to where you can obtain access to the internals of the
630     * OBB.
631     *
632     * @param rawPath path to OBB image
633     * @return absolute path to mounted OBB image data or <code>null</code> if
634     *         not mounted or exception encountered trying to read status
635     */
636    public String getMountedObbPath(String rawPath) {
637        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
638
639        try {
640            return mStorageManager.getMountedObbPath(rawPath);
641        } catch (RemoteException e) {
642            throw e.rethrowFromSystemServer();
643        }
644    }
645
646    /** {@hide} */
647    public @NonNull List<DiskInfo> getDisks() {
648        try {
649            return Arrays.asList(mStorageManager.getDisks());
650        } catch (RemoteException e) {
651            throw e.rethrowFromSystemServer();
652        }
653    }
654
655    /** {@hide} */
656    public @Nullable DiskInfo findDiskById(String id) {
657        Preconditions.checkNotNull(id);
658        // TODO; go directly to service to make this faster
659        for (DiskInfo disk : getDisks()) {
660            if (Objects.equals(disk.id, id)) {
661                return disk;
662            }
663        }
664        return null;
665    }
666
667    /** {@hide} */
668    public @Nullable VolumeInfo findVolumeById(String id) {
669        Preconditions.checkNotNull(id);
670        // TODO; go directly to service to make this faster
671        for (VolumeInfo vol : getVolumes()) {
672            if (Objects.equals(vol.id, id)) {
673                return vol;
674            }
675        }
676        return null;
677    }
678
679    /** {@hide} */
680    public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
681        Preconditions.checkNotNull(fsUuid);
682        // TODO; go directly to service to make this faster
683        for (VolumeInfo vol : getVolumes()) {
684            if (Objects.equals(vol.fsUuid, fsUuid)) {
685                return vol;
686            }
687        }
688        return null;
689    }
690
691    /** {@hide} */
692    public @Nullable VolumeRecord findRecordByUuid(String fsUuid) {
693        Preconditions.checkNotNull(fsUuid);
694        // TODO; go directly to service to make this faster
695        for (VolumeRecord rec : getVolumeRecords()) {
696            if (Objects.equals(rec.fsUuid, fsUuid)) {
697                return rec;
698            }
699        }
700        return null;
701    }
702
703    /** {@hide} */
704    public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
705        if (emulatedVol != null) {
706            return findVolumeById(emulatedVol.getId().replace("emulated", "private"));
707        } else {
708            return null;
709        }
710    }
711
712    /** {@hide} */
713    public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
714        if (privateVol != null) {
715            return findVolumeById(privateVol.getId().replace("private", "emulated"));
716        } else {
717            return null;
718        }
719    }
720
721    /** {@hide} */
722    public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
723        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
724            return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
725        } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
726            return getPrimaryPhysicalVolume();
727        } else {
728            return findVolumeByUuid(volumeUuid);
729        }
730    }
731
732    /**
733     * Return a UUID identifying the storage volume that hosts the given
734     * filesystem path.
735     * <p>
736     * If this path is hosted by the default internal storage of the device at
737     * {@link Environment#getDataDirectory()}, the returned value will be
738     * {@link #UUID_DEFAULT}.
739     *
740     * @throws IOException when the storage device at the given path isn't
741     *             present, or when it doesn't have a valid UUID.
742     */
743    public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
744        Preconditions.checkNotNull(path);
745        final String pathString = path.getCanonicalPath();
746        if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) {
747            return UUID_DEFAULT;
748        }
749        try {
750            for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
751                if (vol.path != null && FileUtils.contains(vol.path, pathString)) {
752                    // TODO: verify that emulated adopted devices have UUID of
753                    // underlying volume
754                    return convert(vol.fsUuid);
755                }
756            }
757        } catch (RemoteException e) {
758            throw e.rethrowFromSystemServer();
759        }
760        throw new FileNotFoundException("Failed to find a storage device for " + path);
761    }
762
763    /** {@hide} */
764    public @NonNull File findPathForUuid(String volumeUuid) throws FileNotFoundException {
765        final VolumeInfo vol = findVolumeByQualifiedUuid(volumeUuid);
766        if (vol != null) {
767            return vol.getPath();
768        }
769        throw new FileNotFoundException("Failed to find a storage device for " + volumeUuid);
770    }
771
772    /** {@hide} */
773    public @NonNull List<VolumeInfo> getVolumes() {
774        try {
775            return Arrays.asList(mStorageManager.getVolumes(0));
776        } catch (RemoteException e) {
777            throw e.rethrowFromSystemServer();
778        }
779    }
780
781    /** {@hide} */
782    public @NonNull List<VolumeInfo> getWritablePrivateVolumes() {
783        try {
784            final ArrayList<VolumeInfo> res = new ArrayList<>();
785            for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
786                if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
787                    res.add(vol);
788                }
789            }
790            return res;
791        } catch (RemoteException e) {
792            throw e.rethrowFromSystemServer();
793        }
794    }
795
796    /** {@hide} */
797    public @NonNull List<VolumeRecord> getVolumeRecords() {
798        try {
799            return Arrays.asList(mStorageManager.getVolumeRecords(0));
800        } catch (RemoteException e) {
801            throw e.rethrowFromSystemServer();
802        }
803    }
804
805    /** {@hide} */
806    public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
807        if (vol == null) return null;
808
809        // Nickname always takes precedence when defined
810        if (!TextUtils.isEmpty(vol.fsUuid)) {
811            final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
812            if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
813                return rec.nickname;
814            }
815        }
816
817        if (!TextUtils.isEmpty(vol.getDescription())) {
818            return vol.getDescription();
819        }
820
821        if (vol.disk != null) {
822            return vol.disk.getDescription();
823        }
824
825        return null;
826    }
827
828    /** {@hide} */
829    public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
830        final List<VolumeInfo> vols = getVolumes();
831        for (VolumeInfo vol : vols) {
832            if (vol.isPrimaryPhysical()) {
833                return vol;
834            }
835        }
836        return null;
837    }
838
839    /** {@hide} */
840    public void mount(String volId) {
841        try {
842            mStorageManager.mount(volId);
843        } catch (RemoteException e) {
844            throw e.rethrowFromSystemServer();
845        }
846    }
847
848    /** {@hide} */
849    public void unmount(String volId) {
850        try {
851            mStorageManager.unmount(volId);
852        } catch (RemoteException e) {
853            throw e.rethrowFromSystemServer();
854        }
855    }
856
857    /** {@hide} */
858    public void format(String volId) {
859        try {
860            mStorageManager.format(volId);
861        } catch (RemoteException e) {
862            throw e.rethrowFromSystemServer();
863        }
864    }
865
866    /** {@hide} */
867    public long benchmark(String volId) {
868        try {
869            return mStorageManager.benchmark(volId);
870        } catch (RemoteException e) {
871            throw e.rethrowFromSystemServer();
872        }
873    }
874
875    /** {@hide} */
876    public void partitionPublic(String diskId) {
877        try {
878            mStorageManager.partitionPublic(diskId);
879        } catch (RemoteException e) {
880            throw e.rethrowFromSystemServer();
881        }
882    }
883
884    /** {@hide} */
885    public void partitionPrivate(String diskId) {
886        try {
887            mStorageManager.partitionPrivate(diskId);
888        } catch (RemoteException e) {
889            throw e.rethrowFromSystemServer();
890        }
891    }
892
893    /** {@hide} */
894    public void partitionMixed(String diskId, int ratio) {
895        try {
896            mStorageManager.partitionMixed(diskId, ratio);
897        } catch (RemoteException e) {
898            throw e.rethrowFromSystemServer();
899        }
900    }
901
902    /** {@hide} */
903    public void wipeAdoptableDisks() {
904        // We only wipe devices in "adoptable" locations, which are in a
905        // long-term stable slot/location on the device, where apps have a
906        // reasonable chance of storing sensitive data. (Apps need to go through
907        // SAF to write to transient volumes.)
908        final List<DiskInfo> disks = getDisks();
909        for (DiskInfo disk : disks) {
910            final String diskId = disk.getId();
911            if (disk.isAdoptable()) {
912                Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
913                try {
914                    // TODO: switch to explicit wipe command when we have it,
915                    // for now rely on the fact that vfat format does a wipe
916                    mStorageManager.partitionPublic(diskId);
917                } catch (Exception e) {
918                    Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
919                }
920            } else {
921                Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
922            }
923        }
924    }
925
926    /** {@hide} */
927    public void setVolumeNickname(String fsUuid, String nickname) {
928        try {
929            mStorageManager.setVolumeNickname(fsUuid, nickname);
930        } catch (RemoteException e) {
931            throw e.rethrowFromSystemServer();
932        }
933    }
934
935    /** {@hide} */
936    public void setVolumeInited(String fsUuid, boolean inited) {
937        try {
938            mStorageManager.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0,
939                    VolumeRecord.USER_FLAG_INITED);
940        } catch (RemoteException e) {
941            throw e.rethrowFromSystemServer();
942        }
943    }
944
945    /** {@hide} */
946    public void setVolumeSnoozed(String fsUuid, boolean snoozed) {
947        try {
948            mStorageManager.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0,
949                    VolumeRecord.USER_FLAG_SNOOZED);
950        } catch (RemoteException e) {
951            throw e.rethrowFromSystemServer();
952        }
953    }
954
955    /** {@hide} */
956    public void forgetVolume(String fsUuid) {
957        try {
958            mStorageManager.forgetVolume(fsUuid);
959        } catch (RemoteException e) {
960            throw e.rethrowFromSystemServer();
961        }
962    }
963
964    /**
965     * This is not the API you're looking for.
966     *
967     * @see PackageManager#getPrimaryStorageCurrentVolume()
968     * @hide
969     */
970    public String getPrimaryStorageUuid() {
971        try {
972            return mStorageManager.getPrimaryStorageUuid();
973        } catch (RemoteException e) {
974            throw e.rethrowFromSystemServer();
975        }
976    }
977
978    /**
979     * This is not the API you're looking for.
980     *
981     * @see PackageManager#movePrimaryStorage(VolumeInfo)
982     * @hide
983     */
984    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
985        try {
986            mStorageManager.setPrimaryStorageUuid(volumeUuid, callback);
987        } catch (RemoteException e) {
988            throw e.rethrowFromSystemServer();
989        }
990    }
991
992    /**
993     * Return the {@link StorageVolume} that contains the given file, or {@code null} if none.
994     */
995    public @Nullable StorageVolume getStorageVolume(File file) {
996        return getStorageVolume(getVolumeList(), file);
997    }
998
999    /** {@hide} */
1000    public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
1001        return getStorageVolume(getVolumeList(userId, 0), file);
1002    }
1003
1004    /** {@hide} */
1005    private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
1006        if (file == null) {
1007            return null;
1008        }
1009        try {
1010            file = file.getCanonicalFile();
1011        } catch (IOException ignored) {
1012            Slog.d(TAG, "Could not get canonical path for " + file);
1013            return null;
1014        }
1015        for (StorageVolume volume : volumes) {
1016            File volumeFile = volume.getPathFile();
1017            try {
1018                volumeFile = volumeFile.getCanonicalFile();
1019            } catch (IOException ignored) {
1020                continue;
1021            }
1022            if (FileUtils.contains(volumeFile, file)) {
1023                return volume;
1024            }
1025        }
1026        return null;
1027    }
1028
1029    /**
1030     * Gets the state of a volume via its mountpoint.
1031     * @hide
1032     */
1033    @Deprecated
1034    public @NonNull String getVolumeState(String mountPoint) {
1035        final StorageVolume vol = getStorageVolume(new File(mountPoint));
1036        if (vol != null) {
1037            return vol.getState();
1038        } else {
1039            return Environment.MEDIA_UNKNOWN;
1040        }
1041    }
1042
1043    /**
1044     * Return the list of shared/external storage volumes available to the
1045     * current user. This includes both the primary shared storage device and
1046     * any attached external volumes including SD cards and USB drives.
1047     *
1048     * @see Environment#getExternalStorageDirectory()
1049     * @see StorageVolume#createAccessIntent(String)
1050     */
1051    public @NonNull List<StorageVolume> getStorageVolumes() {
1052        final ArrayList<StorageVolume> res = new ArrayList<>();
1053        Collections.addAll(res,
1054                getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
1055        return res;
1056    }
1057
1058    /**
1059     * Return the primary shared/external storage volume available to the
1060     * current user. This volume is the same storage device returned by
1061     * {@link Environment#getExternalStorageDirectory()} and
1062     * {@link Context#getExternalFilesDir(String)}.
1063     */
1064    public @NonNull StorageVolume getPrimaryStorageVolume() {
1065        return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
1066    }
1067
1068    /** {@hide} */
1069    public static Pair<String, Long> getPrimaryStoragePathAndSize() {
1070        return Pair.create(null,
1071                FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()));
1072    }
1073
1074    /** {@hide} */
1075    public long getPrimaryStorageSize() {
1076        return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace());
1077    }
1078
1079    /** @removed */
1080    public @NonNull StorageVolume[] getVolumeList() {
1081        return getVolumeList(mContext.getUserId(), 0);
1082    }
1083
1084    /** {@hide} */
1085    public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
1086        final IStorageManager storageManager = IStorageManager.Stub.asInterface(
1087                ServiceManager.getService("mount"));
1088        try {
1089            String packageName = ActivityThread.currentOpPackageName();
1090            if (packageName == null) {
1091                // Package name can be null if the activity thread is running but the app
1092                // hasn't bound yet. In this case we fall back to the first package in the
1093                // current UID. This works for runtime permissions as permission state is
1094                // per UID and permission realted app ops are updated for all UID packages.
1095                String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
1096                        android.os.Process.myUid());
1097                if (packageNames == null || packageNames.length <= 0) {
1098                    return new StorageVolume[0];
1099                }
1100                packageName = packageNames[0];
1101            }
1102            final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
1103                    PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
1104            if (uid <= 0) {
1105                return new StorageVolume[0];
1106            }
1107            return storageManager.getVolumeList(uid, packageName, flags);
1108        } catch (RemoteException e) {
1109            throw e.rethrowFromSystemServer();
1110        }
1111    }
1112
1113    /**
1114     * Returns list of paths for all mountable volumes.
1115     * @hide
1116     */
1117    @Deprecated
1118    public @NonNull String[] getVolumePaths() {
1119        StorageVolume[] volumes = getVolumeList();
1120        int count = volumes.length;
1121        String[] paths = new String[count];
1122        for (int i = 0; i < count; i++) {
1123            paths[i] = volumes[i].getPath();
1124        }
1125        return paths;
1126    }
1127
1128    /** @removed */
1129    public @NonNull StorageVolume getPrimaryVolume() {
1130        return getPrimaryVolume(getVolumeList());
1131    }
1132
1133    /** {@hide} */
1134    public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
1135        for (StorageVolume volume : volumes) {
1136            if (volume.isPrimary()) {
1137                return volume;
1138            }
1139        }
1140        throw new IllegalStateException("Missing primary storage");
1141    }
1142
1143    private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5;
1144    private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
1145
1146    private static final int DEFAULT_CACHE_PERCENTAGE = 10;
1147    private static final long DEFAULT_CACHE_MAX_BYTES = 5 * GB_IN_BYTES;
1148
1149    private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
1150
1151    /**
1152     * Return the number of available bytes until the given path is considered
1153     * running low on storage.
1154     *
1155     * @hide
1156     */
1157    public long getStorageBytesUntilLow(File path) {
1158        return path.getUsableSpace() - getStorageFullBytes(path);
1159    }
1160
1161    /**
1162     * Return the number of available bytes at which the given path is
1163     * considered running low on storage.
1164     *
1165     * @hide
1166     */
1167    public long getStorageLowBytes(File path) {
1168        final long lowPercent = Settings.Global.getInt(mResolver,
1169                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
1170        final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
1171
1172        final long maxLowBytes = Settings.Global.getLong(mResolver,
1173                Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
1174
1175        return Math.min(lowBytes, maxLowBytes);
1176    }
1177
1178    /**
1179     * Return the minimum number of bytes of storage on the device that should
1180     * be reserved for cached data.
1181     *
1182     * @hide
1183     */
1184    public long getStorageCacheBytes(File path) {
1185        final long cachePercent = Settings.Global.getInt(mResolver,
1186                Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE);
1187        final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100;
1188
1189        final long maxCacheBytes = Settings.Global.getLong(mResolver,
1190                Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES);
1191
1192        return Math.min(cacheBytes, maxCacheBytes);
1193    }
1194
1195    /**
1196     * Return the number of available bytes at which the given path is
1197     * considered full.
1198     *
1199     * @hide
1200     */
1201    public long getStorageFullBytes(File path) {
1202        return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
1203                DEFAULT_FULL_THRESHOLD_BYTES);
1204    }
1205
1206    /** {@hide} */
1207    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
1208        try {
1209            mStorageManager.createUserKey(userId, serialNumber, ephemeral);
1210        } catch (RemoteException e) {
1211            throw e.rethrowFromSystemServer();
1212        }
1213    }
1214
1215    /** {@hide} */
1216    public void destroyUserKey(int userId) {
1217        try {
1218            mStorageManager.destroyUserKey(userId);
1219        } catch (RemoteException e) {
1220            throw e.rethrowFromSystemServer();
1221        }
1222    }
1223
1224    /** {@hide} */
1225    public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
1226        try {
1227            mStorageManager.unlockUserKey(userId, serialNumber, token, secret);
1228        } catch (RemoteException e) {
1229            throw e.rethrowFromSystemServer();
1230        }
1231    }
1232
1233    /** {@hide} */
1234    public void lockUserKey(int userId) {
1235        try {
1236            mStorageManager.lockUserKey(userId);
1237        } catch (RemoteException e) {
1238            throw e.rethrowFromSystemServer();
1239        }
1240    }
1241
1242    /** {@hide} */
1243    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
1244        try {
1245            mStorageManager.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
1246        } catch (RemoteException e) {
1247            throw e.rethrowFromSystemServer();
1248        }
1249    }
1250
1251    /** {@hide} */
1252    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
1253        try {
1254            mStorageManager.destroyUserStorage(volumeUuid, userId, flags);
1255        } catch (RemoteException e) {
1256            throw e.rethrowFromSystemServer();
1257        }
1258    }
1259
1260    /** {@hide} */
1261    public void secdiscard(String path) {
1262        try {
1263            mStorageManager.secdiscard(path);
1264        } catch (RemoteException e) {
1265            throw e.rethrowFromSystemServer();
1266        }
1267    }
1268
1269    /** {@hide} */
1270    public static boolean isUserKeyUnlocked(int userId) {
1271        if (sStorageManager == null) {
1272            sStorageManager = IStorageManager.Stub
1273                    .asInterface(ServiceManager.getService("mount"));
1274        }
1275        if (sStorageManager == null) {
1276            Slog.w(TAG, "Early during boot, assuming locked");
1277            return false;
1278        }
1279        final long token = Binder.clearCallingIdentity();
1280        try {
1281            return sStorageManager.isUserKeyUnlocked(userId);
1282        } catch (RemoteException e) {
1283            throw e.rethrowAsRuntimeException();
1284        } finally {
1285            Binder.restoreCallingIdentity(token);
1286        }
1287    }
1288
1289    /**
1290     * Return if data stored at or under the given path will be encrypted while
1291     * at rest. This can help apps avoid the overhead of double-encrypting data.
1292     */
1293    public boolean isEncrypted(File file) {
1294        if (FileUtils.contains(Environment.getDataDirectory(), file)) {
1295            return isEncrypted();
1296        } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) {
1297            return true;
1298        }
1299        // TODO: extend to support shared storage
1300        return false;
1301    }
1302
1303    /** {@hide}
1304     * Is this device encryptable or already encrypted?
1305     * @return true for encryptable or encrypted
1306     *         false not encrypted and not encryptable
1307     */
1308    public static boolean isEncryptable() {
1309        return RoSystemProperties.CRYPTO_ENCRYPTABLE;
1310    }
1311
1312    /** {@hide}
1313     * Is this device already encrypted?
1314     * @return true for encrypted. (Implies isEncryptable() == true)
1315     *         false not encrypted
1316     */
1317    public static boolean isEncrypted() {
1318        return RoSystemProperties.CRYPTO_ENCRYPTED;
1319    }
1320
1321    /** {@hide}
1322     * Is this device file encrypted?
1323     * @return true for file encrypted. (Implies isEncrypted() == true)
1324     *         false not encrypted or block encrypted
1325     */
1326    public static boolean isFileEncryptedNativeOnly() {
1327        if (!isEncrypted()) {
1328            return false;
1329        }
1330        return RoSystemProperties.CRYPTO_FILE_ENCRYPTED;
1331    }
1332
1333    /** {@hide}
1334     * Is this device block encrypted?
1335     * @return true for block encrypted. (Implies isEncrypted() == true)
1336     *         false not encrypted or file encrypted
1337     */
1338    public static boolean isBlockEncrypted() {
1339        if (!isEncrypted()) {
1340            return false;
1341        }
1342        return RoSystemProperties.CRYPTO_BLOCK_ENCRYPTED;
1343    }
1344
1345    /** {@hide}
1346     * Is this device block encrypted with credentials?
1347     * @return true for crediential block encrypted.
1348     *         (Implies isBlockEncrypted() == true)
1349     *         false not encrypted, file encrypted or default block encrypted
1350     */
1351    public static boolean isNonDefaultBlockEncrypted() {
1352        if (!isBlockEncrypted()) {
1353            return false;
1354        }
1355
1356        try {
1357            IStorageManager storageManager = IStorageManager.Stub.asInterface(
1358                    ServiceManager.getService("mount"));
1359            return storageManager.getPasswordType() != CRYPT_TYPE_DEFAULT;
1360        } catch (RemoteException e) {
1361            Log.e(TAG, "Error getting encryption type");
1362            return false;
1363        }
1364    }
1365
1366    /** {@hide}
1367     * Is this device in the process of being block encrypted?
1368     * @return true for encrypting.
1369     *         false otherwise
1370     * Whether device isEncrypted at this point is undefined
1371     * Note that only system services and CryptKeeper will ever see this return
1372     * true - no app will ever be launched in this state.
1373     * Also note that this state will not change without a teardown of the
1374     * framework, so no service needs to check for changes during their lifespan
1375     */
1376    public static boolean isBlockEncrypting() {
1377        final String state = SystemProperties.get("vold.encrypt_progress", "");
1378        return !"".equalsIgnoreCase(state);
1379    }
1380
1381    /** {@hide}
1382     * Is this device non default block encrypted and in the process of
1383     * prompting for credentials?
1384     * @return true for prompting for credentials.
1385     *         (Implies isNonDefaultBlockEncrypted() == true)
1386     *         false otherwise
1387     * Note that only system services and CryptKeeper will ever see this return
1388     * true - no app will ever be launched in this state.
1389     * Also note that this state will not change without a teardown of the
1390     * framework, so no service needs to check for changes during their lifespan
1391     */
1392    public static boolean inCryptKeeperBounce() {
1393        final String status = SystemProperties.get("vold.decrypt");
1394        return "trigger_restart_min_framework".equals(status);
1395    }
1396
1397    /** {@hide} */
1398    public static boolean isFileEncryptedEmulatedOnly() {
1399        return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false);
1400    }
1401
1402    /** {@hide}
1403     * Is this device running in a file encrypted mode, either native or emulated?
1404     * @return true for file encrypted, false otherwise
1405     */
1406    public static boolean isFileEncryptedNativeOrEmulated() {
1407        return isFileEncryptedNativeOnly()
1408               || isFileEncryptedEmulatedOnly();
1409    }
1410
1411    /** {@hide} */
1412    public static File maybeTranslateEmulatedPathToInternal(File path) {
1413        // Disabled now that FUSE has been replaced by sdcardfs
1414        return path;
1415    }
1416
1417    /** {@hide} */
1418    @VisibleForTesting
1419    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
1420            int mode, ProxyFileDescriptorCallback callback, Handler handler, ThreadFactory factory)
1421                    throws IOException {
1422        Preconditions.checkNotNull(callback);
1423        MetricsLogger.count(mContext, "storage_open_proxy_file_descriptor", 1);
1424        // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before
1425        // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount
1426        // the bridge by calling mountProxyFileDescriptorBridge.
1427        while (true) {
1428            try {
1429                synchronized (mFuseAppLoopLock) {
1430                    boolean newlyCreated = false;
1431                    if (mFuseAppLoop == null) {
1432                        final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge();
1433                        if (mount == null) {
1434                            throw new IOException("Failed to mount proxy bridge");
1435                        }
1436                        mFuseAppLoop = new FuseAppLoop(mount.mountPointId, mount.fd, factory);
1437                        newlyCreated = true;
1438                    }
1439                    if (handler == null) {
1440                        handler = new Handler(Looper.getMainLooper());
1441                    }
1442                    try {
1443                        final int fileId = mFuseAppLoop.registerCallback(callback, handler);
1444                        final ParcelFileDescriptor pfd = mStorageManager.openProxyFileDescriptor(
1445                                mFuseAppLoop.getMountPointId(), fileId, mode);
1446                        if (pfd == null) {
1447                            mFuseAppLoop.unregisterCallback(fileId);
1448                            throw new FuseUnavailableMountException(
1449                                    mFuseAppLoop.getMountPointId());
1450                        }
1451                        return pfd;
1452                    } catch (FuseUnavailableMountException exception) {
1453                        // The bridge is being unmounted. Tried to recreate it unless the bridge was
1454                        // just created.
1455                        if (newlyCreated) {
1456                            throw new IOException(exception);
1457                        }
1458                        mFuseAppLoop = null;
1459                        continue;
1460                    }
1461                }
1462            } catch (RemoteException e) {
1463                // Cannot recover from remote exception.
1464                throw new IOException(e);
1465            }
1466        }
1467    }
1468
1469    /** {@hide} */
1470    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
1471            int mode, ProxyFileDescriptorCallback callback)
1472                    throws IOException {
1473        return openProxyFileDescriptor(mode, callback, null, null);
1474    }
1475
1476    /**
1477     * Opens a seekable {@link ParcelFileDescriptor} that proxies all low-level
1478     * I/O requests back to the given {@link ProxyFileDescriptorCallback}.
1479     * <p>
1480     * This can be useful when you want to provide quick access to a large file
1481     * that isn't backed by a real file on disk, such as a file on a network
1482     * share, cloud storage service, etc. As an example, you could respond to a
1483     * {@link ContentResolver#openFileDescriptor(android.net.Uri, String)}
1484     * request by returning a {@link ParcelFileDescriptor} created with this
1485     * method, and then stream the content on-demand as requested.
1486     * <p>
1487     * Another useful example might be where you have an encrypted file that
1488     * you're willing to decrypt on-demand, but where you want to avoid
1489     * persisting the cleartext version.
1490     *
1491     * @param mode The desired access mode, must be one of
1492     *            {@link ParcelFileDescriptor#MODE_READ_ONLY},
1493     *            {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or
1494     *            {@link ParcelFileDescriptor#MODE_READ_WRITE}
1495     * @param callback Callback to process file operation requests issued on
1496     *            returned file descriptor.
1497     * @param handler Handler that invokes callback methods.
1498     * @return Seekable ParcelFileDescriptor.
1499     * @throws IOException
1500     */
1501    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
1502            int mode, ProxyFileDescriptorCallback callback, Handler handler)
1503                    throws IOException {
1504        Preconditions.checkNotNull(handler);
1505        return openProxyFileDescriptor(mode, callback, handler, null);
1506    }
1507
1508    /** {@hide} */
1509    @VisibleForTesting
1510    public int getProxyFileDescriptorMountPointId() {
1511        synchronized (mFuseAppLoopLock) {
1512            return mFuseAppLoop != null ? mFuseAppLoop.getMountPointId() : -1;
1513        }
1514    }
1515
1516    /**
1517     * Return quota size in bytes for all cached data belonging to the calling
1518     * app on the given storage volume.
1519     * <p>
1520     * If your app goes above this quota, your cached files will be some of the
1521     * first to be deleted when additional disk space is needed. Conversely, if
1522     * your app stays under this quota, your cached files will be some of the
1523     * last to be deleted when additional disk space is needed.
1524     * <p>
1525     * This quota will change over time depending on how frequently the user
1526     * interacts with your app, and depending on how much system-wide disk space
1527     * is used.
1528     * <p class="note">
1529     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
1530     * then cached data for all packages in your shared UID is tracked together
1531     * as a single unit.
1532     * </p>
1533     *
1534     * @param storageUuid the UUID of the storage volume that you're interested
1535     *            in. The UUID for a specific path can be obtained using
1536     *            {@link #getUuidForPath(File)}.
1537     * @throws IOException when the storage device isn't present, or when it
1538     *             doesn't support cache quotas.
1539     * @see #getCacheSizeBytes(UUID)
1540     */
1541    @WorkerThread
1542    public @BytesLong long getCacheQuotaBytes(@NonNull UUID storageUuid) throws IOException {
1543        try {
1544            final ApplicationInfo app = mContext.getApplicationInfo();
1545            return mStorageManager.getCacheQuotaBytes(convert(storageUuid), app.uid);
1546        } catch (ParcelableException e) {
1547            e.maybeRethrow(IOException.class);
1548            throw new RuntimeException(e);
1549        } catch (RemoteException e) {
1550            throw e.rethrowFromSystemServer();
1551        }
1552    }
1553
1554    /** @removed */
1555    @Deprecated
1556    public long getCacheQuotaBytes(@NonNull File path) throws IOException {
1557        return getCacheQuotaBytes(getUuidForPath(path));
1558    }
1559
1560    /**
1561     * Return total size in bytes of all cached data belonging to the calling
1562     * app on the given storage volume.
1563     * <p>
1564     * Cached data tracked by this method always includes
1565     * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and
1566     * it also includes {@link Context#getExternalCacheDir()} if the primary
1567     * shared/external storage is hosted on the same storage device as your
1568     * private data.
1569     * <p class="note">
1570     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
1571     * then cached data for all packages in your shared UID is tracked together
1572     * as a single unit.
1573     * </p>
1574     *
1575     * @param storageUuid the UUID of the storage volume that you're interested
1576     *            in. The UUID for a specific path can be obtained using
1577     *            {@link #getUuidForPath(File)}.
1578     * @throws IOException when the storage device isn't present, or when it
1579     *             doesn't support cache quotas.
1580     * @see #getCacheQuotaBytes(UUID)
1581     */
1582    @WorkerThread
1583    public @BytesLong long getCacheSizeBytes(@NonNull UUID storageUuid) throws IOException {
1584        try {
1585            final ApplicationInfo app = mContext.getApplicationInfo();
1586            return mStorageManager.getCacheSizeBytes(convert(storageUuid), app.uid);
1587        } catch (ParcelableException e) {
1588            e.maybeRethrow(IOException.class);
1589            throw new RuntimeException(e);
1590        } catch (RemoteException e) {
1591            throw e.rethrowFromSystemServer();
1592        }
1593    }
1594
1595    /** @removed */
1596    @Deprecated
1597    public long getCacheSizeBytes(@NonNull File path) throws IOException {
1598        return getCacheSizeBytes(getUuidForPath(path));
1599    }
1600
1601    /** @removed */
1602    @Deprecated
1603    public long getCacheQuotaBytes() throws IOException {
1604        return getCacheQuotaBytes(mContext.getCacheDir());
1605    }
1606
1607    /** @removed */
1608    @Deprecated
1609    public long getCacheSizeBytes() throws IOException {
1610        return getCacheSizeBytes(mContext.getCacheDir());
1611    }
1612
1613    /** @removed */
1614    @Deprecated
1615    public long getExternalCacheQuotaBytes() throws IOException {
1616        return getCacheQuotaBytes(mContext.getExternalCacheDir());
1617    }
1618
1619    /** @removed */
1620    @Deprecated
1621    public long getExternalCacheSizeBytes() throws IOException {
1622        return getCacheSizeBytes(mContext.getExternalCacheDir());
1623    }
1624
1625    /**
1626     * Flag indicating that a disk space allocation request should operate in an
1627     * aggressive mode. This flag should only be rarely used in situations that
1628     * are critical to system health or security.
1629     * <p>
1630     * When set, the system is more aggressive about the data that it considers
1631     * for possible deletion when allocating disk space.
1632     * <p class="note">
1633     * Note: your app must hold the
1634     * {@link android.Manifest.permission#ALLOCATE_AGGRESSIVE} permission for
1635     * this flag to take effect.
1636     * </p>
1637     *
1638     * @see #getAllocatableBytes(UUID, int)
1639     * @see #allocateBytes(UUID, long, int)
1640     * @see #allocateBytes(FileDescriptor, long, int)
1641     * @hide
1642     */
1643    @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE)
1644    @SystemApi
1645    public static final int FLAG_ALLOCATE_AGGRESSIVE = 1 << 0;
1646
1647    /**
1648     * Flag indicating that a disk space allocation request should defy any
1649     * reserved disk space.
1650     *
1651     * @hide
1652     */
1653    public static final int FLAG_ALLOCATE_DEFY_RESERVED = 1 << 1;
1654
1655    /** @hide */
1656    @IntDef(flag = true, value = {
1657            FLAG_ALLOCATE_AGGRESSIVE,
1658            FLAG_ALLOCATE_DEFY_RESERVED,
1659    })
1660    @Retention(RetentionPolicy.SOURCE)
1661    public @interface AllocateFlags {}
1662
1663    /**
1664     * Return the maximum number of new bytes that your app can allocate for
1665     * itself on the given storage volume. This value is typically larger than
1666     * {@link File#getUsableSpace()}, since the system may be willing to delete
1667     * cached files to satisfy an allocation request. You can then allocate
1668     * space for yourself using {@link #allocateBytes(UUID, long, int)} or
1669     * {@link #allocateBytes(FileDescriptor, long, int)}.
1670     * <p>
1671     * This method is best used as a pre-flight check, such as deciding if there
1672     * is enough space to store an entire music album before you allocate space
1673     * for each audio file in the album. Attempts to allocate disk space beyond
1674     * the returned value will fail.
1675     * <p>
1676     * If the returned value is not large enough for the data you'd like to
1677     * persist, you can launch {@link #ACTION_MANAGE_STORAGE} with the
1678     * {@link #EXTRA_UUID} and {@link #EXTRA_REQUESTED_BYTES} options to help
1679     * involve the user in freeing up disk space.
1680     * <p>
1681     * If you're progressively allocating an unbounded amount of storage space
1682     * (such as when recording a video) you should avoid calling this method
1683     * more than once every 30 seconds.
1684     * <p class="note">
1685     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
1686     * then allocatable space for all packages in your shared UID is tracked
1687     * together as a single unit.
1688     * </p>
1689     *
1690     * @param storageUuid the UUID of the storage volume where you're
1691     *            considering allocating disk space, since allocatable space can
1692     *            vary widely depending on the underlying storage device. The
1693     *            UUID for a specific path can be obtained using
1694     *            {@link #getUuidForPath(File)}.
1695     * @return the maximum number of new bytes that the calling app can allocate
1696     *         using {@link #allocateBytes(UUID, long, int)} or
1697     *         {@link #allocateBytes(FileDescriptor, long, int)}.
1698     * @throws IOException when the storage device isn't present, or when it
1699     *             doesn't support allocating space.
1700     */
1701    @WorkerThread
1702    public @BytesLong long getAllocatableBytes(@NonNull UUID storageUuid)
1703            throws IOException {
1704        return getAllocatableBytes(storageUuid, 0);
1705    }
1706
1707    /** @hide */
1708    @SystemApi
1709    @WorkerThread
1710    @SuppressLint("Doclava125")
1711    public long getAllocatableBytes(@NonNull UUID storageUuid,
1712            @RequiresPermission @AllocateFlags int flags) throws IOException {
1713        try {
1714            return mStorageManager.getAllocatableBytes(convert(storageUuid), flags);
1715        } catch (ParcelableException e) {
1716            e.maybeRethrow(IOException.class);
1717            throw new RuntimeException(e);
1718        } catch (RemoteException e) {
1719            throw e.rethrowFromSystemServer();
1720        }
1721    }
1722
1723    /** @removed */
1724    @Deprecated
1725    @WorkerThread
1726    @SuppressLint("Doclava125")
1727    public long getAllocatableBytes(@NonNull File path,
1728            @RequiresPermission @AllocateFlags int flags) throws IOException {
1729        return getAllocatableBytes(getUuidForPath(path), flags);
1730    }
1731
1732    /**
1733     * Allocate the requested number of bytes for your application to use on the
1734     * given storage volume. This will cause the system to delete any cached
1735     * files necessary to satisfy your request.
1736     * <p>
1737     * Attempts to allocate disk space beyond the value returned by
1738     * {@link #getAllocatableBytes(UUID, int)} will fail.
1739     * <p>
1740     * Since multiple apps can be running simultaneously, this method may be
1741     * subject to race conditions. If possible, consider using
1742     * {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee
1743     * that bytes are allocated to an opened file.
1744     * <p>
1745     * If you're progressively allocating an unbounded amount of storage space
1746     * (such as when recording a video) you should avoid calling this method
1747     * more than once every 60 seconds.
1748     *
1749     * @param storageUuid the UUID of the storage volume where you'd like to
1750     *            allocate disk space. The UUID for a specific path can be
1751     *            obtained using {@link #getUuidForPath(File)}.
1752     * @param bytes the number of bytes to allocate.
1753     * @throws IOException when the storage device isn't present, or when it
1754     *             doesn't support allocating space, or if the device had
1755     *             trouble allocating the requested space.
1756     * @see #getAllocatableBytes(UUID, int)
1757     */
1758    @WorkerThread
1759    public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes)
1760            throws IOException {
1761        allocateBytes(storageUuid, bytes, 0);
1762    }
1763
1764    /** @hide */
1765    @SystemApi
1766    @WorkerThread
1767    @SuppressLint("Doclava125")
1768    public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes,
1769            @RequiresPermission @AllocateFlags int flags) throws IOException {
1770        try {
1771            mStorageManager.allocateBytes(convert(storageUuid), bytes, flags);
1772        } catch (ParcelableException e) {
1773            e.maybeRethrow(IOException.class);
1774        } catch (RemoteException e) {
1775            throw e.rethrowFromSystemServer();
1776        }
1777    }
1778
1779    /** @removed */
1780    @Deprecated
1781    @WorkerThread
1782    @SuppressLint("Doclava125")
1783    public void allocateBytes(@NonNull File path, @BytesLong long bytes,
1784            @RequiresPermission @AllocateFlags int flags) throws IOException {
1785        allocateBytes(getUuidForPath(path), bytes, flags);
1786    }
1787
1788    /**
1789     * Allocate the requested number of bytes for your application to use in the
1790     * given open file. This will cause the system to delete any cached files
1791     * necessary to satisfy your request.
1792     * <p>
1793     * Attempts to allocate disk space beyond the value returned by
1794     * {@link #getAllocatableBytes(UUID, int)} will fail.
1795     * <p>
1796     * This method guarantees that bytes have been allocated to the opened file,
1797     * otherwise it will throw if fast allocation is not possible. Fast
1798     * allocation is typically only supported in private app data directories,
1799     * and on shared/external storage devices which are emulated.
1800     * <p>
1801     * If you're progressively allocating an unbounded amount of storage space
1802     * (such as when recording a video) you should avoid calling this method
1803     * more than once every 60 seconds.
1804     *
1805     * @param fd the open file that you'd like to allocate disk space for.
1806     * @param bytes the number of bytes to allocate. This is the desired final
1807     *            size of the open file. If the open file is smaller than this
1808     *            requested size, it will be extended without modifying any
1809     *            existing contents. If the open file is larger than this
1810     *            requested size, it will be truncated.
1811     * @throws IOException when the storage device isn't present, or when it
1812     *             doesn't support allocating space, or if the device had
1813     *             trouble allocating the requested space.
1814     * @see #getAllocatableBytes(UUID, int)
1815     * @see Environment#isExternalStorageEmulated(File)
1816     */
1817    @WorkerThread
1818    public void allocateBytes(FileDescriptor fd, @BytesLong long bytes) throws IOException {
1819        allocateBytes(fd, bytes, 0);
1820    }
1821
1822    /** @hide */
1823    @SystemApi
1824    @WorkerThread
1825    @SuppressLint("Doclava125")
1826    public void allocateBytes(FileDescriptor fd, @BytesLong long bytes,
1827            @RequiresPermission @AllocateFlags int flags) throws IOException {
1828        final File file = ParcelFileDescriptor.getFile(fd);
1829        for (int i = 0; i < 3; i++) {
1830            try {
1831                final long haveBytes = Os.fstat(fd).st_blocks * 512;
1832                final long needBytes = bytes - haveBytes;
1833
1834                if (needBytes > 0) {
1835                    allocateBytes(file, needBytes, flags);
1836                }
1837
1838                Os.posix_fallocate(fd, 0, bytes);
1839                return;
1840            } catch (ErrnoException e) {
1841                if (e.errno == OsConstants.ENOSPC) {
1842                    Log.w(TAG, "Odd, not enough space; let's try again?");
1843                    continue;
1844                }
1845                throw e.rethrowAsIOException();
1846            }
1847        }
1848        throw new IOException(
1849                "Well this is embarassing; we can't allocate " + bytes + " for " + file);
1850    }
1851
1852    private static final String XATTR_CACHE_GROUP = "user.cache_group";
1853    private static final String XATTR_CACHE_TOMBSTONE = "user.cache_tombstone";
1854
1855    /** {@hide} */
1856    private static void setCacheBehavior(File path, String name, boolean enabled)
1857            throws IOException {
1858        if (!path.isDirectory()) {
1859            throw new IOException("Cache behavior can only be set on directories");
1860        }
1861        if (enabled) {
1862            try {
1863                Os.setxattr(path.getAbsolutePath(), name,
1864                        "1".getBytes(StandardCharsets.UTF_8), 0);
1865            } catch (ErrnoException e) {
1866                throw e.rethrowAsIOException();
1867            }
1868        } else {
1869            try {
1870                Os.removexattr(path.getAbsolutePath(), name);
1871            } catch (ErrnoException e) {
1872                if (e.errno != OsConstants.ENODATA) {
1873                    throw e.rethrowAsIOException();
1874                }
1875            }
1876        }
1877    }
1878
1879    /** {@hide} */
1880    private static boolean isCacheBehavior(File path, String name) throws IOException {
1881        try {
1882            Os.getxattr(path.getAbsolutePath(), name);
1883            return true;
1884        } catch (ErrnoException e) {
1885            if (e.errno != OsConstants.ENODATA) {
1886                throw e.rethrowAsIOException();
1887            } else {
1888                return false;
1889            }
1890        }
1891    }
1892
1893    /**
1894     * Enable or disable special cache behavior that treats this directory and
1895     * its contents as an entire group.
1896     * <p>
1897     * When enabled and this directory is considered for automatic deletion by
1898     * the OS, all contained files will either be deleted together, or not at
1899     * all. This is useful when you have a directory that contains several
1900     * related metadata files that depend on each other, such as movie file and
1901     * a subtitle file.
1902     * <p>
1903     * When enabled, the <em>newest</em> {@link File#lastModified()} value of
1904     * any contained files is considered the modified time of the entire
1905     * directory.
1906     * <p>
1907     * This behavior can only be set on a directory, and it applies recursively
1908     * to all contained files and directories.
1909     */
1910    public void setCacheBehaviorGroup(File path, boolean group) throws IOException {
1911        setCacheBehavior(path, XATTR_CACHE_GROUP, group);
1912    }
1913
1914    /**
1915     * Read the current value set by
1916     * {@link #setCacheBehaviorGroup(File, boolean)}.
1917     */
1918    public boolean isCacheBehaviorGroup(File path) throws IOException {
1919        return isCacheBehavior(path, XATTR_CACHE_GROUP);
1920    }
1921
1922    /** @removed */
1923    @Deprecated
1924    public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException {
1925        setCacheBehaviorGroup(path, atomic);
1926    }
1927
1928    /** @removed */
1929    @Deprecated
1930    public boolean isCacheBehaviorAtomic(File path) throws IOException {
1931        return isCacheBehaviorGroup(path);
1932    }
1933
1934    /**
1935     * Enable or disable special cache behavior that leaves deleted cache files
1936     * intact as tombstones.
1937     * <p>
1938     * When enabled and a file contained in this directory is automatically
1939     * deleted by the OS, the file will be truncated to have a length of 0 bytes
1940     * instead of being fully deleted. This is useful if you need to distinguish
1941     * between a file that was deleted versus one that never existed.
1942     * <p>
1943     * This behavior can only be set on a directory, and it applies recursively
1944     * to all contained files and directories.
1945     * <p class="note">
1946     * Note: this behavior is ignored completely if the user explicitly requests
1947     * that all cached data be cleared.
1948     * </p>
1949     */
1950    public void setCacheBehaviorTombstone(File path, boolean tombstone) throws IOException {
1951        setCacheBehavior(path, XATTR_CACHE_TOMBSTONE, tombstone);
1952    }
1953
1954    /**
1955     * Read the current value set by
1956     * {@link #setCacheBehaviorTombstone(File, boolean)}.
1957     */
1958    public boolean isCacheBehaviorTombstone(File path) throws IOException {
1959        return isCacheBehavior(path, XATTR_CACHE_TOMBSTONE);
1960    }
1961
1962    /** {@hide} */
1963    public static UUID convert(String uuid) {
1964        if (Objects.equals(uuid, UUID_PRIVATE_INTERNAL)) {
1965            return UUID_DEFAULT;
1966        } else if (Objects.equals(uuid, UUID_PRIMARY_PHYSICAL)) {
1967            return UUID_PRIMARY_PHYSICAL_;
1968        } else if (Objects.equals(uuid, UUID_SYSTEM)) {
1969            return UUID_SYSTEM_;
1970        } else {
1971            return UUID.fromString(uuid);
1972        }
1973    }
1974
1975    /** {@hide} */
1976    public static String convert(UUID storageUuid) {
1977        if (UUID_DEFAULT.equals(storageUuid)) {
1978            return UUID_PRIVATE_INTERNAL;
1979        } else if (UUID_PRIMARY_PHYSICAL_.equals(storageUuid)) {
1980            return UUID_PRIMARY_PHYSICAL;
1981        } else if (UUID_SYSTEM_.equals(storageUuid)) {
1982            return UUID_SYSTEM;
1983        } else {
1984            return storageUuid.toString();
1985        }
1986    }
1987
1988    private final Object mFuseAppLoopLock = new Object();
1989
1990    @GuardedBy("mFuseAppLoopLock")
1991    private @Nullable FuseAppLoop mFuseAppLoop = null;
1992
1993    /// Consts to match the password types in cryptfs.h
1994    /** @hide */
1995    public static final int CRYPT_TYPE_PASSWORD = 0;
1996    /** @hide */
1997    public static final int CRYPT_TYPE_DEFAULT = 1;
1998    /** @hide */
1999    public static final int CRYPT_TYPE_PATTERN = 2;
2000    /** @hide */
2001    public static final int CRYPT_TYPE_PIN = 3;
2002
2003    // Constants for the data available via StorageManagerService.getField.
2004    /** @hide */
2005    public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
2006    /** @hide */
2007    public static final String OWNER_INFO_KEY = "OwnerInfo";
2008    /** @hide */
2009    public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
2010    /** @hide */
2011    public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible";
2012}
2013