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