2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.os.storage;
19 import static android.net.TrafficStats.MB_IN_BYTES;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.app.ActivityThread;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.pm.IPackageMoveObserver;
27 import android.content.pm.PackageManager;
28 import android.os.Binder;
29 import android.os.Environment;
30 import android.os.FileUtils;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.ParcelFileDescriptor;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 import android.os.SystemProperties;
38 import android.os.UserHandle;
39 import android.provider.Settings;
40 import android.text.TextUtils;
41 import android.util.Log;
42 import android.util.Slog;
43 import android.util.SparseArray;
45 import com.android.internal.os.SomeArgs;
46 import com.android.internal.util.Preconditions;
49 import java.io.IOException;
50 import java.lang.ref.WeakReference;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Collections;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Objects;
57 import java.util.concurrent.atomic.AtomicInteger;
60 * StorageManager is the interface to the systems storage service. The storage
61 * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
63 * OBBs contain a filesystem that maybe be encrypted on disk and mounted
64 * on-demand from an application. OBBs are a good way of providing large amounts
65 * of binary assets without packaging them into APKs as they may be multiple
66 * gigabytes in size. However, due to their size, they're most likely stored in
67 * a shared storage pool accessible from all programs. The system does not
68 * guarantee the security of the OBB file itself: if any program modifies the
69 * OBB, there is no guarantee that a read from that OBB will produce the
72 * Get an instance of this class by calling
73 * {@link android.content.Context#getSystemService(java.lang.String)} with an
74 * argument of {@link android.content.Context#STORAGE_SERVICE}.
76 public class StorageManager {
77 private static final String TAG = "StorageManager";
80 public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
82 public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
84 public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
86 public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe";
88 public static final String PROP_SDCARDFS = "persist.sys.sdcardfs";
91 public static final String UUID_PRIVATE_INTERNAL = null;
93 public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
96 public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
98 public static final int DEBUG_EMULATE_FBE = 1 << 1;
100 public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2;
102 public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3;
104 // NOTE: keep in sync with installd
106 public static final int FLAG_STORAGE_DE = 1 << 0;
108 public static final int FLAG_STORAGE_CE = 1 << 1;
111 public static final int FLAG_FOR_WRITE = 1 << 8;
113 public static final int FLAG_REAL_STATE = 1 << 9;
115 public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
117 private static volatile IMountService sMountService = null;
119 private final Context mContext;
120 private final ContentResolver mResolver;
122 private final IMountService mMountService;
123 private final Looper mLooper;
124 private final AtomicInteger mNextNonce = new AtomicInteger(0);
126 private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>();
128 private static class StorageEventListenerDelegate extends IMountServiceListener.Stub implements
130 private static final int MSG_STORAGE_STATE_CHANGED = 1;
131 private static final int MSG_VOLUME_STATE_CHANGED = 2;
132 private static final int MSG_VOLUME_RECORD_CHANGED = 3;
133 private static final int MSG_VOLUME_FORGOTTEN = 4;
134 private static final int MSG_DISK_SCANNED = 5;
135 private static final int MSG_DISK_DESTROYED = 6;
137 final StorageEventListener mCallback;
138 final Handler mHandler;
140 public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
141 mCallback = callback;
142 mHandler = new Handler(looper, this);
146 public boolean handleMessage(Message msg) {
147 final SomeArgs args = (SomeArgs) msg.obj;
149 case MSG_STORAGE_STATE_CHANGED:
150 mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
154 case MSG_VOLUME_STATE_CHANGED:
155 mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
158 case MSG_VOLUME_RECORD_CHANGED:
159 mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1);
162 case MSG_VOLUME_FORGOTTEN:
163 mCallback.onVolumeForgotten((String) args.arg1);
166 case MSG_DISK_SCANNED:
167 mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
170 case MSG_DISK_DESTROYED:
171 mCallback.onDiskDestroyed((DiskInfo) args.arg1);
180 public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
185 public void onStorageStateChanged(String path, String oldState, String newState) {
186 final SomeArgs args = SomeArgs.obtain();
188 args.arg2 = oldState;
189 args.arg3 = newState;
190 mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
194 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
195 final SomeArgs args = SomeArgs.obtain();
197 args.argi2 = oldState;
198 args.argi3 = newState;
199 mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
203 public void onVolumeRecordChanged(VolumeRecord rec) {
204 final SomeArgs args = SomeArgs.obtain();
206 mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
210 public void onVolumeForgotten(String fsUuid) {
211 final SomeArgs args = SomeArgs.obtain();
213 mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
217 public void onDiskScanned(DiskInfo disk, int volumeCount) {
218 final SomeArgs args = SomeArgs.obtain();
220 args.argi2 = volumeCount;
221 mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
225 public void onDiskDestroyed(DiskInfo disk) throws RemoteException {
226 final SomeArgs args = SomeArgs.obtain();
228 mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
233 * Binder listener for OBB action results.
235 private final ObbActionListener mObbActionListener = new ObbActionListener();
237 private class ObbActionListener extends IObbActionListener.Stub {
238 @SuppressWarnings("hiding")
239 private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
242 public void onObbResult(String filename, int nonce, int status) {
243 final ObbListenerDelegate delegate;
244 synchronized (mListeners) {
245 delegate = mListeners.get(nonce);
246 if (delegate != null) {
247 mListeners.remove(nonce);
251 if (delegate != null) {
252 delegate.sendObbStateChanged(filename, status);
256 public int addListener(OnObbStateChangeListener listener) {
257 final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
259 synchronized (mListeners) {
260 mListeners.put(delegate.nonce, delegate);
263 return delegate.nonce;
267 private int getNextNonce() {
268 return mNextNonce.getAndIncrement();
272 * Private class containing sender and receiver code for StorageEvents.
274 private class ObbListenerDelegate {
275 private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
276 private final Handler mHandler;
278 private final int nonce;
280 ObbListenerDelegate(OnObbStateChangeListener listener) {
281 nonce = getNextNonce();
282 mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
283 mHandler = new Handler(mLooper) {
285 public void handleMessage(Message msg) {
286 final OnObbStateChangeListener changeListener = getListener();
287 if (changeListener == null) {
291 changeListener.onObbStateChange((String) msg.obj, msg.arg1);
296 OnObbStateChangeListener getListener() {
297 if (mObbEventListenerRef == null) {
300 return mObbEventListenerRef.get();
303 void sendObbStateChanged(String path, int state) {
304 mHandler.obtainMessage(0, state, 0, path).sendToTarget();
310 public static StorageManager from(Context context) {
311 return context.getSystemService(StorageManager.class);
315 * Constructs a StorageManager object through which an application can
316 * can communicate with the systems mount service.
318 * @param tgtLooper The {@link android.os.Looper} which events will be received on.
320 * <p>Applications can get instance of this class by calling
321 * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
322 * of {@link android.content.Context#STORAGE_SERVICE}.
326 public StorageManager(Context context, Looper looper) {
328 mResolver = context.getContentResolver();
330 mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
331 if (mMountService == null) {
332 throw new IllegalStateException("Failed to find running mount service");
337 * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
339 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
343 public void registerListener(StorageEventListener listener) {
344 synchronized (mDelegates) {
345 final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener,
348 mMountService.registerListener(delegate);
349 } catch (RemoteException e) {
350 throw e.rethrowFromSystemServer();
352 mDelegates.add(delegate);
357 * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
359 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
363 public void unregisterListener(StorageEventListener listener) {
364 synchronized (mDelegates) {
365 for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
366 final StorageEventListenerDelegate delegate = i.next();
367 if (delegate.mCallback == listener) {
369 mMountService.unregisterListener(delegate);
370 } catch (RemoteException e) {
371 throw e.rethrowFromSystemServer();
380 * Enables USB Mass Storage (UMS) on the device.
385 public void enableUsbMassStorage() {
389 * Disables USB Mass Storage (UMS) on the device.
394 public void disableUsbMassStorage() {
398 * Query if a USB Mass Storage (UMS) host is connected.
399 * @return true if UMS host is connected.
404 public boolean isUsbMassStorageConnected() {
409 * Query if a USB Mass Storage (UMS) is enabled on the device.
410 * @return true if UMS host is enabled.
415 public boolean isUsbMassStorageEnabled() {
420 * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
421 * specified, it is supplied to the mounting process to be used in any
422 * encryption used in the OBB.
424 * The OBB will remain mounted for as long as the StorageManager reference
425 * is held by the application. As soon as this reference is lost, the OBBs
426 * in use will be unmounted. The {@link OnObbStateChangeListener} registered
427 * with this call will receive the success or failure of this operation.
429 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
430 * file matches a package ID that is owned by the calling program's UID.
431 * That is, shared UID applications can attempt to mount any other
432 * application's OBB that shares its UID.
434 * @param rawPath the path to the OBB file
435 * @param key secret used to encrypt the OBB; may be <code>null</code> if no
436 * encryption was used on the OBB.
437 * @param listener will receive the success or failure of the operation
438 * @return whether the mount call was successfully queued or not
440 public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
441 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
442 Preconditions.checkNotNull(listener, "listener cannot be null");
445 final String canonicalPath = new File(rawPath).getCanonicalPath();
446 final int nonce = mObbActionListener.addListener(listener);
447 mMountService.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce);
449 } catch (IOException e) {
450 throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e);
451 } catch (RemoteException e) {
452 throw e.rethrowFromSystemServer();
457 * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
458 * <code>force</code> flag is true, it will kill any application needed to
459 * unmount the given OBB (even the calling application).
461 * The {@link OnObbStateChangeListener} registered with this call will
462 * receive the success or failure of this operation.
464 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
465 * file matches a package ID that is owned by the calling program's UID.
466 * That is, shared UID applications can obtain access to any other
467 * application's OBB that shares its UID.
470 * @param rawPath path to the OBB file
471 * @param force whether to kill any programs using this in order to unmount
473 * @param listener will receive the success or failure of the operation
474 * @return whether the unmount call was successfully queued or not
476 public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
477 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
478 Preconditions.checkNotNull(listener, "listener cannot be null");
481 final int nonce = mObbActionListener.addListener(listener);
482 mMountService.unmountObb(rawPath, force, mObbActionListener, nonce);
484 } catch (RemoteException e) {
485 throw e.rethrowFromSystemServer();
490 * Check whether an Opaque Binary Blob (OBB) is mounted or not.
492 * @param rawPath path to OBB image
493 * @return true if OBB is mounted; false if not mounted or on error
495 public boolean isObbMounted(String rawPath) {
496 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
499 return mMountService.isObbMounted(rawPath);
500 } catch (RemoteException e) {
501 throw e.rethrowFromSystemServer();
506 * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
507 * give you the path to where you can obtain access to the internals of the
510 * @param rawPath path to OBB image
511 * @return absolute path to mounted OBB image data or <code>null</code> if
512 * not mounted or exception encountered trying to read status
514 public String getMountedObbPath(String rawPath) {
515 Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
518 return mMountService.getMountedObbPath(rawPath);
519 } catch (RemoteException e) {
520 throw e.rethrowFromSystemServer();
525 public @NonNull List<DiskInfo> getDisks() {
527 return Arrays.asList(mMountService.getDisks());
528 } catch (RemoteException e) {
529 throw e.rethrowFromSystemServer();
534 public @Nullable DiskInfo findDiskById(String id) {
535 Preconditions.checkNotNull(id);
536 // TODO; go directly to service to make this faster
537 for (DiskInfo disk : getDisks()) {
538 if (Objects.equals(disk.id, id)) {
546 public @Nullable VolumeInfo findVolumeById(String id) {
547 Preconditions.checkNotNull(id);
548 // TODO; go directly to service to make this faster
549 for (VolumeInfo vol : getVolumes()) {
550 if (Objects.equals(vol.id, id)) {
558 public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
559 Preconditions.checkNotNull(fsUuid);
560 // TODO; go directly to service to make this faster
561 for (VolumeInfo vol : getVolumes()) {
562 if (Objects.equals(vol.fsUuid, fsUuid)) {
570 public @Nullable VolumeRecord findRecordByUuid(String fsUuid) {
571 Preconditions.checkNotNull(fsUuid);
572 // TODO; go directly to service to make this faster
573 for (VolumeRecord rec : getVolumeRecords()) {
574 if (Objects.equals(rec.fsUuid, fsUuid)) {
582 public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
583 if (emulatedVol != null) {
584 return findVolumeById(emulatedVol.getId().replace("emulated", "private"));
591 public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
592 if (privateVol != null) {
593 return findVolumeById(privateVol.getId().replace("private", "emulated"));
600 public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
601 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
602 return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
603 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
604 return getPrimaryPhysicalVolume();
606 return findVolumeByUuid(volumeUuid);
611 public @NonNull List<VolumeInfo> getVolumes() {
613 return Arrays.asList(mMountService.getVolumes(0));
614 } catch (RemoteException e) {
615 throw e.rethrowFromSystemServer();
620 public @NonNull List<VolumeInfo> getWritablePrivateVolumes() {
622 final ArrayList<VolumeInfo> res = new ArrayList<>();
623 for (VolumeInfo vol : mMountService.getVolumes(0)) {
624 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
629 } catch (RemoteException e) {
630 throw e.rethrowFromSystemServer();
635 public @NonNull List<VolumeRecord> getVolumeRecords() {
637 return Arrays.asList(mMountService.getVolumeRecords(0));
638 } catch (RemoteException e) {
639 throw e.rethrowFromSystemServer();
644 public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
645 if (vol == null) return null;
647 // Nickname always takes precedence when defined
648 if (!TextUtils.isEmpty(vol.fsUuid)) {
649 final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
650 if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
655 if (!TextUtils.isEmpty(vol.getDescription())) {
656 return vol.getDescription();
659 if (vol.disk != null) {
660 return vol.disk.getDescription();
667 public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
668 final List<VolumeInfo> vols = getVolumes();
669 for (VolumeInfo vol : vols) {
670 if (vol.isPrimaryPhysical()) {
678 public void mount(String volId) {
680 mMountService.mount(volId);
681 } catch (RemoteException e) {
682 throw e.rethrowFromSystemServer();
687 public void unmount(String volId) {
689 mMountService.unmount(volId);
690 } catch (RemoteException e) {
691 throw e.rethrowFromSystemServer();
696 public void format(String volId) {
698 mMountService.format(volId);
699 } catch (RemoteException e) {
700 throw e.rethrowFromSystemServer();
705 public long benchmark(String volId) {
707 return mMountService.benchmark(volId);
708 } catch (RemoteException e) {
709 throw e.rethrowFromSystemServer();
714 public void partitionPublic(String diskId) {
716 mMountService.partitionPublic(diskId);
717 } catch (RemoteException e) {
718 throw e.rethrowFromSystemServer();
723 public void partitionPrivate(String diskId) {
725 mMountService.partitionPrivate(diskId);
726 } catch (RemoteException e) {
727 throw e.rethrowFromSystemServer();
732 public void partitionMixed(String diskId, int ratio) {
734 mMountService.partitionMixed(diskId, ratio);
735 } catch (RemoteException e) {
736 throw e.rethrowFromSystemServer();
741 public void wipeAdoptableDisks() {
742 // We only wipe devices in "adoptable" locations, which are in a
743 // long-term stable slot/location on the device, where apps have a
744 // reasonable chance of storing sensitive data. (Apps need to go through
745 // SAF to write to transient volumes.)
746 final List<DiskInfo> disks = getDisks();
747 for (DiskInfo disk : disks) {
748 final String diskId = disk.getId();
749 if (disk.isAdoptable()) {
750 Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
752 // TODO: switch to explicit wipe command when we have it,
753 // for now rely on the fact that vfat format does a wipe
754 mMountService.partitionPublic(diskId);
755 } catch (Exception e) {
756 Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
759 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
765 public void setVolumeNickname(String fsUuid, String nickname) {
767 mMountService.setVolumeNickname(fsUuid, nickname);
768 } catch (RemoteException e) {
769 throw e.rethrowFromSystemServer();
774 public void setVolumeInited(String fsUuid, boolean inited) {
776 mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0,
777 VolumeRecord.USER_FLAG_INITED);
778 } catch (RemoteException e) {
779 throw e.rethrowFromSystemServer();
784 public void setVolumeSnoozed(String fsUuid, boolean snoozed) {
786 mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0,
787 VolumeRecord.USER_FLAG_SNOOZED);
788 } catch (RemoteException e) {
789 throw e.rethrowFromSystemServer();
794 public void forgetVolume(String fsUuid) {
796 mMountService.forgetVolume(fsUuid);
797 } catch (RemoteException e) {
798 throw e.rethrowFromSystemServer();
803 * This is not the API you're looking for.
805 * @see PackageManager#getPrimaryStorageCurrentVolume()
808 public String getPrimaryStorageUuid() {
810 return mMountService.getPrimaryStorageUuid();
811 } catch (RemoteException e) {
812 throw e.rethrowFromSystemServer();
817 * This is not the API you're looking for.
819 * @see PackageManager#movePrimaryStorage(VolumeInfo)
822 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
824 mMountService.setPrimaryStorageUuid(volumeUuid, callback);
825 } catch (RemoteException e) {
826 throw e.rethrowFromSystemServer();
831 * Return the {@link StorageVolume} that contains the given file, or {@code null} if none.
833 public @Nullable StorageVolume getStorageVolume(File file) {
834 return getStorageVolume(getVolumeList(), file);
838 public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
839 return getStorageVolume(getVolumeList(userId, 0), file);
843 private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
848 file = file.getCanonicalFile();
849 } catch (IOException ignored) {
850 Slog.d(TAG, "Could not get canonical path for " + file);
853 for (StorageVolume volume : volumes) {
854 File volumeFile = volume.getPathFile();
856 volumeFile = volumeFile.getCanonicalFile();
857 } catch (IOException ignored) {
860 if (FileUtils.contains(volumeFile, file)) {
868 * Gets the state of a volume via its mountpoint.
872 public @NonNull String getVolumeState(String mountPoint) {
873 final StorageVolume vol = getStorageVolume(new File(mountPoint));
875 return vol.getState();
877 return Environment.MEDIA_UNKNOWN;
882 * Return the list of shared/external storage volumes available to the
883 * current user. This includes both the primary shared storage device and
884 * any attached external volumes including SD cards and USB drives.
886 * @see Environment#getExternalStorageDirectory()
887 * @see StorageVolume#createAccessIntent(String)
889 public @NonNull List<StorageVolume> getStorageVolumes() {
890 final ArrayList<StorageVolume> res = new ArrayList<>();
891 Collections.addAll(res,
892 getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
897 * Return the primary shared/external storage volume available to the
898 * current user. This volume is the same storage device returned by
899 * {@link Environment#getExternalStorageDirectory()} and
900 * {@link Context#getExternalFilesDir(String)}.
902 public @NonNull StorageVolume getPrimaryStorageVolume() {
903 return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
907 public @NonNull StorageVolume[] getVolumeList() {
908 return getVolumeList(mContext.getUserId(), 0);
912 public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
913 final IMountService mountService = IMountService.Stub.asInterface(
914 ServiceManager.getService("mount"));
916 String packageName = ActivityThread.currentOpPackageName();
917 if (packageName == null) {
918 // Package name can be null if the activity thread is running but the app
919 // hasn't bound yet. In this case we fall back to the first package in the
920 // current UID. This works for runtime permissions as permission state is
921 // per UID and permission realted app ops are updated for all UID packages.
922 String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
923 android.os.Process.myUid());
924 if (packageNames == null || packageNames.length <= 0) {
925 return new StorageVolume[0];
927 packageName = packageNames[0];
929 final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
930 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
932 return new StorageVolume[0];
934 return mountService.getVolumeList(uid, packageName, flags);
935 } catch (RemoteException e) {
936 throw e.rethrowFromSystemServer();
941 * Returns list of paths for all mountable volumes.
945 public @NonNull String[] getVolumePaths() {
946 StorageVolume[] volumes = getVolumeList();
947 int count = volumes.length;
948 String[] paths = new String[count];
949 for (int i = 0; i < count; i++) {
950 paths[i] = volumes[i].getPath();
956 public @NonNull StorageVolume getPrimaryVolume() {
957 return getPrimaryVolume(getVolumeList());
961 public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
962 for (StorageVolume volume : volumes) {
963 if (volume.isPrimary()) {
967 throw new IllegalStateException("Missing primary storage");
971 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
972 private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
973 private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
976 * Return the number of available bytes until the given path is considered
977 * running low on storage.
981 public long getStorageBytesUntilLow(File path) {
982 return path.getUsableSpace() - getStorageFullBytes(path);
986 * Return the number of available bytes at which the given path is
987 * considered running low on storage.
991 public long getStorageLowBytes(File path) {
992 final long lowPercent = Settings.Global.getInt(mResolver,
993 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
994 final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
996 final long maxLowBytes = Settings.Global.getLong(mResolver,
997 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
999 return Math.min(lowBytes, maxLowBytes);
1003 * Return the number of available bytes at which the given path is
1008 public long getStorageFullBytes(File path) {
1009 return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
1010 DEFAULT_FULL_THRESHOLD_BYTES);
1014 public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
1016 mMountService.createUserKey(userId, serialNumber, ephemeral);
1017 } catch (RemoteException e) {
1018 throw e.rethrowFromSystemServer();
1023 public void destroyUserKey(int userId) {
1025 mMountService.destroyUserKey(userId);
1026 } catch (RemoteException e) {
1027 throw e.rethrowFromSystemServer();
1032 public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
1034 mMountService.unlockUserKey(userId, serialNumber, token, secret);
1035 } catch (RemoteException e) {
1036 throw e.rethrowFromSystemServer();
1041 public void lockUserKey(int userId) {
1043 mMountService.lockUserKey(userId);
1044 } catch (RemoteException e) {
1045 throw e.rethrowFromSystemServer();
1050 public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
1052 mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
1053 } catch (RemoteException e) {
1054 throw e.rethrowFromSystemServer();
1059 public void destroyUserStorage(String volumeUuid, int userId, int flags) {
1061 mMountService.destroyUserStorage(volumeUuid, userId, flags);
1062 } catch (RemoteException e) {
1063 throw e.rethrowFromSystemServer();
1068 public static boolean isUserKeyUnlocked(int userId) {
1069 if (sMountService == null) {
1070 sMountService = IMountService.Stub
1071 .asInterface(ServiceManager.getService("mount"));
1073 if (sMountService == null) {
1074 Slog.w(TAG, "Early during boot, assuming locked");
1077 final long token = Binder.clearCallingIdentity();
1079 return sMountService.isUserKeyUnlocked(userId);
1080 } catch (RemoteException e) {
1081 throw e.rethrowAsRuntimeException();
1083 Binder.restoreCallingIdentity(token);
1088 * Return if data stored at or under the given path will be encrypted while
1089 * at rest. This can help apps avoid the overhead of double-encrypting data.
1091 public boolean isEncrypted(File file) {
1092 if (FileUtils.contains(Environment.getDataDirectory(), file)) {
1093 return isEncrypted();
1094 } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) {
1097 // TODO: extend to support shared storage
1102 * Is this device encryptable or already encrypted?
1103 * @return true for encryptable or encrypted
1104 * false not encrypted and not encryptable
1106 public static boolean isEncryptable() {
1107 final String state = SystemProperties.get("ro.crypto.state", "unsupported");
1108 return !"unsupported".equalsIgnoreCase(state);
1112 * Is this device already encrypted?
1113 * @return true for encrypted. (Implies isEncryptable() == true)
1114 * false not encrypted
1116 public static boolean isEncrypted() {
1117 final String state = SystemProperties.get("ro.crypto.state", "");
1118 return "encrypted".equalsIgnoreCase(state);
1122 * Is this device file encrypted?
1123 * @return true for file encrypted. (Implies isEncrypted() == true)
1124 * false not encrypted or block encrypted
1126 public static boolean isFileEncryptedNativeOnly() {
1127 if (!isEncrypted()) {
1131 final String status = SystemProperties.get("ro.crypto.type", "");
1132 return "file".equalsIgnoreCase(status);
1136 * Is this device block encrypted?
1137 * @return true for block encrypted. (Implies isEncrypted() == true)
1138 * false not encrypted or file encrypted
1140 public static boolean isBlockEncrypted() {
1141 if (!isEncrypted()) {
1144 final String status = SystemProperties.get("ro.crypto.type", "");
1145 return "block".equalsIgnoreCase(status);
1149 * Is this device block encrypted with credentials?
1150 * @return true for crediential block encrypted.
1151 * (Implies isBlockEncrypted() == true)
1152 * false not encrypted, file encrypted or default block encrypted
1154 public static boolean isNonDefaultBlockEncrypted() {
1155 if (!isBlockEncrypted()) {
1160 IMountService mountService = IMountService.Stub.asInterface(
1161 ServiceManager.getService("mount"));
1162 return mountService.getPasswordType() != CRYPT_TYPE_DEFAULT;
1163 } catch (RemoteException e) {
1164 Log.e(TAG, "Error getting encryption type");
1170 * Is this device in the process of being block encrypted?
1171 * @return true for encrypting.
1173 * Whether device isEncrypted at this point is undefined
1174 * Note that only system services and CryptKeeper will ever see this return
1175 * true - no app will ever be launched in this state.
1176 * Also note that this state will not change without a teardown of the
1177 * framework, so no service needs to check for changes during their lifespan
1179 public static boolean isBlockEncrypting() {
1180 final String state = SystemProperties.get("vold.encrypt_progress", "");
1181 return !"".equalsIgnoreCase(state);
1185 * Is this device non default block encrypted and in the process of
1186 * prompting for credentials?
1187 * @return true for prompting for credentials.
1188 * (Implies isNonDefaultBlockEncrypted() == true)
1190 * Note that only system services and CryptKeeper will ever see this return
1191 * true - no app will ever be launched in this state.
1192 * Also note that this state will not change without a teardown of the
1193 * framework, so no service needs to check for changes during their lifespan
1195 public static boolean inCryptKeeperBounce() {
1196 final String status = SystemProperties.get("vold.decrypt");
1197 return "trigger_restart_min_framework".equals(status);
1201 public static boolean isFileEncryptedEmulatedOnly() {
1202 return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false);
1206 * Is this device running in a file encrypted mode, either native or emulated?
1207 * @return true for file encrypted, false otherwise
1209 public static boolean isFileEncryptedNativeOrEmulated() {
1210 return isFileEncryptedNativeOnly()
1211 || isFileEncryptedEmulatedOnly();
1215 public static File maybeTranslateEmulatedPathToInternal(File path) {
1216 final IMountService mountService = IMountService.Stub.asInterface(
1217 ServiceManager.getService("mount"));
1219 final VolumeInfo[] vols = mountService.getVolumes(0);
1220 for (VolumeInfo vol : vols) {
1221 if ((vol.getType() == VolumeInfo.TYPE_EMULATED
1222 || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
1223 final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
1224 vol.getInternalPath(), path);
1225 if (internalPath != null && internalPath.exists()) {
1226 return internalPath;
1230 } catch (RemoteException e) {
1231 throw e.rethrowFromSystemServer();
1237 public ParcelFileDescriptor mountAppFuse(String name) {
1239 return mMountService.mountAppFuse(name);
1240 } catch (RemoteException e) {
1241 throw e.rethrowFromSystemServer();
1245 /// Consts to match the password types in cryptfs.h
1247 public static final int CRYPT_TYPE_PASSWORD = 0;
1249 public static final int CRYPT_TYPE_DEFAULT = 1;
1251 public static final int CRYPT_TYPE_PATTERN = 2;
1253 public static final int CRYPT_TYPE_PIN = 3;
1255 // Constants for the data available via MountService.getField.
1257 public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
1259 public static final String OWNER_INFO_KEY = "OwnerInfo";
1261 public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
1263 public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible";