StorageManager.java revision 19d9c2d03c478c755eddbe7ac55d0dc778f332fd
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.Pair;
44import android.util.Slog;
45import android.util.SparseArray;
46
47import com.android.internal.os.SomeArgs;
48import com.android.internal.util.Preconditions;
49
50import java.io.BufferedReader;
51import java.io.File;
52import java.io.FileInputStream;
53import java.io.FileNotFoundException;
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 tgtLooper 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) {
355        mContext = context;
356        mResolver = context.getContentResolver();
357        mLooper = looper;
358        mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
359        if (mMountService == null) {
360            throw new IllegalStateException("Failed to find running mount service");
361        }
362    }
363
364    /**
365     * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
366     *
367     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
368     *
369     * @hide
370     */
371    public void registerListener(StorageEventListener listener) {
372        synchronized (mDelegates) {
373            final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener,
374                    mLooper);
375            try {
376                mMountService.registerListener(delegate);
377            } catch (RemoteException e) {
378                throw e.rethrowFromSystemServer();
379            }
380            mDelegates.add(delegate);
381        }
382    }
383
384    /**
385     * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
386     *
387     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
388     *
389     * @hide
390     */
391    public void unregisterListener(StorageEventListener listener) {
392        synchronized (mDelegates) {
393            for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
394                final StorageEventListenerDelegate delegate = i.next();
395                if (delegate.mCallback == listener) {
396                    try {
397                        mMountService.unregisterListener(delegate);
398                    } catch (RemoteException e) {
399                        throw e.rethrowFromSystemServer();
400                    }
401                    i.remove();
402                }
403            }
404        }
405    }
406
407    /**
408     * Enables USB Mass Storage (UMS) on the device.
409     *
410     * @hide
411     */
412    @Deprecated
413    public void enableUsbMassStorage() {
414    }
415
416    /**
417     * Disables USB Mass Storage (UMS) on the device.
418     *
419     * @hide
420     */
421    @Deprecated
422    public void disableUsbMassStorage() {
423    }
424
425    /**
426     * Query if a USB Mass Storage (UMS) host is connected.
427     * @return true if UMS host is connected.
428     *
429     * @hide
430     */
431    @Deprecated
432    public boolean isUsbMassStorageConnected() {
433        return false;
434    }
435
436    /**
437     * Query if a USB Mass Storage (UMS) is enabled on the device.
438     * @return true if UMS host is enabled.
439     *
440     * @hide
441     */
442    @Deprecated
443    public boolean isUsbMassStorageEnabled() {
444        return false;
445    }
446
447    /**
448     * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
449     * specified, it is supplied to the mounting process to be used in any
450     * encryption used in the OBB.
451     * <p>
452     * The OBB will remain mounted for as long as the StorageManager reference
453     * is held by the application. As soon as this reference is lost, the OBBs
454     * in use will be unmounted. The {@link OnObbStateChangeListener} registered
455     * with this call will receive the success or failure of this operation.
456     * <p>
457     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
458     * file matches a package ID that is owned by the calling program's UID.
459     * That is, shared UID applications can attempt to mount any other
460     * application's OBB that shares its UID.
461     *
462     * @param rawPath the path to the OBB file
463     * @param key secret used to encrypt the OBB; may be <code>null</code> if no
464     *            encryption was used on the OBB.
465     * @param listener will receive the success or failure of the operation
466     * @return whether the mount call was successfully queued or not
467     */
468    public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
469        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
470        Preconditions.checkNotNull(listener, "listener cannot be null");
471
472        try {
473            final String canonicalPath = new File(rawPath).getCanonicalPath();
474            final int nonce = mObbActionListener.addListener(listener);
475            mMountService.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce);
476            return true;
477        } catch (IOException e) {
478            throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e);
479        } catch (RemoteException e) {
480            throw e.rethrowFromSystemServer();
481        }
482    }
483
484    /**
485     * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
486     * <code>force</code> flag is true, it will kill any application needed to
487     * unmount the given OBB (even the calling application).
488     * <p>
489     * The {@link OnObbStateChangeListener} registered with this call will
490     * receive the success or failure of this operation.
491     * <p>
492     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
493     * file matches a package ID that is owned by the calling program's UID.
494     * That is, shared UID applications can obtain access to any other
495     * application's OBB that shares its UID.
496     * <p>
497     *
498     * @param rawPath path to the OBB file
499     * @param force whether to kill any programs using this in order to unmount
500     *            it
501     * @param listener will receive the success or failure of the operation
502     * @return whether the unmount call was successfully queued or not
503     */
504    public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
505        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
506        Preconditions.checkNotNull(listener, "listener cannot be null");
507
508        try {
509            final int nonce = mObbActionListener.addListener(listener);
510            mMountService.unmountObb(rawPath, force, mObbActionListener, nonce);
511            return true;
512        } catch (RemoteException e) {
513            throw e.rethrowFromSystemServer();
514        }
515    }
516
517    /**
518     * Check whether an Opaque Binary Blob (OBB) is mounted or not.
519     *
520     * @param rawPath path to OBB image
521     * @return true if OBB is mounted; false if not mounted or on error
522     */
523    public boolean isObbMounted(String rawPath) {
524        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
525
526        try {
527            return mMountService.isObbMounted(rawPath);
528        } catch (RemoteException e) {
529            throw e.rethrowFromSystemServer();
530        }
531    }
532
533    /**
534     * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
535     * give you the path to where you can obtain access to the internals of the
536     * OBB.
537     *
538     * @param rawPath path to OBB image
539     * @return absolute path to mounted OBB image data or <code>null</code> if
540     *         not mounted or exception encountered trying to read status
541     */
542    public String getMountedObbPath(String rawPath) {
543        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
544
545        try {
546            return mMountService.getMountedObbPath(rawPath);
547        } catch (RemoteException e) {
548            throw e.rethrowFromSystemServer();
549        }
550    }
551
552    /** {@hide} */
553    public @NonNull List<DiskInfo> getDisks() {
554        try {
555            return Arrays.asList(mMountService.getDisks());
556        } catch (RemoteException e) {
557            throw e.rethrowFromSystemServer();
558        }
559    }
560
561    /** {@hide} */
562    public @Nullable DiskInfo findDiskById(String id) {
563        Preconditions.checkNotNull(id);
564        // TODO; go directly to service to make this faster
565        for (DiskInfo disk : getDisks()) {
566            if (Objects.equals(disk.id, id)) {
567                return disk;
568            }
569        }
570        return null;
571    }
572
573    /** {@hide} */
574    public @Nullable VolumeInfo findVolumeById(String id) {
575        Preconditions.checkNotNull(id);
576        // TODO; go directly to service to make this faster
577        for (VolumeInfo vol : getVolumes()) {
578            if (Objects.equals(vol.id, id)) {
579                return vol;
580            }
581        }
582        return null;
583    }
584
585    /** {@hide} */
586    public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
587        Preconditions.checkNotNull(fsUuid);
588        // TODO; go directly to service to make this faster
589        for (VolumeInfo vol : getVolumes()) {
590            if (Objects.equals(vol.fsUuid, fsUuid)) {
591                return vol;
592            }
593        }
594        return null;
595    }
596
597    /** {@hide} */
598    public @Nullable VolumeRecord findRecordByUuid(String fsUuid) {
599        Preconditions.checkNotNull(fsUuid);
600        // TODO; go directly to service to make this faster
601        for (VolumeRecord rec : getVolumeRecords()) {
602            if (Objects.equals(rec.fsUuid, fsUuid)) {
603                return rec;
604            }
605        }
606        return null;
607    }
608
609    /** {@hide} */
610    public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
611        if (emulatedVol != null) {
612            return findVolumeById(emulatedVol.getId().replace("emulated", "private"));
613        } else {
614            return null;
615        }
616    }
617
618    /** {@hide} */
619    public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
620        if (privateVol != null) {
621            return findVolumeById(privateVol.getId().replace("private", "emulated"));
622        } else {
623            return null;
624        }
625    }
626
627    /** {@hide} */
628    public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
629        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
630            return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
631        } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
632            return getPrimaryPhysicalVolume();
633        } else {
634            return findVolumeByUuid(volumeUuid);
635        }
636    }
637
638    /** {@hide} */
639    public @NonNull List<VolumeInfo> getVolumes() {
640        try {
641            return Arrays.asList(mMountService.getVolumes(0));
642        } catch (RemoteException e) {
643            throw e.rethrowFromSystemServer();
644        }
645    }
646
647    /** {@hide} */
648    public @NonNull List<VolumeInfo> getWritablePrivateVolumes() {
649        try {
650            final ArrayList<VolumeInfo> res = new ArrayList<>();
651            for (VolumeInfo vol : mMountService.getVolumes(0)) {
652                if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
653                    res.add(vol);
654                }
655            }
656            return res;
657        } catch (RemoteException e) {
658            throw e.rethrowFromSystemServer();
659        }
660    }
661
662    /** {@hide} */
663    public @NonNull List<VolumeRecord> getVolumeRecords() {
664        try {
665            return Arrays.asList(mMountService.getVolumeRecords(0));
666        } catch (RemoteException e) {
667            throw e.rethrowFromSystemServer();
668        }
669    }
670
671    /** {@hide} */
672    public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
673        if (vol == null) return null;
674
675        // Nickname always takes precedence when defined
676        if (!TextUtils.isEmpty(vol.fsUuid)) {
677            final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
678            if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
679                return rec.nickname;
680            }
681        }
682
683        if (!TextUtils.isEmpty(vol.getDescription())) {
684            return vol.getDescription();
685        }
686
687        if (vol.disk != null) {
688            return vol.disk.getDescription();
689        }
690
691        return null;
692    }
693
694    /** {@hide} */
695    public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
696        final List<VolumeInfo> vols = getVolumes();
697        for (VolumeInfo vol : vols) {
698            if (vol.isPrimaryPhysical()) {
699                return vol;
700            }
701        }
702        return null;
703    }
704
705    /** {@hide} */
706    public void mount(String volId) {
707        try {
708            mMountService.mount(volId);
709        } catch (RemoteException e) {
710            throw e.rethrowFromSystemServer();
711        }
712    }
713
714    /** {@hide} */
715    public void unmount(String volId) {
716        try {
717            mMountService.unmount(volId);
718        } catch (RemoteException e) {
719            throw e.rethrowFromSystemServer();
720        }
721    }
722
723    /** {@hide} */
724    public void format(String volId) {
725        try {
726            mMountService.format(volId);
727        } catch (RemoteException e) {
728            throw e.rethrowFromSystemServer();
729        }
730    }
731
732    /** {@hide} */
733    public long benchmark(String volId) {
734        try {
735            return mMountService.benchmark(volId);
736        } catch (RemoteException e) {
737            throw e.rethrowFromSystemServer();
738        }
739    }
740
741    /** {@hide} */
742    public void partitionPublic(String diskId) {
743        try {
744            mMountService.partitionPublic(diskId);
745        } catch (RemoteException e) {
746            throw e.rethrowFromSystemServer();
747        }
748    }
749
750    /** {@hide} */
751    public void partitionPrivate(String diskId) {
752        try {
753            mMountService.partitionPrivate(diskId);
754        } catch (RemoteException e) {
755            throw e.rethrowFromSystemServer();
756        }
757    }
758
759    /** {@hide} */
760    public void partitionMixed(String diskId, int ratio) {
761        try {
762            mMountService.partitionMixed(diskId, ratio);
763        } catch (RemoteException e) {
764            throw e.rethrowFromSystemServer();
765        }
766    }
767
768    /** {@hide} */
769    public void wipeAdoptableDisks() {
770        // We only wipe devices in "adoptable" locations, which are in a
771        // long-term stable slot/location on the device, where apps have a
772        // reasonable chance of storing sensitive data. (Apps need to go through
773        // SAF to write to transient volumes.)
774        final List<DiskInfo> disks = getDisks();
775        for (DiskInfo disk : disks) {
776            final String diskId = disk.getId();
777            if (disk.isAdoptable()) {
778                Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
779                try {
780                    // TODO: switch to explicit wipe command when we have it,
781                    // for now rely on the fact that vfat format does a wipe
782                    mMountService.partitionPublic(diskId);
783                } catch (Exception e) {
784                    Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
785                }
786            } else {
787                Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
788            }
789        }
790    }
791
792    /** {@hide} */
793    public void setVolumeNickname(String fsUuid, String nickname) {
794        try {
795            mMountService.setVolumeNickname(fsUuid, nickname);
796        } catch (RemoteException e) {
797            throw e.rethrowFromSystemServer();
798        }
799    }
800
801    /** {@hide} */
802    public void setVolumeInited(String fsUuid, boolean inited) {
803        try {
804            mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0,
805                    VolumeRecord.USER_FLAG_INITED);
806        } catch (RemoteException e) {
807            throw e.rethrowFromSystemServer();
808        }
809    }
810
811    /** {@hide} */
812    public void setVolumeSnoozed(String fsUuid, boolean snoozed) {
813        try {
814            mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0,
815                    VolumeRecord.USER_FLAG_SNOOZED);
816        } catch (RemoteException e) {
817            throw e.rethrowFromSystemServer();
818        }
819    }
820
821    /** {@hide} */
822    public void forgetVolume(String fsUuid) {
823        try {
824            mMountService.forgetVolume(fsUuid);
825        } catch (RemoteException e) {
826            throw e.rethrowFromSystemServer();
827        }
828    }
829
830    /**
831     * This is not the API you're looking for.
832     *
833     * @see PackageManager#getPrimaryStorageCurrentVolume()
834     * @hide
835     */
836    public String getPrimaryStorageUuid() {
837        try {
838            return mMountService.getPrimaryStorageUuid();
839        } catch (RemoteException e) {
840            throw e.rethrowFromSystemServer();
841        }
842    }
843
844    /**
845     * This is not the API you're looking for.
846     *
847     * @see PackageManager#movePrimaryStorage(VolumeInfo)
848     * @hide
849     */
850    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
851        try {
852            mMountService.setPrimaryStorageUuid(volumeUuid, callback);
853        } catch (RemoteException e) {
854            throw e.rethrowFromSystemServer();
855        }
856    }
857
858    /**
859     * Return the {@link StorageVolume} that contains the given file, or {@code null} if none.
860     */
861    public @Nullable StorageVolume getStorageVolume(File file) {
862        return getStorageVolume(getVolumeList(), file);
863    }
864
865    /** {@hide} */
866    public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
867        return getStorageVolume(getVolumeList(userId, 0), file);
868    }
869
870    /** {@hide} */
871    private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
872        if (file == null) {
873            return null;
874        }
875        try {
876            file = file.getCanonicalFile();
877        } catch (IOException ignored) {
878            Slog.d(TAG, "Could not get canonical path for " + file);
879            return null;
880        }
881        for (StorageVolume volume : volumes) {
882            File volumeFile = volume.getPathFile();
883            try {
884                volumeFile = volumeFile.getCanonicalFile();
885            } catch (IOException ignored) {
886                continue;
887            }
888            if (FileUtils.contains(volumeFile, file)) {
889                return volume;
890            }
891        }
892        return null;
893    }
894
895    /**
896     * Gets the state of a volume via its mountpoint.
897     * @hide
898     */
899    @Deprecated
900    public @NonNull String getVolumeState(String mountPoint) {
901        final StorageVolume vol = getStorageVolume(new File(mountPoint));
902        if (vol != null) {
903            return vol.getState();
904        } else {
905            return Environment.MEDIA_UNKNOWN;
906        }
907    }
908
909    /**
910     * Return the list of shared/external storage volumes available to the
911     * current user. This includes both the primary shared storage device and
912     * any attached external volumes including SD cards and USB drives.
913     *
914     * @see Environment#getExternalStorageDirectory()
915     * @see StorageVolume#createAccessIntent(String)
916     */
917    public @NonNull List<StorageVolume> getStorageVolumes() {
918        final ArrayList<StorageVolume> res = new ArrayList<>();
919        Collections.addAll(res,
920                getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
921        return res;
922    }
923
924    /**
925     * Return the primary shared/external storage volume available to the
926     * current user. This volume is the same storage device returned by
927     * {@link Environment#getExternalStorageDirectory()} and
928     * {@link Context#getExternalFilesDir(String)}.
929     */
930    public @NonNull StorageVolume getPrimaryStorageVolume() {
931        return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
932    }
933
934    /** {@hide} */
935    public static Pair<String, Long> getPrimaryStoragePathAndSize() {
936        for (String path : INTERNAL_STORAGE_SIZE_PATHS) {
937            final long numberBlocks = readLong(path);
938            if (numberBlocks > 0) {
939                return new Pair<>(path, Long.valueOf(numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE));
940            }
941        }
942        return null;
943    }
944
945
946    /** {@hide} */
947    public long getPrimaryStorageSize() {
948        final Pair<String, Long> pair = getPrimaryStoragePathAndSize();
949        return pair == null ? 0 : pair.second.longValue();
950    }
951
952    private static long readLong(String path) {
953        try (final FileInputStream fis = new FileInputStream(path);
954                final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) {
955            return Long.parseLong(reader.readLine());
956        } catch (FileNotFoundException e) {
957            // This is expected since we are trying to parse multiple paths.
958            Slog.i(TAG, "readLong(): Path doesn't exist: " + path + ": " + e);
959            return 0;
960        } catch (NumberFormatException e) {
961            Slog.e(TAG, "readLong(): Could not parse " + path + ": " + e);
962            return 0;
963        } catch (Exception e) {
964            Slog.e(TAG, "readLong(): Unknown exception while opening " + path + ": " + e);
965            return 0;
966       }
967    }
968
969    /** @removed */
970    public @NonNull StorageVolume[] getVolumeList() {
971        return getVolumeList(mContext.getUserId(), 0);
972    }
973
974    /** {@hide} */
975    public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
976        final IMountService mountService = IMountService.Stub.asInterface(
977                ServiceManager.getService("mount"));
978        try {
979            String packageName = ActivityThread.currentOpPackageName();
980            if (packageName == null) {
981                // Package name can be null if the activity thread is running but the app
982                // hasn't bound yet. In this case we fall back to the first package in the
983                // current UID. This works for runtime permissions as permission state is
984                // per UID and permission realted app ops are updated for all UID packages.
985                String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
986                        android.os.Process.myUid());
987                if (packageNames == null || packageNames.length <= 0) {
988                    return new StorageVolume[0];
989                }
990                packageName = packageNames[0];
991            }
992            final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
993                    PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
994            if (uid <= 0) {
995                return new StorageVolume[0];
996            }
997            return mountService.getVolumeList(uid, packageName, flags);
998        } catch (RemoteException e) {
999            throw e.rethrowFromSystemServer();
1000        }
1001    }
1002
1003    /**
1004     * Returns list of paths for all mountable volumes.
1005     * @hide
1006     */
1007    @Deprecated
1008    public @NonNull String[] getVolumePaths() {
1009        StorageVolume[] volumes = getVolumeList();
1010        int count = volumes.length;
1011        String[] paths = new String[count];
1012        for (int i = 0; i < count; i++) {
1013            paths[i] = volumes[i].getPath();
1014        }
1015        return paths;
1016    }
1017
1018    /** @removed */
1019    public @NonNull StorageVolume getPrimaryVolume() {
1020        return getPrimaryVolume(getVolumeList());
1021    }
1022
1023    /** {@hide} */
1024    public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
1025        for (StorageVolume volume : volumes) {
1026            if (volume.isPrimary()) {
1027                return volume;
1028            }
1029        }
1030        throw new IllegalStateException("Missing primary storage");
1031    }
1032
1033    /** {@hide} */
1034    private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
1035    private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
1036    private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
1037
1038    /**
1039     * Return the number of available bytes until the given path is considered
1040     * running low on storage.
1041     *
1042     * @hide
1043     */
1044    public long getStorageBytesUntilLow(File path) {
1045        return path.getUsableSpace() - getStorageFullBytes(path);
1046    }
1047
1048    /**
1049     * Return the number of available bytes at which the given path is
1050     * considered running low on storage.
1051     *
1052     * @hide
1053     */
1054    public long getStorageLowBytes(File path) {
1055        final long lowPercent = Settings.Global.getInt(mResolver,
1056                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
1057        final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
1058
1059        final long maxLowBytes = Settings.Global.getLong(mResolver,
1060                Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
1061
1062        return Math.min(lowBytes, maxLowBytes);
1063    }
1064
1065    /**
1066     * Return the number of available bytes at which the given path is
1067     * considered full.
1068     *
1069     * @hide
1070     */
1071    public long getStorageFullBytes(File path) {
1072        return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
1073                DEFAULT_FULL_THRESHOLD_BYTES);
1074    }
1075
1076    /** {@hide} */
1077    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
1078        try {
1079            mMountService.createUserKey(userId, serialNumber, ephemeral);
1080        } catch (RemoteException e) {
1081            throw e.rethrowFromSystemServer();
1082        }
1083    }
1084
1085    /** {@hide} */
1086    public void destroyUserKey(int userId) {
1087        try {
1088            mMountService.destroyUserKey(userId);
1089        } catch (RemoteException e) {
1090            throw e.rethrowFromSystemServer();
1091        }
1092    }
1093
1094    /** {@hide} */
1095    public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
1096        try {
1097            mMountService.unlockUserKey(userId, serialNumber, token, secret);
1098        } catch (RemoteException e) {
1099            throw e.rethrowFromSystemServer();
1100        }
1101    }
1102
1103    /** {@hide} */
1104    public void lockUserKey(int userId) {
1105        try {
1106            mMountService.lockUserKey(userId);
1107        } catch (RemoteException e) {
1108            throw e.rethrowFromSystemServer();
1109        }
1110    }
1111
1112    /** {@hide} */
1113    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
1114        try {
1115            mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
1116        } catch (RemoteException e) {
1117            throw e.rethrowFromSystemServer();
1118        }
1119    }
1120
1121    /** {@hide} */
1122    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
1123        try {
1124            mMountService.destroyUserStorage(volumeUuid, userId, flags);
1125        } catch (RemoteException e) {
1126            throw e.rethrowFromSystemServer();
1127        }
1128    }
1129
1130    /** {@hide} */
1131    public static boolean isUserKeyUnlocked(int userId) {
1132        if (sMountService == null) {
1133            sMountService = IMountService.Stub
1134                    .asInterface(ServiceManager.getService("mount"));
1135        }
1136        if (sMountService == null) {
1137            Slog.w(TAG, "Early during boot, assuming locked");
1138            return false;
1139        }
1140        final long token = Binder.clearCallingIdentity();
1141        try {
1142            return sMountService.isUserKeyUnlocked(userId);
1143        } catch (RemoteException e) {
1144            throw e.rethrowAsRuntimeException();
1145        } finally {
1146            Binder.restoreCallingIdentity(token);
1147        }
1148    }
1149
1150    /**
1151     * Return if data stored at or under the given path will be encrypted while
1152     * at rest. This can help apps avoid the overhead of double-encrypting data.
1153     */
1154    public boolean isEncrypted(File file) {
1155        if (FileUtils.contains(Environment.getDataDirectory(), file)) {
1156            return isEncrypted();
1157        } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) {
1158            return true;
1159        }
1160        // TODO: extend to support shared storage
1161        return false;
1162    }
1163
1164    /** {@hide}
1165     * Is this device encryptable or already encrypted?
1166     * @return true for encryptable or encrypted
1167     *         false not encrypted and not encryptable
1168     */
1169    public static boolean isEncryptable() {
1170        final String state = SystemProperties.get("ro.crypto.state", "unsupported");
1171        return !"unsupported".equalsIgnoreCase(state);
1172    }
1173
1174    /** {@hide}
1175     * Is this device already encrypted?
1176     * @return true for encrypted. (Implies isEncryptable() == true)
1177     *         false not encrypted
1178     */
1179    public static boolean isEncrypted() {
1180        final String state = SystemProperties.get("ro.crypto.state", "");
1181        return "encrypted".equalsIgnoreCase(state);
1182    }
1183
1184    /** {@hide}
1185     * Is this device file encrypted?
1186     * @return true for file encrypted. (Implies isEncrypted() == true)
1187     *         false not encrypted or block encrypted
1188     */
1189    public static boolean isFileEncryptedNativeOnly() {
1190        if (!isEncrypted()) {
1191            return false;
1192        }
1193
1194        final String status = SystemProperties.get("ro.crypto.type", "");
1195        return "file".equalsIgnoreCase(status);
1196    }
1197
1198    /** {@hide}
1199     * Is this device block encrypted?
1200     * @return true for block encrypted. (Implies isEncrypted() == true)
1201     *         false not encrypted or file encrypted
1202     */
1203    public static boolean isBlockEncrypted() {
1204        if (!isEncrypted()) {
1205            return false;
1206        }
1207        final String status = SystemProperties.get("ro.crypto.type", "");
1208        return "block".equalsIgnoreCase(status);
1209    }
1210
1211    /** {@hide}
1212     * Is this device block encrypted with credentials?
1213     * @return true for crediential block encrypted.
1214     *         (Implies isBlockEncrypted() == true)
1215     *         false not encrypted, file encrypted or default block encrypted
1216     */
1217    public static boolean isNonDefaultBlockEncrypted() {
1218        if (!isBlockEncrypted()) {
1219            return false;
1220        }
1221
1222        try {
1223            IMountService mountService = IMountService.Stub.asInterface(
1224                    ServiceManager.getService("mount"));
1225            return mountService.getPasswordType() != CRYPT_TYPE_DEFAULT;
1226        } catch (RemoteException e) {
1227            Log.e(TAG, "Error getting encryption type");
1228            return false;
1229        }
1230    }
1231
1232    /** {@hide}
1233     * Is this device in the process of being block encrypted?
1234     * @return true for encrypting.
1235     *         false otherwise
1236     * Whether device isEncrypted at this point is undefined
1237     * Note that only system services and CryptKeeper will ever see this return
1238     * true - no app will ever be launched in this state.
1239     * Also note that this state will not change without a teardown of the
1240     * framework, so no service needs to check for changes during their lifespan
1241     */
1242    public static boolean isBlockEncrypting() {
1243        final String state = SystemProperties.get("vold.encrypt_progress", "");
1244        return !"".equalsIgnoreCase(state);
1245    }
1246
1247    /** {@hide}
1248     * Is this device non default block encrypted and in the process of
1249     * prompting for credentials?
1250     * @return true for prompting for credentials.
1251     *         (Implies isNonDefaultBlockEncrypted() == true)
1252     *         false otherwise
1253     * Note that only system services and CryptKeeper will ever see this return
1254     * true - no app will ever be launched in this state.
1255     * Also note that this state will not change without a teardown of the
1256     * framework, so no service needs to check for changes during their lifespan
1257     */
1258    public static boolean inCryptKeeperBounce() {
1259        final String status = SystemProperties.get("vold.decrypt");
1260        return "trigger_restart_min_framework".equals(status);
1261    }
1262
1263    /** {@hide} */
1264    public static boolean isFileEncryptedEmulatedOnly() {
1265        return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false);
1266    }
1267
1268    /** {@hide}
1269     * Is this device running in a file encrypted mode, either native or emulated?
1270     * @return true for file encrypted, false otherwise
1271     */
1272    public static boolean isFileEncryptedNativeOrEmulated() {
1273        return isFileEncryptedNativeOnly()
1274               || isFileEncryptedEmulatedOnly();
1275    }
1276
1277    /** {@hide} */
1278    public static File maybeTranslateEmulatedPathToInternal(File path) {
1279        final IMountService mountService = IMountService.Stub.asInterface(
1280                ServiceManager.getService("mount"));
1281        try {
1282            final VolumeInfo[] vols = mountService.getVolumes(0);
1283            for (VolumeInfo vol : vols) {
1284                if ((vol.getType() == VolumeInfo.TYPE_EMULATED
1285                        || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
1286                    final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
1287                            vol.getInternalPath(), path);
1288                    if (internalPath != null && internalPath.exists()) {
1289                        return internalPath;
1290                    }
1291                }
1292            }
1293        } catch (RemoteException e) {
1294            throw e.rethrowFromSystemServer();
1295        }
1296        return path;
1297    }
1298
1299    /** {@hide} */
1300    public ParcelFileDescriptor mountAppFuse(String name) {
1301        try {
1302            return mMountService.mountAppFuse(name);
1303        } catch (RemoteException e) {
1304            throw e.rethrowFromSystemServer();
1305        }
1306    }
1307
1308    /// Consts to match the password types in cryptfs.h
1309    /** @hide */
1310    public static final int CRYPT_TYPE_PASSWORD = 0;
1311    /** @hide */
1312    public static final int CRYPT_TYPE_DEFAULT = 1;
1313    /** @hide */
1314    public static final int CRYPT_TYPE_PATTERN = 2;
1315    /** @hide */
1316    public static final int CRYPT_TYPE_PIN = 3;
1317
1318    // Constants for the data available via MountService.getField.
1319    /** @hide */
1320    public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
1321    /** @hide */
1322    public static final String OWNER_INFO_KEY = "OwnerInfo";
1323    /** @hide */
1324    public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
1325    /** @hide */
1326    public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible";
1327}
1328