Как программно размонтировать USB DRIVE в Android 4.2

Я прочитал много сообщений на эту тему, но пока не нашел решения. В моем приложении мне нужно отключить USB DRIVE после копирования файлов с USB DRIVE на планшет, чтобы я мог безопасно удалить его, не используя меню "Настройки".

Прямо сейчас я использую этот метод:

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());

Что я получаю в настройках хранения:

Общая площадь 0.0 Доступно 0.0

но SD все еще установлен.

Спасибо заранее за вашу помощь.

Первое редактирование:

Кто-то знает, как использовать IMountService? Я читал об этом, и, возможно, это правильный способ решить размонтирование USB DRIVE, но после добавления classes-full-debug.jar мой проект больше не компилируется

3 ответа

Я использовал эти фрагменты кода в системном приложении, которое имеет право делать что угодно. Я не уверен, что это будет работать с обычными приложениями, даже если у него есть все права доступа к хранилищу.

Я знаю, что с опозданием на 7 лет, но, возможно, это поможет другим. Класс StorageManager имеет скрытую функцию, называемую unmount . Поскольку он не является общедоступным и блокирует поток вызывающего абонента, используйте отражение для доступа к нему и поместите его в 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параметр немного сложно получить. Сначала перечислите доступные тома. StorageManager также имеет скрытый метод для этого, который называется getVolumes . Затем вы можете просмотреть их и получить их атрибуты, такие как идентификатор тома. Этот метод возвращает вам первое «внешнее» хранилище:

      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;
}

Я думаю, что эта строка требует дополнительного пояснения:

      if (volumeId.startsWith("public") && (volumeState == 2 || volumeState == 3 || volumeState == 5)) {

Как я понял во время тестирования, идентификаторы томов внешних дисков начинаются с «общедоступных», поэтому я отфильтровал тома для него. Константы 2, 3 и 5 означают, что том смонтирован. Проверьте константы ЗДЕСЬ .

ExternalStorage — это простой класс данных, который я написал для хранения соответствующей информации.

Удачи!

Это не может быть сделано. Многие пользователи хранят весь свой контент, например, песни, видео, фотографии. это было хорошее решение по безопасности: не разрешать приложениям отключать SD-карту, а использовать их для хранения данных.

но вы можете отправить пользователя к настройке и пользователь сделает это, а не по коду

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

который поднимает этот экран...

я думаю это сработает

Это то, что я должен был сделать на Android 6.0.1, надеюсь, это будет то же самое для 4.2.

Мне пришлось добавить библиотеку SDK/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes-full-debug.jar в мое приложение после компиляции Android. (Я только что скачал версию леденца на палочке отсюда и, к счастью, она сработала и для зефира)

Студия Android будет жаловаться на ошибку нехватки памяти, это исправлено добавлением приведенного ниже кода в build.gradle внутри раздела android.

dexOptions {
    javaMaxHeapSize "4g"
}

Вам также необходимо добавить поддержку мультидекса, добавив строки ниже в build.gradle внутри секции defaultConfig.

multiDexEnabled true

И добавление строки ниже в разделе зависимостей.

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

И добавление строки ниже в разделе приложения внутри манифеста.

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

И ваш apk должен находиться в каталоге /system/priv-app и иметь разрешение ниже.

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

и тогда приведенный ниже код должен работать.

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;
}
Другие вопросы по тегам