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