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