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