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