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