StorageManager.java revision 9fb00183a04036a58ee208f5bfd6c9768982f0aa
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.MB_IN_BYTES;
20
21import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.annotation.SdkConstant;
24import android.app.ActivityThread;
25import android.content.ContentResolver;
26import android.content.Context;
27import android.content.pm.IPackageMoveObserver;
28import android.content.pm.PackageManager;
29import android.os.Binder;
30import android.os.Environment;
31import android.os.FileUtils;
32import android.os.Handler;
33import android.os.ProxyFileDescriptorCallback;
34import android.os.Looper;
35import android.os.Message;
36import android.os.ParcelFileDescriptor;
37import android.os.RemoteException;
38import android.os.ServiceManager;
39import android.os.ServiceManager.ServiceNotFoundException;
40import android.os.SystemProperties;
41import android.os.UserHandle;
42import android.provider.Settings;
43import android.text.TextUtils;
44import android.util.Log;
45import android.util.Pair;
46import android.util.Slog;
47import android.util.SparseArray;
48import com.android.internal.annotations.GuardedBy;
49import com.android.internal.annotations.VisibleForTesting;
50import com.android.internal.os.AppFuseMount;
51import com.android.internal.os.FuseAppLoop;
52import com.android.internal.os.RoSystemProperties;
53import com.android.internal.os.SomeArgs;
54import com.android.internal.util.Preconditions;
55
56import java.io.BufferedReader;
57import java.io.File;
58import java.io.FileInputStream;
59import java.io.FileNotFoundException;
60import java.io.IOException;
61import java.io.InputStreamReader;
62import java.lang.ref.WeakReference;
63import java.util.ArrayList;
64import java.util.Arrays;
65import java.util.Collections;
66import java.util.Iterator;
67import java.util.List;
68import java.util.Objects;
69import java.util.concurrent.ThreadFactory;
70import java.util.concurrent.atomic.AtomicInteger;
71
72/**
73 * StorageManager is the interface to the systems storage service. The storage
74 * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
75 * <p>
76 * OBBs contain a filesystem that maybe be encrypted on disk and mounted
77 * on-demand from an application. OBBs are a good way of providing large amounts
78 * of binary assets without packaging them into APKs as they may be multiple
79 * gigabytes in size. However, due to their size, they're most likely stored in
80 * a shared storage pool accessible from all programs. The system does not
81 * guarantee the security of the OBB file itself: if any program modifies the
82 * OBB, there is no guarantee that a read from that OBB will produce the
83 * expected output.
84 * <p>
85 * Get an instance of this class by calling
86 * {@link android.content.Context#getSystemService(java.lang.String)} with an
87 * argument of {@link android.content.Context#STORAGE_SERVICE}.
88 */
89public class StorageManager {
90    private static final String TAG = "StorageManager";
91
92    /** {@hide} */
93    public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
94    /** {@hide} */
95    public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
96    /** {@hide} */
97    public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
98    /** {@hide} */
99    public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe";
100    /** {@hide} */
101    public static final String PROP_SDCARDFS = "persist.sys.sdcardfs";
102
103    /** {@hide} */
104    public static final String UUID_PRIVATE_INTERNAL = null;
105    /** {@hide} */
106    public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
107
108
109    /**
110     * Activity Action: Allows the user to manage their storage. This activity provides the ability
111     * to free up space on the device by deleting data such as apps.
112     * <p>
113     * Input: Nothing.
114     * <p>
115     * Output: Nothing.
116     */
117    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
118    public static final String ACTION_MANAGE_STORAGE
119            = "android.os.storage.action.MANAGE_STORAGE";
120
121    /** {@hide} */
122    public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
123    /** {@hide} */
124    public static final int DEBUG_EMULATE_FBE = 1 << 1;
125    /** {@hide} */
126    public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2;
127    /** {@hide} */
128    public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3;
129
130    // NOTE: keep in sync with installd
131    /** {@hide} */
132    public static final int FLAG_STORAGE_DE = 1 << 0;
133    /** {@hide} */
134    public static final int FLAG_STORAGE_CE = 1 << 1;
135
136    /** {@hide} */
137    public static final int FLAG_FOR_WRITE = 1 << 8;
138    /** {@hide} */
139    public static final int FLAG_REAL_STATE = 1 << 9;
140    /** {@hide} */
141    public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
142
143    /** {@hide} */
144    public static final int FSTRIM_FLAG_DEEP = 1 << 0;
145    /** {@hide} */
146    public static final int FSTRIM_FLAG_BENCHMARK = 1 << 1;
147
148    /** @hide The volume is not encrypted. */
149    public static final int ENCRYPTION_STATE_NONE = 1;
150
151    /** @hide The volume has been encrypted succesfully. */
152    public static final int ENCRYPTION_STATE_OK = 0;
153
154    /** @hide The volume is in a bad state.*/
155    public static final int ENCRYPTION_STATE_ERROR_UNKNOWN = -1;
156
157    /** @hide Encryption is incomplete */
158    public static final int ENCRYPTION_STATE_ERROR_INCOMPLETE = -2;
159
160    /** @hide Encryption is incomplete and irrecoverable */
161    public static final int ENCRYPTION_STATE_ERROR_INCONSISTENT = -3;
162
163    /** @hide Underlying data is corrupt */
164    public static final int ENCRYPTION_STATE_ERROR_CORRUPT = -4;
165
166    private static volatile IStorageManager sStorageManager = null;
167
168    // TODO: the location of the primary storage block varies from device to device, so we need to
169    // try the most likely candidates - a long-term solution would be a device-specific vold
170    // function that returns the calculated size.
171    private static final String[] INTERNAL_STORAGE_SIZE_PATHS = {
172            "/sys/block/mmcblk0/size",
173            "/sys/block/sda/size"
174    };
175    private static final int INTERNAL_STORAGE_SECTOR_SIZE = 512;
176
177    private final Context mContext;
178    private final ContentResolver mResolver;
179
180    private final IStorageManager mStorageManager;
181    private final Looper mLooper;
182    private final AtomicInteger mNextNonce = new AtomicInteger(0);
183
184    private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>();
185
186    private static class StorageEventListenerDelegate extends IStorageEventListener.Stub implements
187            Handler.Callback {
188        private static final int MSG_STORAGE_STATE_CHANGED = 1;
189        private static final int MSG_VOLUME_STATE_CHANGED = 2;
190        private static final int MSG_VOLUME_RECORD_CHANGED = 3;
191        private static final int MSG_VOLUME_FORGOTTEN = 4;
192        private static final int MSG_DISK_SCANNED = 5;
193        private static final int MSG_DISK_DESTROYED = 6;
194
195        final StorageEventListener mCallback;
196        final Handler mHandler;
197
198        public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
199            mCallback = callback;
200            mHandler = new Handler(looper, this);
201        }
202
203        @Override
204        public boolean handleMessage(Message msg) {
205            final SomeArgs args = (SomeArgs) msg.obj;
206            switch (msg.what) {
207                case MSG_STORAGE_STATE_CHANGED:
208                    mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
209                            (String) args.arg3);
210                    args.recycle();
211                    return true;
212                case MSG_VOLUME_STATE_CHANGED:
213                    mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
214                    args.recycle();
215                    return true;
216                case MSG_VOLUME_RECORD_CHANGED:
217                    mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1);
218                    args.recycle();
219                    return true;
220                case MSG_VOLUME_FORGOTTEN:
221                    mCallback.onVolumeForgotten((String) args.arg1);
222                    args.recycle();
223                    return true;
224                case MSG_DISK_SCANNED:
225                    mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
226                    args.recycle();
227                    return true;
228                case MSG_DISK_DESTROYED:
229                    mCallback.onDiskDestroyed((DiskInfo) args.arg1);
230                    args.recycle();
231                    return true;
232            }
233            args.recycle();
234            return false;
235        }
236
237        @Override
238        public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
239            // Ignored
240        }
241
242        @Override
243        public void onStorageStateChanged(String path, String oldState, String newState) {
244            final SomeArgs args = SomeArgs.obtain();
245            args.arg1 = path;
246            args.arg2 = oldState;
247            args.arg3 = newState;
248            mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
249        }
250
251        @Override
252        public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
253            final SomeArgs args = SomeArgs.obtain();
254            args.arg1 = vol;
255            args.argi2 = oldState;
256            args.argi3 = newState;
257            mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
258        }
259
260        @Override
261        public void onVolumeRecordChanged(VolumeRecord rec) {
262            final SomeArgs args = SomeArgs.obtain();
263            args.arg1 = rec;
264            mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
265        }
266
267        @Override
268        public void onVolumeForgotten(String fsUuid) {
269            final SomeArgs args = SomeArgs.obtain();
270            args.arg1 = fsUuid;
271            mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
272        }
273
274        @Override
275        public void onDiskScanned(DiskInfo disk, int volumeCount) {
276            final SomeArgs args = SomeArgs.obtain();
277            args.arg1 = disk;
278            args.argi2 = volumeCount;
279            mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
280        }
281
282        @Override
283        public void onDiskDestroyed(DiskInfo disk) throws RemoteException {
284            final SomeArgs args = SomeArgs.obtain();
285            args.arg1 = disk;
286            mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
287        }
288    }
289
290    /**
291     * Binder listener for OBB action results.
292     */
293    private final ObbActionListener mObbActionListener = new ObbActionListener();
294
295    private class ObbActionListener extends IObbActionListener.Stub {
296        @SuppressWarnings("hiding")
297        private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
298
299        @Override
300        public void onObbResult(String filename, int nonce, int status) {
301            final ObbListenerDelegate delegate;
302            synchronized (mListeners) {
303                delegate = mListeners.get(nonce);
304                if (delegate != null) {
305                    mListeners.remove(nonce);
306                }
307            }
308
309            if (delegate != null) {
310                delegate.sendObbStateChanged(filename, status);
311            }
312        }
313
314        public int addListener(OnObbStateChangeListener listener) {
315            final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
316
317            synchronized (mListeners) {
318                mListeners.put(delegate.nonce, delegate);
319            }
320
321            return delegate.nonce;
322        }
323    }
324
325    private int getNextNonce() {
326        return mNextNonce.getAndIncrement();
327    }
328
329    /**
330     * Private class containing sender and receiver code for StorageEvents.
331     */
332    private class ObbListenerDelegate {
333        private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
334        private final Handler mHandler;
335
336        private final int nonce;
337
338        ObbListenerDelegate(OnObbStateChangeListener listener) {
339            nonce = getNextNonce();
340            mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
341            mHandler = new Handler(mLooper) {
342                @Override
343                public void handleMessage(Message msg) {
344                    final OnObbStateChangeListener changeListener = getListener();
345                    if (changeListener == null) {
346                        return;
347                    }
348
349                    changeListener.onObbStateChange((String) msg.obj, msg.arg1);
350                }
351            };
352        }
353
354        OnObbStateChangeListener getListener() {
355            if (mObbEventListenerRef == null) {
356                return null;
357            }
358            return mObbEventListenerRef.get();
359        }
360
361        void sendObbStateChanged(String path, int state) {
362            mHandler.obtainMessage(0, state, 0, path).sendToTarget();
363        }
364    }
365
366    /** {@hide} */
367    @Deprecated
368    public static StorageManager from(Context context) {
369        return context.getSystemService(StorageManager.class);
370    }
371
372    /**
373     * Constructs a StorageManager object through which an application can
374     * can communicate with the systems mount service.
375     *
376     * @param looper The {@link android.os.Looper} which events will be received on.
377     *
378     * <p>Applications can get instance of this class by calling
379     * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
380     * of {@link android.content.Context#STORAGE_SERVICE}.
381     *
382     * @hide
383     */
384    public StorageManager(Context context, Looper looper) throws ServiceNotFoundException {
385        mContext = context;
386        mResolver = context.getContentResolver();
387        mLooper = looper;
388        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount"));
389    }
390
391    /**
392     * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
393     *
394     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
395     *
396     * @hide
397     */
398    public void registerListener(StorageEventListener listener) {
399        synchronized (mDelegates) {
400            final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener,
401                    mLooper);
402            try {
403                mStorageManager.registerListener(delegate);
404            } catch (RemoteException e) {
405                throw e.rethrowFromSystemServer();
406            }
407            mDelegates.add(delegate);
408        }
409    }
410
411    /**
412     * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
413     *
414     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
415     *
416     * @hide
417     */
418    public void unregisterListener(StorageEventListener listener) {
419        synchronized (mDelegates) {
420            for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
421                final StorageEventListenerDelegate delegate = i.next();
422                if (delegate.mCallback == listener) {
423                    try {
424                        mStorageManager.unregisterListener(delegate);
425                    } catch (RemoteException e) {
426                        throw e.rethrowFromSystemServer();
427                    }
428                    i.remove();
429                }
430            }
431        }
432    }
433
434    /**
435     * Enables USB Mass Storage (UMS) on the device.
436     *
437     * @hide
438     */
439    @Deprecated
440    public void enableUsbMassStorage() {
441    }
442
443    /**
444     * Disables USB Mass Storage (UMS) on the device.
445     *
446     * @hide
447     */
448    @Deprecated
449    public void disableUsbMassStorage() {
450    }
451
452    /**
453     * Query if a USB Mass Storage (UMS) host is connected.
454     * @return true if UMS host is connected.
455     *
456     * @hide
457     */
458    @Deprecated
459    public boolean isUsbMassStorageConnected() {
460        return false;
461    }
462
463    /**
464     * Query if a USB Mass Storage (UMS) is enabled on the device.
465     * @return true if UMS host is enabled.
466     *
467     * @hide
468     */
469    @Deprecated
470    public boolean isUsbMassStorageEnabled() {
471        return false;
472    }
473
474    /**
475     * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
476     * specified, it is supplied to the mounting process to be used in any
477     * encryption used in the OBB.
478     * <p>
479     * The OBB will remain mounted for as long as the StorageManager reference
480     * is held by the application. As soon as this reference is lost, the OBBs
481     * in use will be unmounted. The {@link OnObbStateChangeListener} registered
482     * with this call will receive the success or failure of this operation.
483     * <p>
484     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
485     * file matches a package ID that is owned by the calling program's UID.
486     * That is, shared UID applications can attempt to mount any other
487     * application's OBB that shares its UID.
488     *
489     * @param rawPath the path to the OBB file
490     * @param key secret used to encrypt the OBB; may be <code>null</code> if no
491     *            encryption was used on the OBB.
492     * @param listener will receive the success or failure of the operation
493     * @return whether the mount call was successfully queued or not
494     */
495    public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
496        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
497        Preconditions.checkNotNull(listener, "listener cannot be null");
498
499        try {
500            final String canonicalPath = new File(rawPath).getCanonicalPath();
501            final int nonce = mObbActionListener.addListener(listener);
502            mStorageManager.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce);
503            return true;
504        } catch (IOException e) {
505            throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e);
506        } catch (RemoteException e) {
507            throw e.rethrowFromSystemServer();
508        }
509    }
510
511    /**
512     * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
513     * <code>force</code> flag is true, it will kill any application needed to
514     * unmount the given OBB (even the calling application).
515     * <p>
516     * The {@link OnObbStateChangeListener} registered with this call will
517     * receive the success or failure of this operation.
518     * <p>
519     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
520     * file matches a package ID that is owned by the calling program's UID.
521     * That is, shared UID applications can obtain access to any other
522     * application's OBB that shares its UID.
523     * <p>
524     *
525     * @param rawPath path to the OBB file
526     * @param force whether to kill any programs using this in order to unmount
527     *            it
528     * @param listener will receive the success or failure of the operation
529     * @return whether the unmount call was successfully queued or not
530     */
531    public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
532        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
533        Preconditions.checkNotNull(listener, "listener cannot be null");
534
535        try {
536            final int nonce = mObbActionListener.addListener(listener);
537            mStorageManager.unmountObb(rawPath, force, mObbActionListener, nonce);
538            return true;
539        } catch (RemoteException e) {
540            throw e.rethrowFromSystemServer();
541        }
542    }
543
544    /**
545     * Check whether an Opaque Binary Blob (OBB) is mounted or not.
546     *
547     * @param rawPath path to OBB image
548     * @return true if OBB is mounted; false if not mounted or on error
549     */
550    public boolean isObbMounted(String rawPath) {
551        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
552
553        try {
554            return mStorageManager.isObbMounted(rawPath);
555        } catch (RemoteException e) {
556            throw e.rethrowFromSystemServer();
557        }
558    }
559
560    /**
561     * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
562     * give you the path to where you can obtain access to the internals of the
563     * OBB.
564     *
565     * @param rawPath path to OBB image
566     * @return absolute path to mounted OBB image data or <code>null</code> if
567     *         not mounted or exception encountered trying to read status
568     */
569    public String getMountedObbPath(String rawPath) {
570        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
571
572        try {
573            return mStorageManager.getMountedObbPath(rawPath);
574        } catch (RemoteException e) {
575            throw e.rethrowFromSystemServer();
576        }
577    }
578
579    /** {@hide} */
580    public @NonNull List<DiskInfo> getDisks() {
581        try {
582            return Arrays.asList(mStorageManager.getDisks());
583        } catch (RemoteException e) {
584            throw e.rethrowFromSystemServer();
585        }
586    }
587
588    /** {@hide} */
589    public @Nullable DiskInfo findDiskById(String id) {
590        Preconditions.checkNotNull(id);
591        // TODO; go directly to service to make this faster
592        for (DiskInfo disk : getDisks()) {
593            if (Objects.equals(disk.id, id)) {
594                return disk;
595            }
596        }
597        return null;
598    }
599
600    /** {@hide} */
601    public @Nullable VolumeInfo findVolumeById(String id) {
602        Preconditions.checkNotNull(id);
603        // TODO; go directly to service to make this faster
604        for (VolumeInfo vol : getVolumes()) {
605            if (Objects.equals(vol.id, id)) {
606                return vol;
607            }
608        }
609        return null;
610    }
611
612    /** {@hide} */
613    public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
614        Preconditions.checkNotNull(fsUuid);
615        // TODO; go directly to service to make this faster
616        for (VolumeInfo vol : getVolumes()) {
617            if (Objects.equals(vol.fsUuid, fsUuid)) {
618                return vol;
619            }
620        }
621        return null;
622    }
623
624    /** {@hide} */
625    public @Nullable VolumeRecord findRecordByUuid(String fsUuid) {
626        Preconditions.checkNotNull(fsUuid);
627        // TODO; go directly to service to make this faster
628        for (VolumeRecord rec : getVolumeRecords()) {
629            if (Objects.equals(rec.fsUuid, fsUuid)) {
630                return rec;
631            }
632        }
633        return null;
634    }
635
636    /** {@hide} */
637    public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
638        if (emulatedVol != null) {
639            return findVolumeById(emulatedVol.getId().replace("emulated", "private"));
640        } else {
641            return null;
642        }
643    }
644
645    /** {@hide} */
646    public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
647        if (privateVol != null) {
648            return findVolumeById(privateVol.getId().replace("private", "emulated"));
649        } else {
650            return null;
651        }
652    }
653
654    /** {@hide} */
655    public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
656        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
657            return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
658        } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
659            return getPrimaryPhysicalVolume();
660        } else {
661            return findVolumeByUuid(volumeUuid);
662        }
663    }
664
665    /** {@hide} */
666    public @NonNull List<VolumeInfo> getVolumes() {
667        try {
668            return Arrays.asList(mStorageManager.getVolumes(0));
669        } catch (RemoteException e) {
670            throw e.rethrowFromSystemServer();
671        }
672    }
673
674    /** {@hide} */
675    public @NonNull List<VolumeInfo> getWritablePrivateVolumes() {
676        try {
677            final ArrayList<VolumeInfo> res = new ArrayList<>();
678            for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
679                if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
680                    res.add(vol);
681                }
682            }
683            return res;
684        } catch (RemoteException e) {
685            throw e.rethrowFromSystemServer();
686        }
687    }
688
689    /** {@hide} */
690    public @NonNull List<VolumeRecord> getVolumeRecords() {
691        try {
692            return Arrays.asList(mStorageManager.getVolumeRecords(0));
693        } catch (RemoteException e) {
694            throw e.rethrowFromSystemServer();
695        }
696    }
697
698    /** {@hide} */
699    public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
700        if (vol == null) return null;
701
702        // Nickname always takes precedence when defined
703        if (!TextUtils.isEmpty(vol.fsUuid)) {
704            final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
705            if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
706                return rec.nickname;
707            }
708        }
709
710        if (!TextUtils.isEmpty(vol.getDescription())) {
711            return vol.getDescription();
712        }
713
714        if (vol.disk != null) {
715            return vol.disk.getDescription();
716        }
717
718        return null;
719    }
720
721    /** {@hide} */
722    public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
723        final List<VolumeInfo> vols = getVolumes();
724        for (VolumeInfo vol : vols) {
725            if (vol.isPrimaryPhysical()) {
726                return vol;
727            }
728        }
729        return null;
730    }
731
732    /** {@hide} */
733    public void mount(String volId) {
734        try {
735            mStorageManager.mount(volId);
736        } catch (RemoteException e) {
737            throw e.rethrowFromSystemServer();
738        }
739    }
740
741    /** {@hide} */
742    public void unmount(String volId) {
743        try {
744            mStorageManager.unmount(volId);
745        } catch (RemoteException e) {
746            throw e.rethrowFromSystemServer();
747        }
748    }
749
750    /** {@hide} */
751    public void format(String volId) {
752        try {
753            mStorageManager.format(volId);
754        } catch (RemoteException e) {
755            throw e.rethrowFromSystemServer();
756        }
757    }
758
759    /** {@hide} */
760    public long benchmark(String volId) {
761        try {
762            return mStorageManager.benchmark(volId);
763        } catch (RemoteException e) {
764            throw e.rethrowFromSystemServer();
765        }
766    }
767
768    /** {@hide} */
769    public void partitionPublic(String diskId) {
770        try {
771            mStorageManager.partitionPublic(diskId);
772        } catch (RemoteException e) {
773            throw e.rethrowFromSystemServer();
774        }
775    }
776
777    /** {@hide} */
778    public void partitionPrivate(String diskId) {
779        try {
780            mStorageManager.partitionPrivate(diskId);
781        } catch (RemoteException e) {
782            throw e.rethrowFromSystemServer();
783        }
784    }
785
786    /** {@hide} */
787    public void partitionMixed(String diskId, int ratio) {
788        try {
789            mStorageManager.partitionMixed(diskId, ratio);
790        } catch (RemoteException e) {
791            throw e.rethrowFromSystemServer();
792        }
793    }
794
795    /** {@hide} */
796    public void wipeAdoptableDisks() {
797        // We only wipe devices in "adoptable" locations, which are in a
798        // long-term stable slot/location on the device, where apps have a
799        // reasonable chance of storing sensitive data. (Apps need to go through
800        // SAF to write to transient volumes.)
801        final List<DiskInfo> disks = getDisks();
802        for (DiskInfo disk : disks) {
803            final String diskId = disk.getId();
804            if (disk.isAdoptable()) {
805                Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
806                try {
807                    // TODO: switch to explicit wipe command when we have it,
808                    // for now rely on the fact that vfat format does a wipe
809                    mStorageManager.partitionPublic(diskId);
810                } catch (Exception e) {
811                    Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
812                }
813            } else {
814                Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
815            }
816        }
817    }
818
819    /** {@hide} */
820    public void setVolumeNickname(String fsUuid, String nickname) {
821        try {
822            mStorageManager.setVolumeNickname(fsUuid, nickname);
823        } catch (RemoteException e) {
824            throw e.rethrowFromSystemServer();
825        }
826    }
827
828    /** {@hide} */
829    public void setVolumeInited(String fsUuid, boolean inited) {
830        try {
831            mStorageManager.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0,
832                    VolumeRecord.USER_FLAG_INITED);
833        } catch (RemoteException e) {
834            throw e.rethrowFromSystemServer();
835        }
836    }
837
838    /** {@hide} */
839    public void setVolumeSnoozed(String fsUuid, boolean snoozed) {
840        try {
841            mStorageManager.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0,
842                    VolumeRecord.USER_FLAG_SNOOZED);
843        } catch (RemoteException e) {
844            throw e.rethrowFromSystemServer();
845        }
846    }
847
848    /** {@hide} */
849    public void forgetVolume(String fsUuid) {
850        try {
851            mStorageManager.forgetVolume(fsUuid);
852        } catch (RemoteException e) {
853            throw e.rethrowFromSystemServer();
854        }
855    }
856
857    /**
858     * This is not the API you're looking for.
859     *
860     * @see PackageManager#getPrimaryStorageCurrentVolume()
861     * @hide
862     */
863    public String getPrimaryStorageUuid() {
864        try {
865            return mStorageManager.getPrimaryStorageUuid();
866        } catch (RemoteException e) {
867            throw e.rethrowFromSystemServer();
868        }
869    }
870
871    /**
872     * This is not the API you're looking for.
873     *
874     * @see PackageManager#movePrimaryStorage(VolumeInfo)
875     * @hide
876     */
877    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
878        try {
879            mStorageManager.setPrimaryStorageUuid(volumeUuid, callback);
880        } catch (RemoteException e) {
881            throw e.rethrowFromSystemServer();
882        }
883    }
884
885    /**
886     * Return the {@link StorageVolume} that contains the given file, or {@code null} if none.
887     */
888    public @Nullable StorageVolume getStorageVolume(File file) {
889        return getStorageVolume(getVolumeList(), file);
890    }
891
892    /** {@hide} */
893    public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
894        return getStorageVolume(getVolumeList(userId, 0), file);
895    }
896
897    /** {@hide} */
898    private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
899        if (file == null) {
900            return null;
901        }
902        try {
903            file = file.getCanonicalFile();
904        } catch (IOException ignored) {
905            Slog.d(TAG, "Could not get canonical path for " + file);
906            return null;
907        }
908        for (StorageVolume volume : volumes) {
909            File volumeFile = volume.getPathFile();
910            try {
911                volumeFile = volumeFile.getCanonicalFile();
912            } catch (IOException ignored) {
913                continue;
914            }
915            if (FileUtils.contains(volumeFile, file)) {
916                return volume;
917            }
918        }
919        return null;
920    }
921
922    /**
923     * Gets the state of a volume via its mountpoint.
924     * @hide
925     */
926    @Deprecated
927    public @NonNull String getVolumeState(String mountPoint) {
928        final StorageVolume vol = getStorageVolume(new File(mountPoint));
929        if (vol != null) {
930            return vol.getState();
931        } else {
932            return Environment.MEDIA_UNKNOWN;
933        }
934    }
935
936    /**
937     * Return the list of shared/external storage volumes available to the
938     * current user. This includes both the primary shared storage device and
939     * any attached external volumes including SD cards and USB drives.
940     *
941     * @see Environment#getExternalStorageDirectory()
942     * @see StorageVolume#createAccessIntent(String)
943     */
944    public @NonNull List<StorageVolume> getStorageVolumes() {
945        final ArrayList<StorageVolume> res = new ArrayList<>();
946        Collections.addAll(res,
947                getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
948        return res;
949    }
950
951    /**
952     * Return the primary shared/external storage volume available to the
953     * current user. This volume is the same storage device returned by
954     * {@link Environment#getExternalStorageDirectory()} and
955     * {@link Context#getExternalFilesDir(String)}.
956     */
957    public @NonNull StorageVolume getPrimaryStorageVolume() {
958        return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
959    }
960
961    /** {@hide} */
962    public static Pair<String, Long> getPrimaryStoragePathAndSize() {
963        for (String path : INTERNAL_STORAGE_SIZE_PATHS) {
964            final long numberBlocks = readLong(path);
965            if (numberBlocks > 0) {
966                return new Pair<>(path, Long.valueOf(numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE));
967            }
968        }
969        return null;
970    }
971
972
973    /** {@hide} */
974    public long getPrimaryStorageSize() {
975        final Pair<String, Long> pair = getPrimaryStoragePathAndSize();
976        return pair == null ? 0 : pair.second.longValue();
977    }
978
979    private static long readLong(String path) {
980        try (final FileInputStream fis = new FileInputStream(path);
981                final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) {
982            return Long.parseLong(reader.readLine());
983        } catch (FileNotFoundException e) {
984            // This is expected since we are trying to parse multiple paths.
985            Slog.i(TAG, "readLong(): Path doesn't exist: " + path + ": " + e);
986            return 0;
987        } catch (NumberFormatException e) {
988            Slog.e(TAG, "readLong(): Could not parse " + path + ": " + e);
989            return 0;
990        } catch (Exception e) {
991            Slog.e(TAG, "readLong(): Unknown exception while opening " + path + ": " + e);
992            return 0;
993       }
994    }
995
996    /** @removed */
997    public @NonNull StorageVolume[] getVolumeList() {
998        return getVolumeList(mContext.getUserId(), 0);
999    }
1000
1001    /** {@hide} */
1002    public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
1003        final IStorageManager storageManager = IStorageManager.Stub.asInterface(
1004                ServiceManager.getService("mount"));
1005        try {
1006            String packageName = ActivityThread.currentOpPackageName();
1007            if (packageName == null) {
1008                // Package name can be null if the activity thread is running but the app
1009                // hasn't bound yet. In this case we fall back to the first package in the
1010                // current UID. This works for runtime permissions as permission state is
1011                // per UID and permission realted app ops are updated for all UID packages.
1012                String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
1013                        android.os.Process.myUid());
1014                if (packageNames == null || packageNames.length <= 0) {
1015                    return new StorageVolume[0];
1016                }
1017                packageName = packageNames[0];
1018            }
1019            final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
1020                    PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
1021            if (uid <= 0) {
1022                return new StorageVolume[0];
1023            }
1024            return storageManager.getVolumeList(uid, packageName, flags);
1025        } catch (RemoteException e) {
1026            throw e.rethrowFromSystemServer();
1027        }
1028    }
1029
1030    /**
1031     * Returns list of paths for all mountable volumes.
1032     * @hide
1033     */
1034    @Deprecated
1035    public @NonNull String[] getVolumePaths() {
1036        StorageVolume[] volumes = getVolumeList();
1037        int count = volumes.length;
1038        String[] paths = new String[count];
1039        for (int i = 0; i < count; i++) {
1040            paths[i] = volumes[i].getPath();
1041        }
1042        return paths;
1043    }
1044
1045    /** @removed */
1046    public @NonNull StorageVolume getPrimaryVolume() {
1047        return getPrimaryVolume(getVolumeList());
1048    }
1049
1050    /** {@hide} */
1051    public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
1052        for (StorageVolume volume : volumes) {
1053            if (volume.isPrimary()) {
1054                return volume;
1055            }
1056        }
1057        throw new IllegalStateException("Missing primary storage");
1058    }
1059
1060    /** {@hide} */
1061    private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
1062    private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
1063    private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
1064
1065    /**
1066     * Return the number of available bytes until the given path is considered
1067     * running low on storage.
1068     *
1069     * @hide
1070     */
1071    public long getStorageBytesUntilLow(File path) {
1072        return path.getUsableSpace() - getStorageFullBytes(path);
1073    }
1074
1075    /**
1076     * Return the number of available bytes at which the given path is
1077     * considered running low on storage.
1078     *
1079     * @hide
1080     */
1081    public long getStorageLowBytes(File path) {
1082        final long lowPercent = Settings.Global.getInt(mResolver,
1083                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
1084        final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
1085
1086        final long maxLowBytes = Settings.Global.getLong(mResolver,
1087                Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
1088
1089        return Math.min(lowBytes, maxLowBytes);
1090    }
1091
1092    /**
1093     * Return the number of available bytes at which the given path is
1094     * considered full.
1095     *
1096     * @hide
1097     */
1098    public long getStorageFullBytes(File path) {
1099        return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
1100                DEFAULT_FULL_THRESHOLD_BYTES);
1101    }
1102
1103    /** {@hide} */
1104    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
1105        try {
1106            mStorageManager.createUserKey(userId, serialNumber, ephemeral);
1107        } catch (RemoteException e) {
1108            throw e.rethrowFromSystemServer();
1109        }
1110    }
1111
1112    /** {@hide} */
1113    public void destroyUserKey(int userId) {
1114        try {
1115            mStorageManager.destroyUserKey(userId);
1116        } catch (RemoteException e) {
1117            throw e.rethrowFromSystemServer();
1118        }
1119    }
1120
1121    /** {@hide} */
1122    public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
1123        try {
1124            mStorageManager.unlockUserKey(userId, serialNumber, token, secret);
1125        } catch (RemoteException e) {
1126            throw e.rethrowFromSystemServer();
1127        }
1128    }
1129
1130    /** {@hide} */
1131    public void lockUserKey(int userId) {
1132        try {
1133            mStorageManager.lockUserKey(userId);
1134        } catch (RemoteException e) {
1135            throw e.rethrowFromSystemServer();
1136        }
1137    }
1138
1139    /** {@hide} */
1140    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
1141        try {
1142            mStorageManager.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
1143        } catch (RemoteException e) {
1144            throw e.rethrowFromSystemServer();
1145        }
1146    }
1147
1148    /** {@hide} */
1149    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
1150        try {
1151            mStorageManager.destroyUserStorage(volumeUuid, userId, flags);
1152        } catch (RemoteException e) {
1153            throw e.rethrowFromSystemServer();
1154        }
1155    }
1156
1157    /** {@hide} */
1158    public static boolean isUserKeyUnlocked(int userId) {
1159        if (sStorageManager == null) {
1160            sStorageManager = IStorageManager.Stub
1161                    .asInterface(ServiceManager.getService("mount"));
1162        }
1163        if (sStorageManager == null) {
1164            Slog.w(TAG, "Early during boot, assuming locked");
1165            return false;
1166        }
1167        final long token = Binder.clearCallingIdentity();
1168        try {
1169            return sStorageManager.isUserKeyUnlocked(userId);
1170        } catch (RemoteException e) {
1171            throw e.rethrowAsRuntimeException();
1172        } finally {
1173            Binder.restoreCallingIdentity(token);
1174        }
1175    }
1176
1177    /**
1178     * Return if data stored at or under the given path will be encrypted while
1179     * at rest. This can help apps avoid the overhead of double-encrypting data.
1180     */
1181    public boolean isEncrypted(File file) {
1182        if (FileUtils.contains(Environment.getDataDirectory(), file)) {
1183            return isEncrypted();
1184        } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) {
1185            return true;
1186        }
1187        // TODO: extend to support shared storage
1188        return false;
1189    }
1190
1191    /** {@hide}
1192     * Is this device encryptable or already encrypted?
1193     * @return true for encryptable or encrypted
1194     *         false not encrypted and not encryptable
1195     */
1196    public static boolean isEncryptable() {
1197        return RoSystemProperties.CRYPTO_ENCRYPTABLE;
1198    }
1199
1200    /** {@hide}
1201     * Is this device already encrypted?
1202     * @return true for encrypted. (Implies isEncryptable() == true)
1203     *         false not encrypted
1204     */
1205    public static boolean isEncrypted() {
1206        return RoSystemProperties.CRYPTO_ENCRYPTED;
1207    }
1208
1209    /** {@hide}
1210     * Is this device file encrypted?
1211     * @return true for file encrypted. (Implies isEncrypted() == true)
1212     *         false not encrypted or block encrypted
1213     */
1214    public static boolean isFileEncryptedNativeOnly() {
1215        if (!isEncrypted()) {
1216            return false;
1217        }
1218        return RoSystemProperties.CRYPTO_FILE_ENCRYPTED;
1219    }
1220
1221    /** {@hide}
1222     * Is this device block encrypted?
1223     * @return true for block encrypted. (Implies isEncrypted() == true)
1224     *         false not encrypted or file encrypted
1225     */
1226    public static boolean isBlockEncrypted() {
1227        if (!isEncrypted()) {
1228            return false;
1229        }
1230        return RoSystemProperties.CRYPTO_BLOCK_ENCRYPTED;
1231    }
1232
1233    /** {@hide}
1234     * Is this device block encrypted with credentials?
1235     * @return true for crediential block encrypted.
1236     *         (Implies isBlockEncrypted() == true)
1237     *         false not encrypted, file encrypted or default block encrypted
1238     */
1239    public static boolean isNonDefaultBlockEncrypted() {
1240        if (!isBlockEncrypted()) {
1241            return false;
1242        }
1243
1244        try {
1245            IStorageManager storageManager = IStorageManager.Stub.asInterface(
1246                    ServiceManager.getService("mount"));
1247            return storageManager.getPasswordType() != CRYPT_TYPE_DEFAULT;
1248        } catch (RemoteException e) {
1249            Log.e(TAG, "Error getting encryption type");
1250            return false;
1251        }
1252    }
1253
1254    /** {@hide}
1255     * Is this device in the process of being block encrypted?
1256     * @return true for encrypting.
1257     *         false otherwise
1258     * Whether device isEncrypted at this point is undefined
1259     * Note that only system services and CryptKeeper will ever see this return
1260     * true - no app will ever be launched in this state.
1261     * Also note that this state will not change without a teardown of the
1262     * framework, so no service needs to check for changes during their lifespan
1263     */
1264    public static boolean isBlockEncrypting() {
1265        final String state = SystemProperties.get("vold.encrypt_progress", "");
1266        return !"".equalsIgnoreCase(state);
1267    }
1268
1269    /** {@hide}
1270     * Is this device non default block encrypted and in the process of
1271     * prompting for credentials?
1272     * @return true for prompting for credentials.
1273     *         (Implies isNonDefaultBlockEncrypted() == true)
1274     *         false otherwise
1275     * Note that only system services and CryptKeeper will ever see this return
1276     * true - no app will ever be launched in this state.
1277     * Also note that this state will not change without a teardown of the
1278     * framework, so no service needs to check for changes during their lifespan
1279     */
1280    public static boolean inCryptKeeperBounce() {
1281        final String status = SystemProperties.get("vold.decrypt");
1282        return "trigger_restart_min_framework".equals(status);
1283    }
1284
1285    /** {@hide} */
1286    public static boolean isFileEncryptedEmulatedOnly() {
1287        return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false);
1288    }
1289
1290    /** {@hide}
1291     * Is this device running in a file encrypted mode, either native or emulated?
1292     * @return true for file encrypted, false otherwise
1293     */
1294    public static boolean isFileEncryptedNativeOrEmulated() {
1295        return isFileEncryptedNativeOnly()
1296               || isFileEncryptedEmulatedOnly();
1297    }
1298
1299    /** {@hide} */
1300    public static File maybeTranslateEmulatedPathToInternal(File path) {
1301        final IStorageManager storageManager = IStorageManager.Stub.asInterface(
1302                ServiceManager.getService("mount"));
1303        try {
1304            final VolumeInfo[] vols = storageManager.getVolumes(0);
1305            for (VolumeInfo vol : vols) {
1306                if ((vol.getType() == VolumeInfo.TYPE_EMULATED
1307                        || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
1308                    final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
1309                            vol.getInternalPath(), path);
1310                    if (internalPath != null && internalPath.exists()) {
1311                        return internalPath;
1312                    }
1313                }
1314            }
1315        } catch (RemoteException e) {
1316            throw e.rethrowFromSystemServer();
1317        }
1318        return path;
1319    }
1320
1321    /** {@hide} */
1322    public ParcelFileDescriptor mountAppFuse(String name) {
1323        try {
1324            return mStorageManager.mountAppFuse(name);
1325        } catch (RemoteException e) {
1326            throw e.rethrowFromSystemServer();
1327        }
1328    }
1329
1330    /** {@hide} */
1331    @VisibleForTesting
1332    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
1333            int mode, ProxyFileDescriptorCallback callback, ThreadFactory factory)
1334                    throws IOException {
1335        // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before
1336        // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount
1337        // the bridge by calling mountProxyFileDescriptorBridge.
1338        int retry = 3;
1339        while (retry-- > 0) {
1340            try {
1341                synchronized (mFuseAppLoopLock) {
1342                    if (mFuseAppLoop == null) {
1343                        final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge();
1344                        if (mount == null) {
1345                            Log.e(TAG, "Failed to open proxy file bridge.");
1346                            throw new IOException("Failed to open proxy file bridge.");
1347                        }
1348                        mFuseAppLoop = FuseAppLoop.open(mount.mountPointId, mount.fd, factory);
1349                    }
1350
1351                    try {
1352                        final int fileId = mFuseAppLoop.registerCallback(callback);
1353                        final ParcelFileDescriptor pfd =
1354                                mStorageManager.openProxyFileDescriptor(
1355                                        mFuseAppLoop.getMountPointId(), fileId, mode);
1356                        if (pfd != null) {
1357                            return pfd;
1358                        }
1359                        // Probably the bridge is being unmounted but mFuseAppLoop has not been
1360                        // noticed it yet.
1361                        mFuseAppLoop.unregisterCallback(fileId);
1362                    } catch (FuseAppLoop.UnmountedException error) {
1363                        Log.d(TAG, "mFuseAppLoop has been already unmounted.");
1364                        mFuseAppLoop = null;
1365                        continue;
1366                    }
1367                }
1368                try {
1369                    Thread.sleep(100);
1370                } catch (InterruptedException e) {
1371                    break;
1372                }
1373            } catch (RemoteException e) {
1374                e.rethrowFromSystemServer();
1375            }
1376        }
1377
1378        throw new IOException("Failed to mount bridge.");
1379    }
1380
1381    /** {@hide} */
1382    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
1383            int mode, ProxyFileDescriptorCallback callback)
1384                    throws IOException {
1385        return openProxyFileDescriptor(mode, callback, null);
1386    }
1387
1388    /** {@hide} */
1389    @VisibleForTesting
1390    public int getProxyFileDescriptorMountPointId() {
1391        synchronized (mFuseAppLoopLock) {
1392            return mFuseAppLoop != null ? mFuseAppLoop.getMountPointId() : -1;
1393        }
1394    }
1395
1396    private final Object mFuseAppLoopLock = new Object();
1397
1398    @GuardedBy("mFuseAppLoopLock")
1399    private @Nullable FuseAppLoop mFuseAppLoop = null;
1400
1401    /// Consts to match the password types in cryptfs.h
1402    /** @hide */
1403    public static final int CRYPT_TYPE_PASSWORD = 0;
1404    /** @hide */
1405    public static final int CRYPT_TYPE_DEFAULT = 1;
1406    /** @hide */
1407    public static final int CRYPT_TYPE_PATTERN = 2;
1408    /** @hide */
1409    public static final int CRYPT_TYPE_PIN = 3;
1410
1411    // Constants for the data available via StorageManagerService.getField.
1412    /** @hide */
1413    public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
1414    /** @hide */
1415    public static final String OWNER_INFO_KEY = "OwnerInfo";
1416    /** @hide */
1417    public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
1418    /** @hide */
1419    public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible";
1420}
1421