简体   繁体   中英

How to access removable storage on Android devices?

I am looking for a way to detect and access removable sd cards on a variety of Android devices (Samsung, Motorola, LG, Sony, HTC).

I also need to be compatible with 2.2 so Environment.isExternalStorageRemovable() is unavailable for me.

Motorola has its own library, and for Samsung I can detect the existence of /external_sd/

I have no clue for the rest of them. For example, I have seen a /_ExternalSD/ on some LG's but it the directory remains even when the SD is removed.

A bonus question: will the ACTION_MEDIA_MOUNTED intent be broadcast for any of them

Any hint on this would be very helpful.

Here's a class I use to find all sdcards on a device; built in and removable. I've been using it on Ice Cream Sandwich, but it should work at 2x levels.

public class GetRemovableDevice {

private final static String TAG = "GetRemoveableDevice";

public GetRemovableDevice() {
}

public static String[] getDirectories() {
    MyLog.d(TAG, "getStorageDirectories");
    File tempFile;
    String[] directories = null;
    String[] splits;
    ArrayList<String> arrayList = new ArrayList<String>();
    BufferedReader bufferedReader = null;
    String lineRead;

    try {
        arrayList.clear(); // redundant, but what the hey
        bufferedReader = new BufferedReader(new FileReader("/proc/mounts"));

        while ((lineRead = bufferedReader.readLine()) != null) {
            MyLog.d(TAG, "lineRead: " + lineRead);
            splits = lineRead.split(" ");

            // System external storage
            if (splits[1].equals(Environment.getExternalStorageDirectory()
                    .getPath())) {
                arrayList.add(splits[1]);
                MyLog.d(TAG, "gesd split 1: " + splits[1]);
                continue;
            }

            // skip if not external storage device
            if (!splits[0].contains("/dev/block/")) {
                continue;
            }

            // skip if mtdblock device

            if (splits[0].contains("/dev/block/mtdblock")) {
                continue;
            }

            // skip if not in /mnt node

            if (!splits[1].contains("/mnt")) {
                continue;
            }

            // skip these names

            if (splits[1].contains("/secure")) {
                continue;
            }

            if (splits[1].contains("/mnt/asec")) {
                continue;
            }

            // Eliminate if not a directory or fully accessible
            tempFile = new File(splits[1]);
            if (!tempFile.exists()) {
                continue;
            }
            if (!tempFile.isDirectory()) {
                continue;
            }
            if (!tempFile.canRead()) {
                continue;
            }
            if (!tempFile.canWrite()) {
                continue;
            }

            // Met all the criteria, assume sdcard
            arrayList.add(splits[1]);
        }

    } catch (FileNotFoundException e) {
    } catch (IOException e) {
    } finally {
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (IOException e) {
            }
        }
    }

    // Send list back to caller

    if (arrayList.size() == 0) {
        arrayList.add("sdcard not found");
    }
    directories = new String[arrayList.size()];
    for (int i = 0; i < arrayList.size(); i++) {
        directories[i] = arrayList.get(i);
    }
    return directories;
}

}

The MyLog.d is a trace class that expands Log.d - it can be dropped.

The class reads /proc/mounts/ and:

  1. checks to see if the path name is the internal sdcard directory
  2. checks to see if its a block device
  3. skips mtdblock devices
  4. skips anything that is not mounted
  5. skips secure and asec directories
  6. makes sure it exists, is a directory, and read/write accessible

If all this is matched, it assumes that you have an sdcard and adds the path to the array list. It returns a string array of path names.

To call the getDirectories function, code something similar to:

String[] sdcardDirectories = GetRemoveableDevice.getDirectories();

The paths returned can be used to create a user selection list, scan for a file, or whatever.

Finally, here's two lines of MyLog.d from an emulator test (second line is the emulator sdcard):

09-19 15:57:12.511: D/GetRemoveableDevice(651): lineRead: /dev/block/mtdblock2 /cache yaffs2 rw,nosuid,nodev 0 0

09-19 15:57:12.511: D/GetRemoveableDevice(651): lineRead: /dev/block/vold/179:0 /mnt/sdcard vfat rw,dirsync,nosuid,nodev,noexec,uid=1000,gid=1015,fmask=0702,dmask=0702,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0

