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

11,413

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

Share:
11,413
cgr
Author by

cgr

Android developer at Uptodown.com

Updated on June 04, 2022

Comments

  • cgr
    cgr almost 2 years

    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?

  • cgr
    cgr over 8 years
    getExternalFilesDirs() returns "/storage/emulated/0/Android/data/packagename/files" but this is not the external sdcard. Solved with Storage Access Framework, here a good explanation stackoverflow.com/questions/26744842/…
  • somesh
    somesh over 8 years
    Are you sure you used getExternalFilesDirs and not getExternalFilesDir? Dir will return only the emulated path. Dirs will return emulated path and sdcard path.
  • cgr
    cgr over 7 years
    Environment.getExternalStorageDirectory.getPath() returns "/storage/emulated/0/", this is not the external sdcard
  • Wraithious
    Wraithious over 7 years
    correct that is why i did not use that in my above code, first i got the Environment.getExternalStorageDirectory() and then put the string together with File folder = new File(Root.getAbsolutePath() , not getPath, I tested it on api 4.1 and 6, it worked and made the folders that the user can easily find whereas all other methods I've seen posted on here didn't work (for me at least)
  • Deep Dave
    Deep Dave over 7 years
    Any Solutions? @cgr
  • cgr
    cgr over 7 years
    yes @DeepDave, using Storage Access Framework, see this answer stackoverflow.com/questions/26744842/…