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.Environment;
29 import android.os.FileUtils;
30 import android.os.Handler;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.RemoteException;
34 import android.os.ServiceManager;
35 import android.provider.Settings;
36 import android.text.TextUtils;
37 import android.util.Log;
38 import android.util.Slog;
39 import android.util.SparseArray;
40 
41 import com.android.internal.os.SomeArgs;
42 import com.android.internal.util.Preconditions;
43 
44 import java.io.File;
45 import java.io.IOException;
46 import java.lang.ref.WeakReference;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Iterator;
50 import java.util.List;
51 import java.util.Objects;
52 import java.util.concurrent.atomic.AtomicInteger;
53 
54 /**
55  * StorageManager is the interface to the systems storage service. The storage
56  * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
57  * <p>
58  * OBBs contain a filesystem that maybe be encrypted on disk and mounted
59  * on-demand from an application. OBBs are a good way of providing large amounts
60  * of binary assets without packaging them into APKs as they may be multiple
61  * gigabytes in size. However, due to their size, they're most likely stored in
62  * a shared storage pool accessible from all programs. The system does not
63  * guarantee the security of the OBB file itself: if any program modifies the
64  * OBB, there is no guarantee that a read from that OBB will produce the
65  * expected output.
66  * <p>
67  * Get an instance of this class by calling
68  * {@link android.content.Context#getSystemService(java.lang.String)} with an
69  * argument of {@link android.content.Context#STORAGE_SERVICE}.
70  */
71 public class StorageManager {
72     private static final String TAG = "StorageManager";
73 
74     /** {@hide} */
75     public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
76     /** {@hide} */
77     public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
78     /** {@hide} */
79     public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
80 
81     /** {@hide} */
82     public static final String UUID_PRIVATE_INTERNAL = null;
83     /** {@hide} */
84     public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
85 
86     /** {@hide} */
87     public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
88 
89     /** {@hide} */
90     public static final int FLAG_FOR_WRITE = 1 << 0;
91 
92     private final Context mContext;
93     private final ContentResolver mResolver;
94 
95     private final IMountService mMountService;
96     private final Looper mLooper;
97     private final AtomicInteger mNextNonce = new AtomicInteger(0);
98 
99     private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>();
100 
101     private static class StorageEventListenerDelegate extends IMountServiceListener.Stub implements
102             Handler.Callback {
103         private static final int MSG_STORAGE_STATE_CHANGED = 1;
104         private static final int MSG_VOLUME_STATE_CHANGED = 2;
105         private static final int MSG_VOLUME_RECORD_CHANGED = 3;
106         private static final int MSG_VOLUME_FORGOTTEN = 4;
107         private static final int MSG_DISK_SCANNED = 5;
108         private static final int MSG_DISK_DESTROYED = 6;
109 
110         final StorageEventListener mCallback;
111         final Handler mHandler;
112 
StorageEventListenerDelegate(StorageEventListener callback, Looper looper)113         public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
114             mCallback = callback;
115             mHandler = new Handler(looper, this);
116         }
117 
118         @Override
handleMessage(Message msg)119         public boolean handleMessage(Message msg) {
120             final SomeArgs args = (SomeArgs) msg.obj;
121             switch (msg.what) {
122                 case MSG_STORAGE_STATE_CHANGED:
123                     mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
124                             (String) args.arg3);
125                     args.recycle();
126                     return true;
127                 case MSG_VOLUME_STATE_CHANGED:
128                     mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
129                     args.recycle();
130                     return true;
131                 case MSG_VOLUME_RECORD_CHANGED:
132                     mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1);
133                     args.recycle();
134                     return true;
135                 case MSG_VOLUME_FORGOTTEN:
136                     mCallback.onVolumeForgotten((String) args.arg1);
137                     args.recycle();
138                     return true;
139                 case MSG_DISK_SCANNED:
140                     mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
141                     args.recycle();
142                     return true;
143                 case MSG_DISK_DESTROYED:
144                     mCallback.onDiskDestroyed((DiskInfo) args.arg1);
145                     args.recycle();
146                     return true;
147             }
148             args.recycle();
149             return false;
150         }
151 
152         @Override
onUsbMassStorageConnectionChanged(boolean connected)153         public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
154             // Ignored
155         }
156 
157         @Override
onStorageStateChanged(String path, String oldState, String newState)158         public void onStorageStateChanged(String path, String oldState, String newState) {
159             final SomeArgs args = SomeArgs.obtain();
160             args.arg1 = path;
161             args.arg2 = oldState;
162             args.arg3 = newState;
163             mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
164         }
165 
166         @Override
onVolumeStateChanged(VolumeInfo vol, int oldState, int newState)167         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
168             final SomeArgs args = SomeArgs.obtain();
169             args.arg1 = vol;
170             args.argi2 = oldState;
171             args.argi3 = newState;
172             mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
173         }
174 
175         @Override
onVolumeRecordChanged(VolumeRecord rec)176         public void onVolumeRecordChanged(VolumeRecord rec) {
177             final SomeArgs args = SomeArgs.obtain();
178             args.arg1 = rec;
179             mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
180         }
181 
182         @Override
onVolumeForgotten(String fsUuid)183         public void onVolumeForgotten(String fsUuid) {
184             final SomeArgs args = SomeArgs.obtain();
185             args.arg1 = fsUuid;
186             mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
187         }
188 
189         @Override
onDiskScanned(DiskInfo disk, int volumeCount)190         public void onDiskScanned(DiskInfo disk, int volumeCount) {
191             final SomeArgs args = SomeArgs.obtain();
192             args.arg1 = disk;
193             args.argi2 = volumeCount;
194             mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
195         }
196 
197         @Override
onDiskDestroyed(DiskInfo disk)198         public void onDiskDestroyed(DiskInfo disk) throws RemoteException {
199             final SomeArgs args = SomeArgs.obtain();
200             args.arg1 = disk;
201             mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget();
202         }
203     }
204 
205     /**
206      * Binder listener for OBB action results.
207      */
208     private final ObbActionListener mObbActionListener = new ObbActionListener();
209 
210     private class ObbActionListener extends IObbActionListener.Stub {
211         @SuppressWarnings("hiding")
212         private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
213 
214         @Override
onObbResult(String filename, int nonce, int status)215         public void onObbResult(String filename, int nonce, int status) {
216             final ObbListenerDelegate delegate;
217             synchronized (mListeners) {
218                 delegate = mListeners.get(nonce);
219                 if (delegate != null) {
220                     mListeners.remove(nonce);
221                 }
222             }
223 
224             if (delegate != null) {
225                 delegate.sendObbStateChanged(filename, status);
226             }
227         }
228 
addListener(OnObbStateChangeListener listener)229         public int addListener(OnObbStateChangeListener listener) {
230             final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
231 
232             synchronized (mListeners) {
233                 mListeners.put(delegate.nonce, delegate);
234             }
235 
236             return delegate.nonce;
237         }
238     }
239 
getNextNonce()240     private int getNextNonce() {
241         return mNextNonce.getAndIncrement();
242     }
243 
244     /**
245      * Private class containing sender and receiver code for StorageEvents.
246      */
247     private class ObbListenerDelegate {
248         private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
249         private final Handler mHandler;
250 
251         private final int nonce;
252 
ObbListenerDelegate(OnObbStateChangeListener listener)253         ObbListenerDelegate(OnObbStateChangeListener listener) {
254             nonce = getNextNonce();
255             mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
256             mHandler = new Handler(mLooper) {
257                 @Override
258                 public void handleMessage(Message msg) {
259                     final OnObbStateChangeListener changeListener = getListener();
260                     if (changeListener == null) {
261                         return;
262                     }
263 
264                     changeListener.onObbStateChange((String) msg.obj, msg.arg1);
265                 }
266             };
267         }
268 
getListener()269         OnObbStateChangeListener getListener() {
270             if (mObbEventListenerRef == null) {
271                 return null;
272             }
273             return mObbEventListenerRef.get();
274         }
275 
sendObbStateChanged(String path, int state)276         void sendObbStateChanged(String path, int state) {
277             mHandler.obtainMessage(0, state, 0, path).sendToTarget();
278         }
279     }
280 
281     /** {@hide} */
282     @Deprecated
from(Context context)283     public static StorageManager from(Context context) {
284         return context.getSystemService(StorageManager.class);
285     }
286 
287     /**
288      * Constructs a StorageManager object through which an application can
289      * can communicate with the systems mount service.
290      *
291      * @param tgtLooper The {@link android.os.Looper} which events will be received on.
292      *
293      * <p>Applications can get instance of this class by calling
294      * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
295      * of {@link android.content.Context#STORAGE_SERVICE}.
296      *
297      * @hide
298      */
StorageManager(Context context, Looper looper)299     public StorageManager(Context context, Looper looper) {
300         mContext = context;
301         mResolver = context.getContentResolver();
302         mLooper = looper;
303         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
304         if (mMountService == null) {
305             throw new IllegalStateException("Failed to find running mount service");
306         }
307     }
308 
309     /**
310      * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
311      *
312      * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
313      *
314      * @hide
315      */
registerListener(StorageEventListener listener)316     public void registerListener(StorageEventListener listener) {
317         synchronized (mDelegates) {
318             final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener,
319                     mLooper);
320             try {
321                 mMountService.registerListener(delegate);
322             } catch (RemoteException e) {
323                 throw e.rethrowAsRuntimeException();
324             }
325             mDelegates.add(delegate);
326         }
327     }
328 
329     /**
330      * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
331      *
332      * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
333      *
334      * @hide
335      */
unregisterListener(StorageEventListener listener)336     public void unregisterListener(StorageEventListener listener) {
337         synchronized (mDelegates) {
338             for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
339                 final StorageEventListenerDelegate delegate = i.next();
340                 if (delegate.mCallback == listener) {
341                     try {
342                         mMountService.unregisterListener(delegate);
343                     } catch (RemoteException e) {
344                         throw e.rethrowAsRuntimeException();
345                     }
346                     i.remove();
347                 }
348             }
349         }
350     }
351 
352     /**
353      * Enables USB Mass Storage (UMS) on the device.
354      *
355      * @hide
356      */
357     @Deprecated
enableUsbMassStorage()358     public void enableUsbMassStorage() {
359     }
360 
361     /**
362      * Disables USB Mass Storage (UMS) on the device.
363      *
364      * @hide
365      */
366     @Deprecated
disableUsbMassStorage()367     public void disableUsbMassStorage() {
368     }
369 
370     /**
371      * Query if a USB Mass Storage (UMS) host is connected.
372      * @return true if UMS host is connected.
373      *
374      * @hide
375      */
376     @Deprecated
isUsbMassStorageConnected()377     public boolean isUsbMassStorageConnected() {
378         return false;
379     }
380 
381     /**
382      * Query if a USB Mass Storage (UMS) is enabled on the device.
383      * @return true if UMS host is enabled.
384      *
385      * @hide
386      */
387     @Deprecated
isUsbMassStorageEnabled()388     public boolean isUsbMassStorageEnabled() {
389         return false;
390     }
391 
392     /**
393      * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
394      * specified, it is supplied to the mounting process to be used in any
395      * encryption used in the OBB.
396      * <p>
397      * The OBB will remain mounted for as long as the StorageManager reference
398      * is held by the application. As soon as this reference is lost, the OBBs
399      * in use will be unmounted. The {@link OnObbStateChangeListener} registered
400      * with this call will receive the success or failure of this operation.
401      * <p>
402      * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
403      * file matches a package ID that is owned by the calling program's UID.
404      * That is, shared UID applications can attempt to mount any other
405      * application's OBB that shares its UID.
406      *
407      * @param rawPath the path to the OBB file
408      * @param key secret used to encrypt the OBB; may be <code>null</code> if no
409      *            encryption was used on the OBB.
410      * @param listener will receive the success or failure of the operation
411      * @return whether the mount call was successfully queued or not
412      */
mountObb(String rawPath, String key, OnObbStateChangeListener listener)413     public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
414         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
415         Preconditions.checkNotNull(listener, "listener cannot be null");
416 
417         try {
418             final String canonicalPath = new File(rawPath).getCanonicalPath();
419             final int nonce = mObbActionListener.addListener(listener);
420             mMountService.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce);
421             return true;
422         } catch (IOException e) {
423             throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e);
424         } catch (RemoteException e) {
425             Log.e(TAG, "Failed to mount OBB", e);
426         }
427 
428         return false;
429     }
430 
431     /**
432      * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
433      * <code>force</code> flag is true, it will kill any application needed to
434      * unmount the given OBB (even the calling application).
435      * <p>
436      * The {@link OnObbStateChangeListener} registered with this call will
437      * receive the success or failure of this operation.
438      * <p>
439      * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
440      * file matches a package ID that is owned by the calling program's UID.
441      * That is, shared UID applications can obtain access to any other
442      * application's OBB that shares its UID.
443      * <p>
444      *
445      * @param rawPath path to the OBB file
446      * @param force whether to kill any programs using this in order to unmount
447      *            it
448      * @param listener will receive the success or failure of the operation
449      * @return whether the unmount call was successfully queued or not
450      */
unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener)451     public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
452         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
453         Preconditions.checkNotNull(listener, "listener cannot be null");
454 
455         try {
456             final int nonce = mObbActionListener.addListener(listener);
457             mMountService.unmountObb(rawPath, force, mObbActionListener, nonce);
458             return true;
459         } catch (RemoteException e) {
460             Log.e(TAG, "Failed to mount OBB", e);
461         }
462 
463         return false;
464     }
465 
466     /**
467      * Check whether an Opaque Binary Blob (OBB) is mounted or not.
468      *
469      * @param rawPath path to OBB image
470      * @return true if OBB is mounted; false if not mounted or on error
471      */
isObbMounted(String rawPath)472     public boolean isObbMounted(String rawPath) {
473         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
474 
475         try {
476             return mMountService.isObbMounted(rawPath);
477         } catch (RemoteException e) {
478             Log.e(TAG, "Failed to check if OBB is mounted", e);
479         }
480 
481         return false;
482     }
483 
484     /**
485      * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
486      * give you the path to where you can obtain access to the internals of the
487      * OBB.
488      *
489      * @param rawPath path to OBB image
490      * @return absolute path to mounted OBB image data or <code>null</code> if
491      *         not mounted or exception encountered trying to read status
492      */
getMountedObbPath(String rawPath)493     public String getMountedObbPath(String rawPath) {
494         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
495 
496         try {
497             return mMountService.getMountedObbPath(rawPath);
498         } catch (RemoteException e) {
499             Log.e(TAG, "Failed to find mounted path for OBB", e);
500         }
501 
502         return null;
503     }
504 
505     /** {@hide} */
getDisks()506     public @NonNull List<DiskInfo> getDisks() {
507         try {
508             return Arrays.asList(mMountService.getDisks());
509         } catch (RemoteException e) {
510             throw e.rethrowAsRuntimeException();
511         }
512     }
513 
514     /** {@hide} */
findDiskById(String id)515     public @Nullable DiskInfo findDiskById(String id) {
516         Preconditions.checkNotNull(id);
517         // TODO; go directly to service to make this faster
518         for (DiskInfo disk : getDisks()) {
519             if (Objects.equals(disk.id, id)) {
520                 return disk;
521             }
522         }
523         return null;
524     }
525 
526     /** {@hide} */
findVolumeById(String id)527     public @Nullable VolumeInfo findVolumeById(String id) {
528         Preconditions.checkNotNull(id);
529         // TODO; go directly to service to make this faster
530         for (VolumeInfo vol : getVolumes()) {
531             if (Objects.equals(vol.id, id)) {
532                 return vol;
533             }
534         }
535         return null;
536     }
537 
538     /** {@hide} */
findVolumeByUuid(String fsUuid)539     public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
540         Preconditions.checkNotNull(fsUuid);
541         // TODO; go directly to service to make this faster
542         for (VolumeInfo vol : getVolumes()) {
543             if (Objects.equals(vol.fsUuid, fsUuid)) {
544                 return vol;
545             }
546         }
547         return null;
548     }
549 
550     /** {@hide} */
findRecordByUuid(String fsUuid)551     public @Nullable VolumeRecord findRecordByUuid(String fsUuid) {
552         Preconditions.checkNotNull(fsUuid);
553         // TODO; go directly to service to make this faster
554         for (VolumeRecord rec : getVolumeRecords()) {
555             if (Objects.equals(rec.fsUuid, fsUuid)) {
556                 return rec;
557             }
558         }
559         return null;
560     }
561 
562     /** {@hide} */
findPrivateForEmulated(VolumeInfo emulatedVol)563     public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
564         if (emulatedVol != null) {
565             return findVolumeById(emulatedVol.getId().replace("emulated", "private"));
566         } else {
567             return null;
568         }
569     }
570 
571     /** {@hide} */
findEmulatedForPrivate(VolumeInfo privateVol)572     public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
573         if (privateVol != null) {
574             return findVolumeById(privateVol.getId().replace("private", "emulated"));
575         } else {
576             return null;
577         }
578     }
579 
580     /** {@hide} */
findVolumeByQualifiedUuid(String volumeUuid)581     public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
582         if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
583             return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
584         } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
585             return getPrimaryPhysicalVolume();
586         } else {
587             return findVolumeByUuid(volumeUuid);
588         }
589     }
590 
591     /** {@hide} */
getVolumes()592     public @NonNull List<VolumeInfo> getVolumes() {
593         try {
594             return Arrays.asList(mMountService.getVolumes(0));
595         } catch (RemoteException e) {
596             throw e.rethrowAsRuntimeException();
597         }
598     }
599 
600     /** {@hide} */
getWritablePrivateVolumes()601     public @NonNull List<VolumeInfo> getWritablePrivateVolumes() {
602         try {
603             final ArrayList<VolumeInfo> res = new ArrayList<>();
604             for (VolumeInfo vol : mMountService.getVolumes(0)) {
605                 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
606                     res.add(vol);
607                 }
608             }
609             return res;
610         } catch (RemoteException e) {
611             throw e.rethrowAsRuntimeException();
612         }
613     }
614 
615     /** {@hide} */
getVolumeRecords()616     public @NonNull List<VolumeRecord> getVolumeRecords() {
617         try {
618             return Arrays.asList(mMountService.getVolumeRecords(0));
619         } catch (RemoteException e) {
620             throw e.rethrowAsRuntimeException();
621         }
622     }
623 
624     /** {@hide} */
getBestVolumeDescription(VolumeInfo vol)625     public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
626         if (vol == null) return null;
627 
628         // Nickname always takes precedence when defined
629         if (!TextUtils.isEmpty(vol.fsUuid)) {
630             final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
631             if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
632                 return rec.nickname;
633             }
634         }
635 
636         if (!TextUtils.isEmpty(vol.getDescription())) {
637             return vol.getDescription();
638         }
639 
640         if (vol.disk != null) {
641             return vol.disk.getDescription();
642         }
643 
644         return null;
645     }
646 
647     /** {@hide} */
getPrimaryPhysicalVolume()648     public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
649         final List<VolumeInfo> vols = getVolumes();
650         for (VolumeInfo vol : vols) {
651             if (vol.isPrimaryPhysical()) {
652                 return vol;
653             }
654         }
655         return null;
656     }
657 
658     /** {@hide} */
mount(String volId)659     public void mount(String volId) {
660         try {
661             mMountService.mount(volId);
662         } catch (RemoteException e) {
663             throw e.rethrowAsRuntimeException();
664         }
665     }
666 
667     /** {@hide} */
unmount(String volId)668     public void unmount(String volId) {
669         try {
670             mMountService.unmount(volId);
671         } catch (RemoteException e) {
672             throw e.rethrowAsRuntimeException();
673         }
674     }
675 
676     /** {@hide} */
format(String volId)677     public void format(String volId) {
678         try {
679             mMountService.format(volId);
680         } catch (RemoteException e) {
681             throw e.rethrowAsRuntimeException();
682         }
683     }
684 
685     /** {@hide} */
benchmark(String volId)686     public long benchmark(String volId) {
687         try {
688             return mMountService.benchmark(volId);
689         } catch (RemoteException e) {
690             throw e.rethrowAsRuntimeException();
691         }
692     }
693 
694     /** {@hide} */
partitionPublic(String diskId)695     public void partitionPublic(String diskId) {
696         try {
697             mMountService.partitionPublic(diskId);
698         } catch (RemoteException e) {
699             throw e.rethrowAsRuntimeException();
700         }
701     }
702 
703     /** {@hide} */
partitionPrivate(String diskId)704     public void partitionPrivate(String diskId) {
705         try {
706             mMountService.partitionPrivate(diskId);
707         } catch (RemoteException e) {
708             throw e.rethrowAsRuntimeException();
709         }
710     }
711 
712     /** {@hide} */
partitionMixed(String diskId, int ratio)713     public void partitionMixed(String diskId, int ratio) {
714         try {
715             mMountService.partitionMixed(diskId, ratio);
716         } catch (RemoteException e) {
717             throw e.rethrowAsRuntimeException();
718         }
719     }
720 
721     /** {@hide} */
wipeAdoptableDisks()722     public void wipeAdoptableDisks() {
723         // We only wipe devices in "adoptable" locations, which are in a
724         // long-term stable slot/location on the device, where apps have a
725         // reasonable chance of storing sensitive data. (Apps need to go through
726         // SAF to write to transient volumes.)
727         final List<DiskInfo> disks = getDisks();
728         for (DiskInfo disk : disks) {
729             final String diskId = disk.getId();
730             if (disk.isAdoptable()) {
731                 Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
732                 try {
733                     // TODO: switch to explicit wipe command when we have it,
734                     // for now rely on the fact that vfat format does a wipe
735                     mMountService.partitionPublic(diskId);
736                 } catch (Exception e) {
737                     Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
738                 }
739             } else {
740                 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
741             }
742         }
743     }
744 
745     /** {@hide} */
setVolumeNickname(String fsUuid, String nickname)746     public void setVolumeNickname(String fsUuid, String nickname) {
747         try {
748             mMountService.setVolumeNickname(fsUuid, nickname);
749         } catch (RemoteException e) {
750             throw e.rethrowAsRuntimeException();
751         }
752     }
753 
754     /** {@hide} */
setVolumeInited(String fsUuid, boolean inited)755     public void setVolumeInited(String fsUuid, boolean inited) {
756         try {
757             mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0,
758                     VolumeRecord.USER_FLAG_INITED);
759         } catch (RemoteException e) {
760             throw e.rethrowAsRuntimeException();
761         }
762     }
763 
764     /** {@hide} */
setVolumeSnoozed(String fsUuid, boolean snoozed)765     public void setVolumeSnoozed(String fsUuid, boolean snoozed) {
766         try {
767             mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0,
768                     VolumeRecord.USER_FLAG_SNOOZED);
769         } catch (RemoteException e) {
770             throw e.rethrowAsRuntimeException();
771         }
772     }
773 
774     /** {@hide} */
forgetVolume(String fsUuid)775     public void forgetVolume(String fsUuid) {
776         try {
777             mMountService.forgetVolume(fsUuid);
778         } catch (RemoteException e) {
779             throw e.rethrowAsRuntimeException();
780         }
781     }
782 
783     /**
784      * This is not the API you're looking for.
785      *
786      * @see PackageManager#getPrimaryStorageCurrentVolume()
787      * @hide
788      */
getPrimaryStorageUuid()789     public String getPrimaryStorageUuid() {
790         try {
791             return mMountService.getPrimaryStorageUuid();
792         } catch (RemoteException e) {
793             throw e.rethrowAsRuntimeException();
794         }
795     }
796 
797     /**
798      * This is not the API you're looking for.
799      *
800      * @see PackageManager#movePrimaryStorage(VolumeInfo)
801      * @hide
802      */
setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)803     public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
804         try {
805             mMountService.setPrimaryStorageUuid(volumeUuid, callback);
806         } catch (RemoteException e) {
807             throw e.rethrowAsRuntimeException();
808         }
809     }
810 
811     /** {@hide} */
getStorageVolume(File file)812     public @Nullable StorageVolume getStorageVolume(File file) {
813         return getStorageVolume(getVolumeList(), file);
814     }
815 
816     /** {@hide} */
getStorageVolume(File file, int userId)817     public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
818         return getStorageVolume(getVolumeList(userId, 0), file);
819     }
820 
821     /** {@hide} */
getStorageVolume(StorageVolume[] volumes, File file)822     private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
823         try {
824             file = file.getCanonicalFile();
825         } catch (IOException ignored) {
826             return null;
827         }
828         for (StorageVolume volume : volumes) {
829             File volumeFile = volume.getPathFile();
830             try {
831                 volumeFile = volumeFile.getCanonicalFile();
832             } catch (IOException ignored) {
833                 continue;
834             }
835             if (FileUtils.contains(volumeFile, file)) {
836                 return volume;
837             }
838         }
839         return null;
840     }
841 
842     /**
843      * Gets the state of a volume via its mountpoint.
844      * @hide
845      */
846     @Deprecated
getVolumeState(String mountPoint)847     public @NonNull String getVolumeState(String mountPoint) {
848         final StorageVolume vol = getStorageVolume(new File(mountPoint));
849         if (vol != null) {
850             return vol.getState();
851         } else {
852             return Environment.MEDIA_UNKNOWN;
853         }
854     }
855 
856     /** {@hide} */
getVolumeList()857     public @NonNull StorageVolume[] getVolumeList() {
858         return getVolumeList(mContext.getUserId(), 0);
859     }
860 
861     /** {@hide} */
getVolumeList(int userId, int flags)862     public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
863         final IMountService mountService = IMountService.Stub.asInterface(
864                 ServiceManager.getService("mount"));
865         try {
866             String packageName = ActivityThread.currentOpPackageName();
867             if (packageName == null) {
868                 // Package name can be null if the activity thread is running but the app
869                 // hasn't bound yet. In this case we fall back to the first package in the
870                 // current UID. This works for runtime permissions as permission state is
871                 // per UID and permission realted app ops are updated for all UID packages.
872                 String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
873                         android.os.Process.myUid());
874                 if (packageNames == null || packageNames.length <= 0) {
875                     return new StorageVolume[0];
876                 }
877                 packageName = packageNames[0];
878             }
879             final int uid = ActivityThread.getPackageManager().getPackageUid(packageName, userId);
880             if (uid <= 0) {
881                 return new StorageVolume[0];
882             }
883             return mountService.getVolumeList(uid, packageName, flags);
884         } catch (RemoteException e) {
885             throw e.rethrowAsRuntimeException();
886         }
887     }
888 
889     /**
890      * Returns list of paths for all mountable volumes.
891      * @hide
892      */
893     @Deprecated
getVolumePaths()894     public @NonNull String[] getVolumePaths() {
895         StorageVolume[] volumes = getVolumeList();
896         int count = volumes.length;
897         String[] paths = new String[count];
898         for (int i = 0; i < count; i++) {
899             paths[i] = volumes[i].getPath();
900         }
901         return paths;
902     }
903 
904     /** {@hide} */
getPrimaryVolume()905     public @NonNull StorageVolume getPrimaryVolume() {
906         return getPrimaryVolume(getVolumeList());
907     }
908 
909     /** {@hide} */
getPrimaryVolume(StorageVolume[] volumes)910     public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
911         for (StorageVolume volume : volumes) {
912             if (volume.isPrimary()) {
913                 return volume;
914             }
915         }
916         throw new IllegalStateException("Missing primary storage");
917     }
918 
919     /** {@hide} */
920     private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
921     private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
922     private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
923 
924     /**
925      * Return the number of available bytes until the given path is considered
926      * running low on storage.
927      *
928      * @hide
929      */
getStorageBytesUntilLow(File path)930     public long getStorageBytesUntilLow(File path) {
931         return path.getUsableSpace() - getStorageFullBytes(path);
932     }
933 
934     /**
935      * Return the number of available bytes at which the given path is
936      * considered running low on storage.
937      *
938      * @hide
939      */
getStorageLowBytes(File path)940     public long getStorageLowBytes(File path) {
941         final long lowPercent = Settings.Global.getInt(mResolver,
942                 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
943         final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
944 
945         final long maxLowBytes = Settings.Global.getLong(mResolver,
946                 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
947 
948         return Math.min(lowBytes, maxLowBytes);
949     }
950 
951     /**
952      * Return the number of available bytes at which the given path is
953      * considered full.
954      *
955      * @hide
956      */
getStorageFullBytes(File path)957     public long getStorageFullBytes(File path) {
958         return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
959                 DEFAULT_FULL_THRESHOLD_BYTES);
960     }
961 
962     /** {@hide} */
maybeTranslateEmulatedPathToInternal(File path)963     public static File maybeTranslateEmulatedPathToInternal(File path) {
964         final IMountService mountService = IMountService.Stub.asInterface(
965                 ServiceManager.getService("mount"));
966         try {
967             final VolumeInfo[] vols = mountService.getVolumes(0);
968             for (VolumeInfo vol : vols) {
969                 if ((vol.getType() == VolumeInfo.TYPE_EMULATED
970                         || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
971                     final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
972                             vol.getInternalPath(), path);
973                     if (internalPath != null && internalPath.exists()) {
974                         return internalPath;
975                     }
976                 }
977             }
978         } catch (RemoteException ignored) {
979         }
980         return path;
981     }
982 
983     /// Consts to match the password types in cryptfs.h
984     /** @hide */
985     public static final int CRYPT_TYPE_PASSWORD = 0;
986     /** @hide */
987     public static final int CRYPT_TYPE_DEFAULT = 1;
988     /** @hide */
989     public static final int CRYPT_TYPE_PATTERN = 2;
990     /** @hide */
991     public static final int CRYPT_TYPE_PIN = 3;
992 
993     // Constants for the data available via MountService.getField.
994     /** @hide */
995     public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
996     /** @hide */
997     public static final String OWNER_INFO_KEY = "OwnerInfo";
998     /** @hide */
999     public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
1000     /** @hide */
1001     public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible";
1002 }
1003