Based on Howards class I made some modifications to make it work on the Galaxy S3.

  1. Environment.getExternalStorageDirectory() returns internal storage on the S3.
  2. Removable storage is not necessarily mounted under /mnt
  3. Removable media must have vfat filesystem

_

public static String getDirectory() {
        Log.d(TAG, "getStorageDirectories");
        File tempFile;
        String[] splits;
        ArrayList<String> arrayList = new ArrayList<String>();
        BufferedReader bufferedReader = null;
        String lineRead;

        try {
            arrayList.clear(); // redundant, but what the hey
            bufferedReader = new BufferedReader(new FileReader("/proc/mounts"));

            while ((lineRead = bufferedReader.readLine()) != null) {
                Log.d(TAG, "lineRead: " + lineRead);
                splits = lineRead.split(" ");

                // skip if not external storage device
                if (!splits[0].contains("/dev/block/")) {
                    continue;
                }

                // skip if mtdblock device
                if (splits[0].contains("/dev/block/mtdblock")) {
                    continue;
                }

                // skip if not in vfat node
                if (!splits[2].contains("vfat")) {
                    continue;
                }

                // skip these names
                if (splits[1].contains("/secure")) {
                    continue;
                }

                if (splits[1].contains("/mnt/asec")) {
                    continue;
                }

                // Eliminate if not a directory or fully accessible
                tempFile = new File(splits[1]);
                if (!tempFile.exists()) {
                    continue;
                }
                if (!tempFile.isDirectory()) {
                    continue;
                }
                if (!tempFile.canRead()) {
                    continue;
                }
                if (!tempFile.canWrite()) {
                    continue;
                }

                // Met all the criteria, assume sdcard
                return splits[1];
            }

        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                }
            }
        }

        return null;
    }

Based on the 2 classes presented above I modified the class further on noticing that all of the external removable storage on several phones and tablets that I could find were mounted using vold, the volume mounting daemon.

            // skip if not external storage device
            if (!splits[0].contains("vold")) {
                continue;
            }

            if (splits[1].contains("/mnt/asec")) {
                continue;
            }

            // Eliminate if not a directory or fully accessible
            tempFile = new File(splits[1]);
            if (!tempFile.exists()) {
                continue;
            }
            if (!tempFile.isDirectory()) {
                continue;
            }
            if (!tempFile.canRead()) {
                continue;
            }
            if (!tempFile.canWrite()) {
                continue;
            }

            // Met all the criteria, assume sdcard
            arrayList.add(splits[1]);

These capabilities are available in all Android versions:

  • To obtain the application's folder on the external storage, call Context.getExternalFilesDir .

  • Keep in mind that your app needs explicit permission to access external storage, and that you should check if it is available via Environment.getExternalStorageState

  • And yes, ACTION_MEDIA_MOUNTED will be broadcast whenever removable media becomes accessible (You should also listen for ACTION_MEDIA_EJECT and ACTION_MEDIA_REMOVED )

This is the method I created and am using. This has worked on Samsung Galaxy S4, Samsung Galaxy Note 3 and Sony Xperia Z2.

private static String[] getRemovableStoragePaths() {
    String[] directories;
    String[] splits;
    ArrayList<String> pathList = new ArrayList<String>();
    BufferedReader bufferedReader = null;
    String lineRead;

    try {
        bufferedReader = new BufferedReader(new FileReader("/proc/mounts"));

        while ((lineRead = bufferedReader.readLine()) != null) {
            Log.d(TAG, "lineRead: " + lineRead);
            splits = lineRead.split(" ");
            Log.d(TAG, "Testing path: " + splits[1]);

            if (!splits[1].contains("/storage")) {
                continue;
            }

            if (splits[1].contains("/emulated")) {
                // emulated indicates an internal storage location, so skip it.
                continue;
            }

            // Eliminate if not a directory or fully accessible
            Log.d(TAG, "Path found: " + splits[1]);

            // Met all the criteria, assume sdcard
            pathList.add(splits[1]);
        }

    } catch (FileNotFoundException e) {
    } catch (IOException e) {
    } finally {
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (IOException e) {
            }
        }
    }

    // Send list back to caller

    if (pathList.size() == 0) {
        pathList.add("sdcard not found");
    } else {
        Log.d(TAG, "Found potential removable storage locations: " + pathList);
    }
    directories = new String[pathList.size()];
    for (int i = 0; i < pathList.size(); i++) {
        directories[i] = pathList.get(i);
    }
    return directories;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM