1

I have read many posts on this topic, but I have not found a solution yet. In my application I need to unmount the USB DRIVE after copying files from USB DRIVE to the tablet, so I can safely remove it without using the Settings menu.

Right now I am using this method:

Utility.copyDirectory(file,new File(_CURR_FOLDER));
Process su;
su = Runtime.getRuntime().exec("/system/bin/su");
String cmd = "umount" +
             " " + SDPath + "\n" + "exit\n";
             su.getOutputStream().write(cmd.getBytes());

What I get in the storage settings is:

Total space 0.0 Available 0.0

but SD is still mounted.

Thank you in advance for your help.

First Edit:

Someone know how use IMountService? I read about it, and maybe is the right way to solve the unmount of USB DRIVE, but after adding classes-full-debug.jar my project is no longer compiled

matzrm
  • 77
  • 3
  • 12

3 Answers3

3

this cannot be done. Many users store all their content like songs, videos, photos there. it was a good security decision to not allow apps to unmount SD card, but use them to store data.

but u can send user to setting and user do it , not by code

Intent i = new Intent(android.provider.Settings.ACTION_MEMORY_CARD_SETTINGS);
startActivity(i);

which brings up that screen...

enter image description here

i think this will work

OneWorld
  • 17,512
  • 21
  • 86
  • 136
Saeed-rz
  • 1,435
  • 1
  • 20
  • 38
  • In my case, it is not SD, but a USB DRIVE used to auto-update the configuration of my application. There isn't an API to do unmount? @Leon_SFS – matzrm Jul 16 '14 at 14:50
  • @user2704214 look at this : http://developer.android.com/guide/topics/connectivity/usb/accessory.html maybe helpful and are u use right permission for unMount? – Saeed-rz Jul 16 '14 at 15:03
  • Note the question makes it clear that the poster is not constrained by the Android security model. – Chris Stratton Jul 16 '14 at 16:44
  • I had already read the documentation, but does not say anything concerning the unmount of USB DRIVE. I tried to run the command from terminal `unmount /mnt/usb_storage` but the result is the same. – matzrm Jul 17 '14 at 07:09
  • I read this thread: [link](http://stackoverflow.com/q/24647143/2704214), but to use IMountService I have to include _classes-full-debug.jar_, but it prevents Android Studio to successfully compile the project. Someone can help me? – matzrm Jul 17 '14 at 13:09
2

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!

Mosomate
  • 31
  • 2
  • I'm getting "java.lang.SecurityException: android.permission.MOUNT_UNMOUNT_FILESYSTEMS: Neither user 10127 nor current process has android.permission.MOUNT_UNMOUNT_FILESYSTEMS.". Also when i add "" into manifest, doesn't work. It says "Permission is only granted to system apps". How can i solve it? What should be the Target SDK Version? – Talha Ç Jul 18 '23 at 06:18
0

This is what i had to do on Android 6.0.1, hopefully it will be the same for 4.2.

I had to add the library SDK/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes-full-debug.jar to my app after compiling android. (I just downloaded the lollipop version from here and thankfully it worked for marshmallow as well)

Android studio will complain about an out of memory error, this is fixed by adding the below code to the build.gradle inside the android section.

dexOptions {
    javaMaxHeapSize "4g"
}

You also need to add multidex support by adding the below lines in build.gradle inside the defaultConfig section.

multiDexEnabled true

And adding the below line inside the dependencies section.

compile 'com.android.support:multidex:1.0.0'

And adding the below line in the application section inside the manifest.

android:name="android.support.multidex.MultiDexApplication"

And your apk has to be in the /system/priv-app directory and have the below permission.

<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />

and then the below code should work.

private void unmount() {
    IMountService mountService = getMountService();
    try {
        if (mountService != null) {
            mountService.unmountVolume("/mnt/media_rw/8842-89A4", true, false);
            Log.e(TAG,"Unmounted");
        } else {
            Log.e(TAG, "Mount service is null, can't unmount");
        }
    } catch (RemoteException ex) {
        // Not much can be done
    }
}

private synchronized IMountService getMountService() {
    if (mMountService == null) {
        IBinder service = ServiceManager.getService("mount");
        if (service != null) {
            mMountService = IMountService.Stub.asInterface(service);
        } else {
            Log.e(TAG, "Can't get mount service");
        }
    }
    return mMountService;
}
Habbert
  • 221
  • 1
  • 10