Search code examples
androidandroid-5.0-lollipopandroid-sdcard

Can't create a directory in external sd card (Lollipop)


I have no problem with internal storage but I want to create a directory on the external SD card, with versions prior to KitKat, File.mkdirs() works.

There is part of my code:

//external sd card
DocumentFile.fromFile(new File("/storage/sdcard1/")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/")).canRead(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/")).canWrite(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/test")).exists(); //returns false
DocumentFile.fromFile(new File("/storage/sdcard1/")).createDirectory("test"); //returns null

//internal storage
DocumentFile.fromFile(new File("/storage/emulated/0/")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/emulated/0/test")).exists(); //returns false
DocumentFile.fromFile(new File("/storage/emulated/0/")).createDirectory("test"); //it works
DocumentFile.fromFile(new File("/storage/emulated/0/test")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/emulated/0/test")).createFile("text", "file.txt"); //it works

//external sd card
(new File("/storage/sdcard1/test2/subfolder")).exists(); //returns false
(new File("/storage/sdcard1/test2/subfolder")).mkdirs(); //returns false

//internal storage
(new File("/storage/emulated/0/test2/subfolder")).exists(); //returns false
(new File("/storage/emulated/0/test2/subfolder")).mkdirs(); //returns true

In AndroidManifest.xml:

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

Testing with an BQ Aquaris e4, Android Lollipop 5.0 and a 1GB micro SD card.

UPDATE: This is how I get the volume list:

private static ArrayList<StorageInfo> listAvaliableStorage(Context context) {
        ArrayList<StorageInfo> storagges = new ArrayList<>();
        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        try {
            Class<?>[] paramClasses = {};
            Method getVolumeList = StorageManager.class.getMethod("getVolumeList", paramClasses);
            getVolumeList.setAccessible(true);
            Object[] params = {};
            Object[] invokes = (Object[]) getVolumeList.invoke(storageManager, params);
            if (invokes != null) {
                StorageInfo info;
                for (Object obj : invokes) {
                    Method getPath = obj.getClass().getMethod("getPath");
                    String path = (String) getPath.invoke(obj);
                    info = new StorageInfo(path);
                    File file = new File(info.getPath());
                    if ((file.exists()) && (file.isDirectory())
                        //&& (file.canWrite())
                            ) {
                        info.setTotalStorage(file.getTotalSpace());
                        info.setFreeStorage(file.getUsableSpace());
                        Method isRemovable = obj.getClass().getMethod("isRemovable");
                        String state;
                        try {
                            Method getVolumeState = StorageManager.class.getMethod("getVolumeState", String.class);
                            state = (String) getVolumeState.invoke(storageManager, info.getPath());
                            info.setState(state);
                            info.setRemovable((Boolean) isRemovable.invoke(obj));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                        storagges.add(info);
                    }
                }
            }
        } catch (NoSuchMethodException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        storagges.trimToSize();

        return storagges;
    }

The hard coded paths is only for this example

UPDATE 2: I've created the directory: "test" in SD Card with a file explorer. When I execute this code:

File file = new File("/storage/sdcard1/test/", "file.txt");
fileOutput = new FileOutputStream(file);

The second line throws FileNotFoundException: /storage/sdcard1/test/file.txt: open failed: EACCES (Permission denied)

What am I doing wrong?


Solution

  • That is due to the design of Android Lollipop. Environment.getExternalStorageDirectory() will return only the path of emulated external storage. Android does not expose any API that gives you the path of the sdcard.

    Also, in Lollipop, apps cannot write to location outside of their own directory in the sdcard, unless user permission is obtained through Storage Access Framework.

    Try context.getExternalFilesDirs(). That gives you a list of paths where your app can write data to. One of it will be in the sdcard and will go like /storage/sdcardname/Android/data/yourAppName/files