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