I used these snippet of codes in a system application which has the right to do anything. I'm not sure this will work with regular apps, even it has all the storage permissions.
I know it's 7 years late, but maybe helps for others. The StorageManager class has a hidden function called unmount. Because it's not public and blocks the caller thread, use reflection to access it and put it in an AsyncTask:
public static void unmountStorage(Context context, String volId) {
new AsyncTask<Void, Void, Exception>() {
@Override
protected void onPostExecute(Exception e) {
if (e == null) {
Log.i("storageman", "unmounting " + volId + " success!");
}
else {
Log.e("storageman", "unmounting", e);
}
}
@Override
protected Exception doInBackground(Void... params) {
try {
StorageManager storageManager = context.getSystemService(StorageManager.class);
Method unmountMethod = StorageManager.class.getMethod("unmount", String.class);
unmountMethod.setAccessible(true);
unmountMethod.invoke(storageManager, volId);
return null;
}
catch (Exception ex) {
return ex;
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
The volId
parameter is a bit tricky to get. First, list the available volumes. StorageManager also has a hidden method to do this called getVolumes. Then you can iterate through them and get their attributes like volume ID. This method gives you back the first "external" storage:
public static ExternalStorage getExternalStorage(Context context) {
try {
StorageManager storageManager = context.getSystemService(StorageManager.class);
Method getVolumes = StorageManager.class.getMethod("getVolumes");
getVolumes.setAccessible(true);
List<Object> volumes = (List<Object>) getVolumes.invoke(storageManager);
for (Object volume : volumes) {
Class<?> volumeInfoClass = Class.forName("android.os.storage.VolumeInfo");
Field idField = volumeInfoClass.getField("id");
idField.setAccessible(true);
String volumeId = (String) idField.get(volume);
Field nameField = volumeInfoClass.getField("fsLabel");
nameField.setAccessible(true);
String volumeName = (String) nameField.get(volume);
Field stateField = volumeInfoClass.getField("state");
stateField.setAccessible(true);
Integer volumeState = (Integer) stateField.get(volume);
Field pathField = volumeInfoClass.getField("path");
pathField.setAccessible(true);
String volumePath = (String) pathField.get(volume);
if (volumeId.startsWith("public") && (volumeState == 2 || volumeState == 3 || volumeState == 5)) {
return new ExternalStorage(volumeId, volumeName, new File(volumePath));
}
}
}
catch (Exception ex) {
Log.e("storagehelper", "getExternalStorage(Context context)", ex);
}
return null;
}
I think this line requires further explanation:
if (volumeId.startsWith("public") && (volumeState == 2 || volumeState == 3 || volumeState == 5)) {
As I experienced during testing, the volume IDs of external drives starts with "public", that's why I filtered the volumes for it. The constants 2, 3 and 5 are indicates that the volume is mounted. Check out the constants HERE.
ExternalStorage is a simple data class I wrote to hold the relevant infos together.
Good luck!