In one of my android projects I had to show the memory information of SD card. There was no built-in method to get the SD card directory. The method getExternalStorageDirectory() of the class android.os.Environment has very little chance of returning the SD card directory as it is very much dependent on what has been set as an external storage in the environment, this could be the SD card but most probably it will be the built-in storage device directory distinct from the protected internal storage.
Since the android is based on the linux kernel we can leverage it to find out the solution of this problem. As the system file “/proc/mounts” keeps the track of all mounted storage devices, we can seek the SD card directory in that file. The following code snippet does exactly the same thing. It reads the “/proc/mounts” file line by line and filters out temporary file system or emulated file system, and retrieves the SD card directory if mounted.
/** * Returns the file object that contains SD card directory * * @return SD card directory as a file, null if the card is not mounted or unavailble */ public File getSDCardDirectory() { File d = null; String storagePath = "/storage"; StringBuilder sb = new StringBuilder(); boolean isFirst = true; try { //we need to read the /proc/mounts file FileInputStream fs = new FileInputStream("/proc/mounts"); DataInputStream in = new DataInputStream(fs); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String fileLine; //reading the file and separting each line by line separator while ((fileLine = br.readLine()) != null) { if (fileLine.contains(storagePath)) { if (isFirst) { isFirst = false; } else { sb.append("\n"); } sb.append(fileLine); } } in.close(); //Close the stream } catch (Exception e) { //not doing anything Log.e("tag-ex",""+e.getLocalizedMessage()); } //now we need to parse the file to get the SD card directory if (null != sb.toString() && !sb.toString().equals("")) { //putting all the storage directories in an array String sPathArray[] = sb.toString().split("\n"); String sPath = ""; if (sPathArray.length > 1) { // most of the case the second one is the SD card directory sPath = sPathArray[1]; // if the directory is emulated then it is not SD card, we shall seek if (sPath.contains("emulated") && sPathArray.length > 2) { sPath = sPathArray[2]; } } //final filter, emulated and tmpfs not considered as SD card if (!sPath.equals("") && !sPath.contains("emulated") && !sPath.contains("tmpfs")) { //now we need the column that contains the directory sPath = sPath.substring(sPath.indexOf(storagePath), sPath.indexOf(" ", sPath.indexOf(storagePath))); d = new File(sPath); } } return d; }
Once the directory is retrieved, then it becomes very easy to get the memory information (total space, available space).
/** * Returns the available external memory size */ public double getAvailableSDCardMemorySize() { File path = getSDCardDirectory(); if (null != path) {//check if SD card is mounted StatFs stat = new StatFs(path.getPath()); long blockSize = 0; if (Build.VERSION.SDK_INT < 18) { blockSize = stat.getBlockSize(); } else { blockSize = stat.getBlockSizeLong(); } long availableBlocks = 0; if (Build.VERSION.SDK_INT < 18) { availableBlocks = stat.getAvailableBlocks(); } else { availableBlocks = stat.getAvailableBlocksLong(); } return formatSizeGB(availableBlocks * blockSize); } else { return 0; } } /** * Returns the total external memory size */ public double getTotalSDCardMemorySize() { File path = getSDCardDirectory(); if (null != path) {//check if SD card is mounted StatFs stat = new StatFs(path.getPath()); long blockSize = 0; if (Build.VERSION.SDK_INT < 18) { blockSize = stat.getBlockSize(); } else { blockSize = stat.getBlockSizeLong(); } long totalBlocks = 0; if (Build.VERSION.SDK_INT < 18) { totalBlocks = stat.getBlockCount(); } else { totalBlocks = stat.getBlockCountLong(); } return formatSizeGB(totalBlocks * blockSize); } else { return 0; } } private double gbDivider = 1073741824; //1 GB = 1073741824 Bytes /** * Returns the string formatted value for the size */ public double formatSizeGB(double total) { double amount = (total / gbDivider); BigDecimal bd = new BigDecimal(amount).setScale(2, RoundingMode.HALF_EVEN); amount = bd.doubleValue(); return amount; }
The above screen-shot shows what is excepted from the sample app.
The code has been tested in Samsung GT-S6310 (android os 4.1.2), Huawei T-701u (android os 4.4.2), Lenovo A7-30HC (android os 4.4.2), LG D855 (android os 6.0).
The full source code can be found here.