StorageManager.java revision 47f7108c1270a9e81d9560b6b0570c659bb93a71
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.rethrowAsRuntimeException();
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.rethrowAsRuntimeException();
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            Log.e(TAG, "Failed to mount OBB", e);
438        }
439
440        return false;
441    }
442
443    /**
444     * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
445     * <code>force</code> flag is true, it will kill any application needed to
446     * unmount the given OBB (even the calling application).
447     * <p>
448     * The {@link OnObbStateChangeListener} registered with this call will
449     * receive the success or failure of this operation.
450     * <p>
451     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
452     * file matches a package ID that is owned by the calling program's UID.
453     * That is, shared UID applications can obtain access to any other
454     * application's OBB that shares its UID.
455     * <p>
456     *
457     * @param rawPath path to the OBB file
458     * @param force whether to kill any programs using this in order to unmount
459     *            it
460     * @param listener will receive the success or failure of the operation
461     * @return whether the unmount call was successfully queued or not
462     */
463    public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
464        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
465        Preconditions.checkNotNull(listener, "listener cannot be null");
466
467        try {
468            final int nonce = mObbActionListener.addListener(listener);
469            mMountService.unmountObb(rawPath, force, mObbActionListener, nonce);
470            return true;
471        } catch (RemoteException e) {
472            Log.e(TAG, "Failed to mount OBB", e);
473        }
474
475        return false;
476    }
477
478    /**
479     * Check whether an Opaque Binary Blob (OBB) is mounted or not.
480     *
481     * @param rawPath path to OBB image
482     * @return true if OBB is mounted; false if not mounted or on error
483     */
484    public boolean isObbMounted(String rawPath) {
485        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
486
487        try {
488            return mMountService.isObbMounted(rawPath);
489        } catch (RemoteException e) {
490            Log.e(TAG, "Failed to check if OBB is mounted", e);
491        }
492
493        return false;
494    }
495
496    /**
497     * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
498     * give you the path to where you can obtain access to the internals of the
499     * OBB.
500     *
501     * @param rawPath path to OBB image
502     * @return absolute path to mounted OBB image data or <code>null</code> if
503     *         not mounted or exception encountered trying to read status
504     */
505    public String getMountedObbPath(String rawPath) {
506        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
507
508        try {
509            return mMountService.getMountedObbPath(rawPath);
510        } catch (RemoteException e) {
511            Log.e(TAG, "Failed to find mounted path for OBB", e);
512        }
513
514        return null;
515    }
516
517    /** {@hide} */
518    public @NonNull List<DiskInfo> getDisks() {
519        try {
520            return Arrays.asList(mMountService.getDisks());
521        } catch (RemoteException e) {
522            throw e.rethrowAsRuntimeException();
523        }
524    }
525
526    /** {@hide} */
527    public @Nullable DiskInfo findDiskById(String id) {
528        Preconditions.checkNotNull(id);
529        // TODO; go directly to service to make this faster
530        for (DiskInfo disk : getDisks()) {
531            if (Objects.equals(disk.id, id)) {
532                return disk;
533            }
534        }
535        return null;
536    }
537
538    /** {@hide} */
539    public @Nullable VolumeInfo findVolumeById(String id) {
540        Preconditions.checkNotNull(id);
541        // TODO; go directly to service to make this faster
542        for (VolumeInfo vol : getVolumes()) {
543            if (Objects.equals(vol.id, id)) {
544                return vol;
545            }
546        }
547        return null;
548    }
549
550    /** {@hide} */
551    public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
552        Preconditions.checkNotNull(fsUuid);
553        // TODO; go directly to service to make this faster
554        for (VolumeInfo vol : getVolumes()) {
555            if (Objects.equals(vol.fsUuid, fsUuid)) {
556                return vol;
557            }
558        }
559        return null;
560    }
561
562    /** {@hide} */
563    public @Nullable VolumeRecord findRecordByUuid(String fsUuid) {
564        Preconditions.checkNotNull(fsUuid);
565        // TODO; go directly to service to make this faster
566        for (VolumeRecord rec : getVolumeRecords()) {
567            if (Objects.equals(rec.fsUuid, fsUuid)) {
568                return rec;
569            }
570        }
571        return null;
572    }
573
574    /** {@hide} */
575    public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
576        if (emulatedVol != null) {
577            return findVolumeById(emulatedVol.getId().replace("emulated", "private"));
578        } else {
579            return null;
580        }
581    }
582
583    /** {@hide} */
584    public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
585        if (privateVol != null) {
586            return findVolumeById(privateVol.getId().replace("private", "emulated"));
587        } else {
588            return null;
589        }
590    }
591
592    /** {@hide} */
593    public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
594        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
595            return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
596        } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
597            return getPrimaryPhysicalVolume();
598        } else {
599            return findVolumeByUuid(volumeUuid);
600        }
601    }
602
603    /** {@hide} */
604    public @NonNull List<VolumeInfo> getVolumes() {
605        try {
606            return Arrays.asList(mMountService.getVolumes(0));
607        } catch (RemoteException e) {
608            throw e.rethrowAsRuntimeException();
609        }
610    }
611
612    /** {@hide} */
613    public @NonNull List<VolumeInfo> getWritablePrivateVolumes() {
614        try {
615            final ArrayList<VolumeInfo> res = new ArrayList<>();
616            for (VolumeInfo vol : mMountService.getVolumes(0)) {
617                if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
618                    res.add(vol);
619                }
620            }
621            return res;
622        } catch (RemoteException e) {
623            throw e.rethrowAsRuntimeException();
624        }
625    }
626
627    /** {@hide} */
628    public @NonNull List<VolumeRecord> getVolumeRecords() {
629        try {
630            return Arrays.asList(mMountService.getVolumeRecords(0));
631        } catch (RemoteException e) {
632            throw e.rethrowAsRuntimeException();
633        }
634    }
635
636    /** {@hide} */
637    public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
638        if (vol == null) return null;
639
640        // Nickname always takes precedence when defined
641        if (!TextUtils.isEmpty(vol.fsUuid)) {
642            final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
643            if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
644                return rec.nickname;
645            }
646        }
647
648        if (!TextUtils.isEmpty(vol.getDescription())) {
649            return vol.getDescription();
650        }
651
652        if (vol.disk != null) {
653            return vol.disk.getDescription();
654        }
655
656        return null;
657    }
658
659    /** {@hide} */
660    public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
661        final List<VolumeInfo> vols = getVolumes();
662        for (VolumeInfo vol : vols) {
663            if (vol.isPrimaryPhysical()) {
664                return vol;
665            }
666        }
667        return null;
668    }
669
670    /** {@hide} */
671    public void mount(String volId) {
672        try {
673            mMountService.mount(volId);
674        } catch (RemoteException e) {
675            throw e.rethrowAsRuntimeException();
676        }
677    }
678
679    /** {@hide} */
680    public void unmount(String volId) {
681        try {
682            mMountService.unmount(volId);
683        } catch (RemoteException e) {
684            throw e.rethrowAsRuntimeException();
685        }
686    }
687
688    /** {@hide} */
689    public void format(String volId) {
690        try {
691            mMountService.format(volId);
692        } catch (RemoteException e) {
693            throw e.rethrowAsRuntimeException();
694        }
695    }
696
697    /** {@hide} */
698    public long benchmark(String volId) {
699        try {
700            return mMountService.benchmark(volId);
701        } catch (RemoteException e) {
702            throw e.rethrowAsRuntimeException();
703        }
704    }
705
706    /** {@hide} */
707    public void partitionPublic(String diskId) {
708        try {
709            mMountService.partitionPublic(diskId);
710        } catch (RemoteException e) {
711            throw e.rethrowAsRuntimeException();
712        }
713    }
714
715    /** {@hide} */
716    public void partitionPrivate(String diskId) {
717        try {
718            mMountService.partitionPrivate(diskId);
719        } catch (RemoteException e) {
720            throw e.rethrowAsRuntimeException();
721        }
722    }
723
724    /** {@hide} */
725    public void partitionMixed(String diskId, int ratio) {
726        try {
727            mMountService.partitionMixed(diskId, ratio);
728        } catch (RemoteException e) {
729            throw e.rethrowAsRuntimeException();
730        }
731    }
732
733    /** {@hide} */
734    public void wipeAdoptableDisks() {
735        // We only wipe devices in "adoptable" locations, which are in a
736        // long-term stable slot/location on the device, where apps have a
737        // reasonable chance of storing sensitive data. (Apps need to go through
738        // SAF to write to transient volumes.)
739        final List<DiskInfo> disks = getDisks();
740        for (DiskInfo disk : disks) {
741            final String diskId = disk.getId();
742            if (disk.isAdoptable()) {
743                Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
744                try {
745                    // TODO: switch to explicit wipe command when we have it,
746                    // for now rely on the fact that vfat format does a wipe
747                    mMountService.partitionPublic(diskId);
748                } catch (Exception e) {
749                    Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
750                }
751            } else {
752                Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
753            }
754        }
755    }
756
757    /** {@hide} */
758    public void setVolumeNickname(String fsUuid, String nickname) {
759        try {
760            mMountService.setVolumeNickname(fsUuid, nickname);
761        } catch (RemoteException e) {
762            throw e.rethrowAsRuntimeException();
763        }
764    }
765
766    /** {@hide} */
767    public void setVolumeInited(String fsUuid, boolean inited) {
768        try {
769            mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0,
770                    VolumeRecord.USER_FLAG_INITED);
771        } catch (RemoteException e) {
772            throw e.rethrowAsRuntimeException();
773        }
774    }
775
776    /** {@hide} */
777    public void setVolumeSnoozed(String fsUuid, boolean snoozed) {
778        try {
779            mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0,
780                    VolumeRecord.USER_FLAG_SNOOZED);
781        } catch (RemoteException e) {
782            throw e.rethrowAsRuntimeException();
783        }
784    }
785
786    /** {@hide} */
787    public void forgetVolume(String fsUuid) {
788        try {
789            mMountService.forgetVolume(fsUuid);
790        } catch (RemoteException e) {
791            throw e.rethrowAsRuntimeException();
792        }
793    }
794
795    /**
796     * This is not the API you're looking for.
797     *
798     * @see PackageManager#getPrimaryStorageCurrentVolume()
799     * @hide
800     */
801    public String getPrimaryStorageUuid() {
802        try {
803            return mMountService.getPrimaryStorageUuid();
804        } catch (RemoteException e) {
805            throw e.rethrowAsRuntimeException();
806        }
807    }
808
809    /**
810     * This is not the API you're looking for.
811     *
812     * @see PackageManager#movePrimaryStorage(VolumeInfo)
813     * @hide
814     */
815    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
816        try {
817            mMountService.setPrimaryStorageUuid(volumeUuid, callback);
818        } catch (RemoteException e) {
819            throw e.rethrowAsRuntimeException();
820        }
821    }
822
823    /** {@hide} */
824    public @Nullable StorageVolume getStorageVolume(File file) {
825        return getStorageVolume(getVolumeList(), file);
826    }
827
828    /** {@hide} */
829    public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
830        return getStorageVolume(getVolumeList(userId, 0), file);
831    }
832
833    /** {@hide} */
834    private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
835        try {
836            file = file.getCanonicalFile();
837        } catch (IOException ignored) {
838            return null;
839        }
840        for (StorageVolume volume : volumes) {
841            File volumeFile = volume.getPathFile();
842            try {
843                volumeFile = volumeFile.getCanonicalFile();
844            } catch (IOException ignored) {
845                continue;
846            }
847            if (FileUtils.contains(volumeFile, file)) {
848                return volume;
849            }
850        }
851        return null;
852    }
853
854    /**
855     * Gets the state of a volume via its mountpoint.
856     * @hide
857     */
858    @Deprecated
859    public @NonNull String getVolumeState(String mountPoint) {
860        final StorageVolume vol = getStorageVolume(new File(mountPoint));
861        if (vol != null) {
862            return vol.getState();
863        } else {
864            return Environment.MEDIA_UNKNOWN;
865        }
866    }
867
868    /** {@hide} */
869    public @NonNull StorageVolume[] getVolumeList() {
870        return getVolumeList(mContext.getUserId(), 0);
871    }
872
873    /** {@hide} */
874    public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
875        final IMountService mountService = IMountService.Stub.asInterface(
876                ServiceManager.getService("mount"));
877        try {
878            String packageName = ActivityThread.currentOpPackageName();
879            if (packageName == null) {
880                // Package name can be null if the activity thread is running but the app
881                // hasn't bound yet. In this case we fall back to the first package in the
882                // current UID. This works for runtime permissions as permission state is
883                // per UID and permission realted app ops are updated for all UID packages.
884                String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
885                        android.os.Process.myUid());
886                if (packageNames == null || packageNames.length <= 0) {
887                    return new StorageVolume[0];
888                }
889                packageName = packageNames[0];
890            }
891            final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
892                    PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
893            if (uid <= 0) {
894                return new StorageVolume[0];
895            }
896            return mountService.getVolumeList(uid, packageName, flags);
897        } catch (RemoteException e) {
898            throw e.rethrowAsRuntimeException();
899        }
900    }
901
902    /**
903     * Returns list of paths for all mountable volumes.
904     * @hide
905     */
906    @Deprecated
907    public @NonNull String[] getVolumePaths() {
908        StorageVolume[] volumes = getVolumeList();
909        int count = volumes.length;
910        String[] paths = new String[count];
911        for (int i = 0; i < count; i++) {
912            paths[i] = volumes[i].getPath();
913        }
914        return paths;
915    }
916
917    /** {@hide} */
918    public @NonNull StorageVolume getPrimaryVolume() {
919        return getPrimaryVolume(getVolumeList());
920    }
921
922    /** {@hide} */
923    public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
924        for (StorageVolume volume : volumes) {
925            if (volume.isPrimary()) {
926                return volume;
927            }
928        }
929        throw new IllegalStateException("Missing primary storage");
930    }
931
932    /** {@hide} */
933    private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
934    private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
935    private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
936
937    /**
938     * Return the number of available bytes until the given path is considered
939     * running low on storage.
940     *
941     * @hide
942     */
943    public long getStorageBytesUntilLow(File path) {
944        return path.getUsableSpace() - getStorageFullBytes(path);
945    }
946
947    /**
948     * Return the number of available bytes at which the given path is
949     * considered running low on storage.
950     *
951     * @hide
952     */
953    public long getStorageLowBytes(File path) {
954        final long lowPercent = Settings.Global.getInt(mResolver,
955                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
956        final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
957
958        final long maxLowBytes = Settings.Global.getLong(mResolver,
959                Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
960
961        return Math.min(lowBytes, maxLowBytes);
962    }
963
964    /**
965     * Return the number of available bytes at which the given path is
966     * considered full.
967     *
968     * @hide
969     */
970    public long getStorageFullBytes(File path) {
971        return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
972                DEFAULT_FULL_THRESHOLD_BYTES);
973    }
974
975    /** {@hide} */
976    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
977        try {
978            mMountService.createUserKey(userId, serialNumber, ephemeral);
979        } catch (RemoteException e) {
980            throw e.rethrowAsRuntimeException();
981        }
982    }
983
984    /** {@hide} */
985    public void destroyUserKey(int userId) {
986        try {
987            mMountService.destroyUserKey(userId);
988        } catch (RemoteException e) {
989            throw e.rethrowAsRuntimeException();
990        }
991    }
992
993    /** {@hide} */
994    public void unlockUserKey(int userId, int serialNumber, byte[] token) {
995        try {
996            mMountService.unlockUserKey(userId, serialNumber, token);
997        } catch (RemoteException e) {
998            throw e.rethrowAsRuntimeException();
999        }
1000    }
1001
1002    /** {@hide} */
1003    public void lockUserKey(int userId) {
1004        try {
1005            mMountService.lockUserKey(userId);
1006        } catch (RemoteException e) {
1007            throw e.rethrowAsRuntimeException();
1008        }
1009    }
1010
1011    /** {@hide} */
1012    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
1013        try {
1014            mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
1015        } catch (RemoteException e) {
1016            throw e.rethrowAsRuntimeException();
1017        }
1018    }
1019
1020    /** {@hide} */
1021    public boolean isUserKeyUnlocked(int userId) {
1022        try {
1023            return mMountService.isUserKeyUnlocked(userId);
1024        } catch (RemoteException e) {
1025            throw e.rethrowAsRuntimeException();
1026        }
1027    }
1028
1029    /** {@hide} */
1030    public static boolean isFileBasedEncryptionEnabled() {
1031        return isNativeFileBasedEncryptionEnabled() || isEmulatedFileBasedEncryptionEnabled();
1032    }
1033
1034    /** {@hide} */
1035    public static boolean isNativeFileBasedEncryptionEnabled() {
1036        return "file".equals(SystemProperties.get("ro.crypto.type", "none"));
1037    }
1038
1039    /** {@hide} */
1040    public static boolean isEmulatedFileBasedEncryptionEnabled() {
1041        return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false);
1042    }
1043
1044    /** {@hide} */
1045    public static File maybeTranslateEmulatedPathToInternal(File path) {
1046        final IMountService mountService = IMountService.Stub.asInterface(
1047                ServiceManager.getService("mount"));
1048        try {
1049            final VolumeInfo[] vols = mountService.getVolumes(0);
1050            for (VolumeInfo vol : vols) {
1051                if ((vol.getType() == VolumeInfo.TYPE_EMULATED
1052                        || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
1053                    final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
1054                            vol.getInternalPath(), path);
1055                    if (internalPath != null && internalPath.exists()) {
1056                        return internalPath;
1057                    }
1058                }
1059            }
1060        } catch (RemoteException ignored) {
1061        }
1062        return path;
1063    }
1064
1065    /** {@hide} */
1066    public ParcelFileDescriptor mountAppFuse(String name) {
1067        try {
1068            return mMountService.mountAppFuse(name);
1069        } catch (RemoteException e) {
1070            throw e.rethrowAsRuntimeException();
1071        }
1072    }
1073
1074    /// Consts to match the password types in cryptfs.h
1075    /** @hide */
1076    public static final int CRYPT_TYPE_PASSWORD = 0;
1077    /** @hide */
1078    public static final int CRYPT_TYPE_DEFAULT = 1;
1079    /** @hide */
1080    public static final int CRYPT_TYPE_PATTERN = 2;
1081    /** @hide */
1082    public static final int CRYPT_TYPE_PIN = 3;
1083
1084    // Constants for the data available via MountService.getField.
1085    /** @hide */
1086    public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
1087    /** @hide */
1088    public static final String OWNER_INFO_KEY = "OwnerInfo";
1089    /** @hide */
1090    public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
1091    /** @hide */
1092    public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible";
1093}
1094