Home | History | Annotate | Download | only in storage
      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 
    113         public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
    114             mCallback = callback;
    115             mHandler = new Handler(looper, this);
    116         }
    117 
    118         @Override
    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
    153         public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
    154             // Ignored
    155         }
    156 
    157         @Override
    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
    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
    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
    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
    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
    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
    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 
    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 
    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 
    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 
    269         OnObbStateChangeListener getListener() {
    270             if (mObbEventListenerRef == null) {
    271                 return null;
    272             }
    273             return mObbEventListenerRef.get();
    274         }
    275 
    276         void sendObbStateChanged(String path, int state) {
    277             mHandler.obtainMessage(0, state, 0, path).sendToTarget();
    278         }
    279     }
    280 
    281     /** {@hide} */
    282     @Deprecated
    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      */
    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      */
    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      */
    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
    358     public void enableUsbMassStorage() {
    359     }
    360 
    361     /**
    362      * Disables USB Mass Storage (UMS) on the device.
    363      *
    364      * @hide
    365      */
    366     @Deprecated
    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
    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
    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      */
    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      */
    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      */
    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      */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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} */
    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      */
    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      */
    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} */
    812     public @Nullable StorageVolume getStorageVolume(File file) {
    813         return getStorageVolume(getVolumeList(), file);
    814     }
    815 
    816     /** {@hide} */
    817     public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
    818         return getStorageVolume(getVolumeList(userId, 0), file);
    819     }
    820 
    821     /** {@hide} */
    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
    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} */
    857     public @NonNull StorageVolume[] getVolumeList() {
    858         return getVolumeList(mContext.getUserId(), 0);
    859     }
    860 
    861     /** {@hide} */
    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
    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} */
    905     public @NonNull StorageVolume getPrimaryVolume() {
    906         return getPrimaryVolume(getVolumeList());
    907     }
    908 
    909     /** {@hide} */
    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      */
    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      */
    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      */
    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} */
    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