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