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