How to list all SD cards on Android

With all these new phones and tablets coming out, a lot of Android devices are now equipped with more than one SD cards. On these new devices, there is usually an internal (built-in) storage device, and an external storage device. The built-in storage device might not be an SD card, but the external storage device usually is, which might be removable by the user. And we might eventually see Android devices with more than one SD slot, who knows?

The storage device is called the shared storage on Android, or traditionally called external storage. However, on these new Android devices, the internal built-in storage is not external anymore. But the Android API still refers to it as an external storage. In compensation for their naming gaffe, a note was added to the API documentation calling it now the primary external storage.

Unfortunately, for developers who need to find out all the storage devices, the API still does not have an easy way to list them all. If you call

Environment.getExternalStorageDirectory();

you get the primary external storage. For the Android designers, one external storage gotta be enough for anyone. Doesn’t this remind us of a certain legend, in which someone has claimed that 64K of memory gotta be enough for anyone?

And unfortunately for all developers too, the only standard mount point for storage devices is /mnt/sdcard. The mount point for any other external storage is left to the device manufacturers. Therefore, we got something like:

/mnt/sdcard/external_sd/
/Removable/SD
/Removable/MicroSD
...

And a long list of others. You can’t even use the stupid method of hard-coding the mount point.

I ran into this issue recently when I wanted to add an option, in Renzhi Safe, to allow users to store their safe container files on more than one external storage. I have to use the following codes to deduct the list of available external storage. It may not be foolproof, but hopefully, this will be useful to someone else too.

  /**
   * Similar to android.os.Environment.getExternalStorageDirectory(), except that
   * here, we return all possible storage directories. The Environment class only
   * returns one storage directory. If you have an extended SD card, it does not
   * return the directory path. Here we are trying to return all of them.
   *
   * @return
   */
  public static String[] getStorageDirectories()
  {
      String[] dirs = null;
      BufferedReader bufReader = null;
      try {
          bufReader = new BufferedReader(new FileReader("/proc/mounts"));
          ArrayList list = new ArrayList();
          String line;
          while ((line = bufReader.readLine()) != null) {
              if (line.contains("vfat") || line.contains("/mnt")) {
                  StringTokenizer tokens = new StringTokenizer(line, " ");
                  String s = tokens.nextToken();
                  s = tokens.nextToken(); // Take the second token, i.e. mount point

                  if (s.equals(Environment.getExternalStorageDirectory().getPath())) {
                      list.add(s);
                  }
                  else if (line.contains("/dev/block/vold")) {
                      if (!line.contains("/mnt/secure") && !line.contains("/mnt/asec") && !line.contains("/mnt/obb") && !line.contains("/dev/mapper") && !line.contains("tmpfs")) {
                          list.add(s);
                      }
                  }
              }
          }

          dirs = new String[list.size()];
          for (int i = 0; i < list.size(); i++) {
              dirs[i] = list.get(i);
          }
      }
      catch (FileNotFoundException e) {}
      catch (IOException e) {}
      finally {
          if (bufReader != null) {
              try {
                  bufReader.close();
              }
              catch (IOException e) {}
      }

      return dirs;
  }

There are a few assumptions here. We look for all mounts with vfat file system, or those with mount point starting with /mnt. We take the mount point that matches what is returned by

Environment.getExternalStorageDirectory().getPath()

regardless of the file system type. The mount point of this primary external storage is standardized to /mnt/sdcard. If the device has only one external storage, the file system would be vfat. However, if the primary external storage is built-in and the device has another secondary external storage, the file system of the primary external storage could be anything, from ext3, ext4 to fuse. And then, we discard all those used by Android internally. The rest is, hopefully, what we are looking for.

This is certainly not completely reliable, but it does the job. I haven’t found a better way yet, and have no way of testing the code on all devices, so suggestions are welcomed.

6 Comments

  1. Jim says:

    Thanks, I was thinking about this issue the whole day. It works.

    Jim

  2. Tom says:

    In my work, get devices all storage list method as below:
    Parse ‘/system/etc/vold.fstab’ file, get all support storages.
    —————-begin———————
    public static HashSet getStorageSet(){
    HashSet storageSet = getStorageSet(new File(“/system/etc/vold.fstab”));
    if (storageSet == null || storageSet.isEmpty()) {
    storageSet = new HashSet();
    storageSet.add(Environment.getExternalStorageDirectory().getAbsolutePath());
    }
    return storageSet;
    }

    public static HashSet getStorageSet(File file) {
    HashSet storageSet = new HashSet();
    BufferedReader reader = null;
    try {
    reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
    String line;
    while ((line = reader.readLine()) != null) {
    HashSet _storage = parse(line);
    if(_storage==null)
    continue;
    storageSet.addAll(_storage);
    }
    } catch (Exception e) {
    e.printStackTrace();
    }finally{
    try {
    reader.close();
    } catch (Exception e) {
    e.printStackTrace();
    }
    reader = null;
    }
    /*
    * set default external storage
    */
    storageSet.add(Environment.getExternalStorageDirectory().getAbsolutePath());
    return storageSet;
    }

    private static HashSet parse(String str) {
    if (str == null)
    return null;
    if (str.length()==0)
    return null;
    if (str.startsWith(“#”))
    return null;
    HashSet storageSet = new HashSet();
    /*
    * dev_mount sdcard /mnt/sdcard auto /devices/platform/msm_sdcc.1/mmc_host
    * dev_mount SdCard /mnt/sdcard/extStorages /mnt/sdcard/extStorages/SdCard auto sd /devices/platform/s3c-sdhci.2/mmc_host/mmc1
    */
    Pattern patter1 = Pattern.compile(“(/mnt/[^ ]+?)((?=[ ]+auto[ ]+)|(?=[ ]+(\\d*[ ]+)))”);
    /*
    * dev_mount ins /mnt/emmc emmc /devices/platform/msm_sdcc.3/mmc_host
    */
    Pattern patter2 = Pattern.compile(“(/mnt/.+?[ ]+)”);
    Matcher matcher1 = patter1.matcher(str);
    boolean b1 = matcher1.find();
    if (b1) {
    String _group = matcher1.group(1);
    storageSet.add(_group);
    }

    Matcher matcher2 = patter2.matcher(str);
    boolean b2 = matcher2.find();
    if (!b1 && b2) {
    String _group = matcher2.group(1);
    storageSet.add(_group);
    }
    return storageSet;
    }

    —————-end———————

    1, Call getStorageSet() method, you can get all storages list.
    2, Check which storages are available.

  3. Mazin says:

    Thank you, this was really a good post. I use your implementation and actually cover all device in our office. Why you guess isn’t completely reliable? until big g dosen’t provide a official guidelines to follow to me this seems ok.

    Best Regards.

  4. JM says:

    Strangely, it seems it only returns “aditional” SD cards.

    On my Nexus 7 2013 (no SD Card slot), it does return nothing.

    On my Sony XP1, it just returns the external SDcard, not the internal memory…

    Is it the intended behaviour?

  5. xp says:

    Hi,

    This code is very old, the new Android devices have very different mount list, and it is highly customized by the hardware manufacturers. This makes storage management on Android quite a hassle.

  6. mayank says:

    HashSet storageSet = getStorageSet(new File(“/system/etc/vold.fstab”));

    Will cause a file not found exception on a number of devices, where this file isnt present – have you encountered similar issues in the past?

Leave a Reply

*


Switch to our mobile site