[Q][SOLVED]Get path and size of SDcard in Android 4.4 KitKat

Search This thread

MattMatt0240

Senior Member
Sep 10, 2011
478
197
Hello everyone, I have an app on Google Play that shows the end user information about their device. Within this information, a Memory/Storage category is shown. Everything was good and fine until mean ol` KitKat wanted to deny access to the SDcard... Although I can understand Google's move on that subject, it can not go un-noticed that it may break many app's functionality (like my own). Anyhow, below is my class that scans for mount points, and stores them in an ArrayList. I used some code from StackOverflow somewhere, but I do not have the link.

StorageUtils :
Code:
public class StorageUtils {

	private static final String TAG = "StorageUtils";

	public static class StorageInfo {

		public final String path;
		public final boolean internal;
		public final boolean readonly;
		public final int display_number;

		StorageInfo(String path, boolean internal, boolean readonly,
				int display_number) {
			this.path = path;
			this.internal = internal;
			this.readonly = readonly;
			this.display_number = display_number;
		}
	}

	public static ArrayList<StorageInfo> getStorageList() {

		ArrayList<StorageInfo> list = new ArrayList<StorageInfo>();
		String def_path = Environment.getExternalStorageDirectory().getPath();
		boolean def_path_internal = !Environment.isExternalStorageRemovable();
		String def_path_state = Environment.getExternalStorageState();
		boolean def_path_available = def_path_state
				.equals(Environment.MEDIA_MOUNTED)
				|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
		boolean def_path_readonly = Environment.getExternalStorageState()
				.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
		BufferedReader buf_reader = null;
		try {
			HashSet<String> paths = new HashSet<String>();
			buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
			String line;
			int cur_display_number = 1;
			Log.d(TAG, "/proc/mounts");
			while ((line = buf_reader.readLine()) != null) {
				Log.d(TAG, line);
				if (line.contains("vfat") || line.contains("/mnt")) {
					StringTokenizer tokens = new StringTokenizer(line, " ");
					String unused = tokens.nextToken(); // device
					String mount_point = tokens.nextToken(); // mount point
					if (paths.contains(mount_point)) {
						continue;
					}
					unused = tokens.nextToken(); // file system
					List<String> flags = Arrays.asList(tokens.nextToken()
							.split(",")); // flags
					boolean readonly = flags.contains("ro");

					if (mount_point.equals(def_path)) {
						paths.add(def_path);
						list.add(new StorageInfo(def_path, def_path_internal,
								readonly, -1));
					} 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")) {
							paths.add(mount_point);
							list.add(new StorageInfo(mount_point, false,
									readonly, cur_display_number++));
						}
					}
				}
			}

			if (!paths.contains(def_path) && def_path_available) {
				list.add(new StorageInfo(def_path, def_path_internal,
						def_path_readonly, -1));
			}

		} catch (FileNotFoundException ex) {
			ex.printStackTrace();
		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			if (buf_reader != null) {
				try {
					buf_reader.close();
				} catch (IOException ex) {
				}
			}
		}
		return list;
	}

	public static String getReadableFileSize(long bytes, boolean si) {
		int unit = si ? 1000 : 1024;
		if (bytes < unit)
			return bytes + " B";
		int exp = (int) (Math.log(bytes) / Math.log(unit));
		String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1)
				+ (si ? "" : "i");
		return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
	}

	@SuppressLint("NewApi")
	public static long getFreeSpace(String path) {
		StatFs statFs = new StatFs(path);
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
			long sdAvailSize = statFs.getFreeBlocksLong()
					* statFs.getBlockSizeLong();
			return sdAvailSize;
		} else {
			@SuppressWarnings("deprecation")
			double sdAvailSize = (double) statFs.getFreeBlocks()
					* (double) statFs.getBlockSize();

			return (long) sdAvailSize;
		}
	}

	public static long getUsedSpace(String path) {
		return getTotalSpace(path) - getFreeSpace(path);
	}

	@SuppressLint("NewApi")
	public static long getTotalSpace(String path) {
		StatFs statFs = new StatFs(path);
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
			long sdTotalSize = statFs.getBlockCountLong()
					* statFs.getBlockSizeLong();
			return sdTotalSize;
		} else {
			@SuppressWarnings("deprecation")
			double sdTotalSize = (double) statFs.getBlockCount()
					* statFs.getBlockSize();

			return (long) sdTotalSize;
		}
	}

	/**
	 * getSize()[0] is /mnt/sdcard. getSize()[1] is size of sd (example 12.0G),
	 * getSize()[2] is used, [3] is free, [4] is blksize
	 * 
	 * @return
	 * @throws IOException
	 */
	public static String[] getSize() throws IOException {
		String memory = "";
		Process p = Runtime.getRuntime().exec("df /mnt/sdcard");
		InputStream is = p.getInputStream();
		int by = -1;
		while ((by = is.read()) != -1) {
			memory += new String(new byte[] { (byte) by });
		}
		for (String df : memory.split("/n")) {
			if (df.startsWith("/mnt/sdcard")) {
				String[] par = df.split(" ");
				List<String> pp = new ArrayList<String>();
				for (String pa : par) {
					if (!pa.isEmpty()) {
						pp.add(pa);
					}
				}
				return pp.toArray(new String[pp.size()]);

			}
		}
		return null;
	}

}

Next, I retrieve the used, free, and total space of each mount point. This is where KitKat breaks my app.

CpuMemFragment :
Code:
public class CpuMemFragment extends Fragment {
	// CPU
	String devCpuInfo;
	TextView tvCpuInfo;

	// RAM
	String devRamInfo;
	TextView tvRamInfo;

	// Storage
	String devStorageA, devStorageB;
	TextView tvStorageAName, tvStorageA, tvStorageB, tvStorageBName;

	AdView adView;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

		View rootView = inflater.inflate(R.layout.cpu_mem, container, false);

		return rootView;
	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onActivityCreated(savedInstanceState);

		// *** CPU ***
		//
		devCpuInfo = readCpuInfo();
		//
		// #################################

		// *** RAM ***
		//
		devRamInfo = readTotalRam();
		//
		// #################################

		// *** STORAGE ***
		//

		ArrayList<StorageInfo> storageInfoList = StorageUtils.getStorageList();

		tvStorageAName = (TextView) getView().findViewById(R.id.tvStorageAName);

		tvStorageBName = (TextView) getView().findViewById(R.id.tvStorageBName);

		if (storageInfoList.size() > 0) {

			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
					&& !storageInfoList.get(0).internal) {
				kitKatWorkaround(0);
			}
			tvStorageAName.setText(storageInfoList.get(0).path);

			devStorageA = StorageUtils.getReadableFileSize(
					(StorageUtils.getUsedSpace(storageInfoList.get(0).path)),
					true)
					+ "/"
					+ StorageUtils.getReadableFileSize((StorageUtils
							.getTotalSpace(storageInfoList.get(0).path)), true);

			if (storageInfoList.size() > 1) {
				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
						&& !storageInfoList.get(0).internal) {
					kitKatWorkaround(1);
				}
				tvStorageBName.setText(storageInfoList.get(1).path);

				devStorageB = StorageUtils.getReadableFileSize(
						StorageUtils.getUsedSpace(storageInfoList.get(1).path)
								+ (StorageUtils.getUsedSpace("system/")), true)
						+ "/"
						+ StorageUtils.getReadableFileSize((StorageUtils
								.getTotalSpace(storageInfoList.get(1).path)),
								true);
			} else {
				devStorageB = "N/A";
			}
		} else {
			devStorageA = "N/A";
			devStorageB = "N/A";
		}
		//
		// #################################

		// CPU
		tvCpuInfo = (TextView) getView().findViewById(R.id.tvCpuInfo);
		tvCpuInfo.setText(devCpuInfo);
		//
		// #################################

		// RAM
		tvRamInfo = (TextView) getView().findViewById(R.id.tvRamInfo);
		tvRamInfo.setText(devRamInfo);
		//
		// #################################

		// STORAGE
		tvStorageA = (TextView) getView().findViewById(R.id.tvStorageA);
		tvStorageA.setText(devStorageA);

		tvStorageB = (TextView) getView().findViewById(R.id.tvStorageB);
		tvStorageB.setText(devStorageB);
		//
		// #################################

		// Look up the AdView as a resource and load a request.
		adView = (AdView) getActivity().findViewById(R.id.adCpuMemBanner);
		AdRequest adRequest = new AdRequest.Builder().build();
		adView.loadAd(adRequest);
	}

	@Override
	public void onPause() {
		if (adView != null) {
			adView.pause();
		}
		super.onPause();
	}

	@Override
	public void onResume() {
		super.onResume();
		if (adView != null) {
			adView.resume();
		}
	}

	@Override
	public void onDestroy() {
		if (adView != null) {
			adView.destroy();
		}
		super.onDestroy();
	}

	private static synchronized String readCpuInfo() {
		ProcessBuilder cmd;
		String result = "";

		try {
			String[] args = { "/system/bin/cat", "/proc/cpuinfo" };
			cmd = new ProcessBuilder(args);

			Process process = cmd.start();
			InputStream in = process.getInputStream();
			byte[] re = new byte[1024];
			while (in.read(re) != -1) {
				System.out.println(new String(re));
				result = result + new String(re);
			}
			in.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
		return result;
	}

	public static synchronized String readTotalRam() {
		String load = "";
		try {
			RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
			load = reader.readLine();
			reader.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
		return load;
	}

// An attempt to workaround KitKat changes according to android documentation
	public void kitKatWorkaround(int index) {
		String path1 = Environment.getExternalStorageDirectory().getPath();

		if (index == 0) {

			tvStorageAName.setText(path1);

			devStorageA = StorageUtils.getReadableFileSize(
					(StorageUtils.getUsedSpace(path1)), true)
					+ "/"
					+ StorageUtils.getReadableFileSize(
							(StorageUtils.getTotalSpace(path1)), true);
		}
		if (index == 1) {
			tvStorageBName.setText(path1);

			devStorageB = StorageUtils.getReadableFileSize(
					StorageUtils.getUsedSpace(path1)
							+ (StorageUtils.getUsedSpace("system/")), true)
					+ "/"
					+ StorageUtils.getReadableFileSize(
							(StorageUtils.getTotalSpace(path1)), true);

		}
	}
}

The suspected error is right here in the logcat:

Caused by: libcore.io.ErrnoException: statvfs failed: EACCES (Permission denied)

is there any alternative to retrieving sdCards sizes, or should I simply display a dialog stating that I have no control over Google's decision in Android 4.4, and apologize for the inconvenince?
Also, while I'm at it: On some models of Android devices, the sizes of the SDcard are all out of whack. Some showing more used space than total, and innacurate readings. This seems to only happen with internal/emulated storage. What is the most accurate way of getting sizes of all SDcard locations?

Thank you kindly for your time, Happy Coding!
 
Last edited:
  • Like
Reactions: tschmid

MattMatt0240

Senior Member
Sep 10, 2011
478
197
Fully Functional

Got it working after further looking into android documentation, and implementing new methods:

Within analyzing storage method:
Code:
...
ArrayList<StorageInfo> storageInfoList = StorageUtils.getStorageList();

		tvStorageAName = (TextView) getView().findViewById(R.id.tvStorageAName);

		tvStorageBName = (TextView) getView().findViewById(R.id.tvStorageBName);

		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
			kitKatWorkaround();
		} else if (storageInfoList.size() > 0) {

			tvStorageAName.setText(storageInfoList.get(0).path);

			devStorageA = StorageUtils.getReadableFileSize(
					(StorageUtils.getUsedSpace(storageInfoList.get(0).path)),
					true)
					+ "/"
					+ StorageUtils.getReadableFileSize((StorageUtils
							.getTotalSpace(storageInfoList.get(0).path)), true);

			if (storageInfoList.size() > 1) {
				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
						&& !storageInfoList.get(0).internal) {
					kitKatWorkaround();
				}
				tvStorageBName.setText(storageInfoList.get(1).path);

				devStorageB = StorageUtils.getReadableFileSize(
						StorageUtils.getUsedSpace(storageInfoList.get(1).path)
								+ (StorageUtils.getUsedSpace("system/")), true)
						+ "/"
						+ StorageUtils.getReadableFileSize((StorageUtils
								.getTotalSpace(storageInfoList.get(1).path)),
								true);
			} else {
				devStorageB = "N/A";
			}
		} else {
			devStorageA = "N/A";
			devStorageB = "N/A";
		}
...

kitKatWorkaround();
Code:
@SuppressLint("NewApi")
	public void kitKatWorkaround() {

		tvStorageA = (TextView) getView().findViewById(R.id.tvStorageA);
		tvStorageB = (TextView) getView().findViewById(R.id.tvStorageB);

		File[] sdCards = getActivity().getApplicationContext()
				.getExternalCacheDirs();

		if (sdCards.length > 0) {

			File sdCard1 = sdCards[0];

			tvStorageAName.setText(sdCard1.getAbsolutePath()
					.replace(
							"Android/data/" + getActivity().getPackageName()
									+ "/cache", ""));

			devStorageA = StorageUtils.getReadableFileSize(
					(StorageUtils.getUsedSpace(sdCard1.getAbsolutePath())),
					true)
					+ "/"
					+ StorageUtils.getReadableFileSize((StorageUtils
							.getTotalSpace(sdCard1.getAbsolutePath())), true);

			if (sdCards.length > 1) {
				File sdCard2 = sdCards[1];

				tvStorageBName.setText(sdCard2.getAbsolutePath().replace(
						"Android/data/" + getActivity().getPackageName()
								+ "/cache", ""));

				devStorageB = StorageUtils.getReadableFileSize(
						(StorageUtils.getUsedSpace(sdCard2.getAbsolutePath())),
						true)
						+ "/"
						+ StorageUtils.getReadableFileSize((StorageUtils
								.getTotalSpace(sdCard2.getAbsolutePath())),
								true);
			} else {
				devStorageB = "N/A";
			}
		} else {
			devStorageA = "N/A";
			devStorageB = "N/A";
		}

		tvStorageA.setText(devStorageA);
		tvStorageB.setText(devStorageB);
	}

Just going to leave this out there, it works on at least Android 3.0+ that I have tested. Using StorageUtils retrieve all available mount points, and use code above to setText() path and size ;)

Feel free to use this if it helps (don't forget the thanks button) :good:
 

Top Liked Posts

  • There are no posts matching your filters.
  • 3
    Fully Functional

    Got it working after further looking into android documentation, and implementing new methods:

    Within analyzing storage method:
    Code:
    ...
    ArrayList<StorageInfo> storageInfoList = StorageUtils.getStorageList();
    
    		tvStorageAName = (TextView) getView().findViewById(R.id.tvStorageAName);
    
    		tvStorageBName = (TextView) getView().findViewById(R.id.tvStorageBName);
    
    		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    			kitKatWorkaround();
    		} else if (storageInfoList.size() > 0) {
    
    			tvStorageAName.setText(storageInfoList.get(0).path);
    
    			devStorageA = StorageUtils.getReadableFileSize(
    					(StorageUtils.getUsedSpace(storageInfoList.get(0).path)),
    					true)
    					+ "/"
    					+ StorageUtils.getReadableFileSize((StorageUtils
    							.getTotalSpace(storageInfoList.get(0).path)), true);
    
    			if (storageInfoList.size() > 1) {
    				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
    						&& !storageInfoList.get(0).internal) {
    					kitKatWorkaround();
    				}
    				tvStorageBName.setText(storageInfoList.get(1).path);
    
    				devStorageB = StorageUtils.getReadableFileSize(
    						StorageUtils.getUsedSpace(storageInfoList.get(1).path)
    								+ (StorageUtils.getUsedSpace("system/")), true)
    						+ "/"
    						+ StorageUtils.getReadableFileSize((StorageUtils
    								.getTotalSpace(storageInfoList.get(1).path)),
    								true);
    			} else {
    				devStorageB = "N/A";
    			}
    		} else {
    			devStorageA = "N/A";
    			devStorageB = "N/A";
    		}
    ...

    kitKatWorkaround();
    Code:
    @SuppressLint("NewApi")
    	public void kitKatWorkaround() {
    
    		tvStorageA = (TextView) getView().findViewById(R.id.tvStorageA);
    		tvStorageB = (TextView) getView().findViewById(R.id.tvStorageB);
    
    		File[] sdCards = getActivity().getApplicationContext()
    				.getExternalCacheDirs();
    
    		if (sdCards.length > 0) {
    
    			File sdCard1 = sdCards[0];
    
    			tvStorageAName.setText(sdCard1.getAbsolutePath()
    					.replace(
    							"Android/data/" + getActivity().getPackageName()
    									+ "/cache", ""));
    
    			devStorageA = StorageUtils.getReadableFileSize(
    					(StorageUtils.getUsedSpace(sdCard1.getAbsolutePath())),
    					true)
    					+ "/"
    					+ StorageUtils.getReadableFileSize((StorageUtils
    							.getTotalSpace(sdCard1.getAbsolutePath())), true);
    
    			if (sdCards.length > 1) {
    				File sdCard2 = sdCards[1];
    
    				tvStorageBName.setText(sdCard2.getAbsolutePath().replace(
    						"Android/data/" + getActivity().getPackageName()
    								+ "/cache", ""));
    
    				devStorageB = StorageUtils.getReadableFileSize(
    						(StorageUtils.getUsedSpace(sdCard2.getAbsolutePath())),
    						true)
    						+ "/"
    						+ StorageUtils.getReadableFileSize((StorageUtils
    								.getTotalSpace(sdCard2.getAbsolutePath())),
    								true);
    			} else {
    				devStorageB = "N/A";
    			}
    		} else {
    			devStorageA = "N/A";
    			devStorageB = "N/A";
    		}
    
    		tvStorageA.setText(devStorageA);
    		tvStorageB.setText(devStorageB);
    	}

    Just going to leave this out there, it works on at least Android 3.0+ that I have tested. Using StorageUtils retrieve all available mount points, and use code above to setText() path and size ;)

    Feel free to use this if it helps (don't forget the thanks button) :good:
    1
    Hello everyone, I have an app on Google Play that shows the end user information about their device. Within this information, a Memory/Storage category is shown. Everything was good and fine until mean ol` KitKat wanted to deny access to the SDcard... Although I can understand Google's move on that subject, it can not go un-noticed that it may break many app's functionality (like my own). Anyhow, below is my class that scans for mount points, and stores them in an ArrayList. I used some code from StackOverflow somewhere, but I do not have the link.

    StorageUtils :
    Code:
    public class StorageUtils {
    
    	private static final String TAG = "StorageUtils";
    
    	public static class StorageInfo {
    
    		public final String path;
    		public final boolean internal;
    		public final boolean readonly;
    		public final int display_number;
    
    		StorageInfo(String path, boolean internal, boolean readonly,
    				int display_number) {
    			this.path = path;
    			this.internal = internal;
    			this.readonly = readonly;
    			this.display_number = display_number;
    		}
    	}
    
    	public static ArrayList<StorageInfo> getStorageList() {
    
    		ArrayList<StorageInfo> list = new ArrayList<StorageInfo>();
    		String def_path = Environment.getExternalStorageDirectory().getPath();
    		boolean def_path_internal = !Environment.isExternalStorageRemovable();
    		String def_path_state = Environment.getExternalStorageState();
    		boolean def_path_available = def_path_state
    				.equals(Environment.MEDIA_MOUNTED)
    				|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
    		boolean def_path_readonly = Environment.getExternalStorageState()
    				.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
    		BufferedReader buf_reader = null;
    		try {
    			HashSet<String> paths = new HashSet<String>();
    			buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
    			String line;
    			int cur_display_number = 1;
    			Log.d(TAG, "/proc/mounts");
    			while ((line = buf_reader.readLine()) != null) {
    				Log.d(TAG, line);
    				if (line.contains("vfat") || line.contains("/mnt")) {
    					StringTokenizer tokens = new StringTokenizer(line, " ");
    					String unused = tokens.nextToken(); // device
    					String mount_point = tokens.nextToken(); // mount point
    					if (paths.contains(mount_point)) {
    						continue;
    					}
    					unused = tokens.nextToken(); // file system
    					List<String> flags = Arrays.asList(tokens.nextToken()
    							.split(",")); // flags
    					boolean readonly = flags.contains("ro");
    
    					if (mount_point.equals(def_path)) {
    						paths.add(def_path);
    						list.add(new StorageInfo(def_path, def_path_internal,
    								readonly, -1));
    					} 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")) {
    							paths.add(mount_point);
    							list.add(new StorageInfo(mount_point, false,
    									readonly, cur_display_number++));
    						}
    					}
    				}
    			}
    
    			if (!paths.contains(def_path) && def_path_available) {
    				list.add(new StorageInfo(def_path, def_path_internal,
    						def_path_readonly, -1));
    			}
    
    		} catch (FileNotFoundException ex) {
    			ex.printStackTrace();
    		} catch (IOException ex) {
    			ex.printStackTrace();
    		} finally {
    			if (buf_reader != null) {
    				try {
    					buf_reader.close();
    				} catch (IOException ex) {
    				}
    			}
    		}
    		return list;
    	}
    
    	public static String getReadableFileSize(long bytes, boolean si) {
    		int unit = si ? 1000 : 1024;
    		if (bytes < unit)
    			return bytes + " B";
    		int exp = (int) (Math.log(bytes) / Math.log(unit));
    		String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1)
    				+ (si ? "" : "i");
    		return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
    	}
    
    	@SuppressLint("NewApi")
    	public static long getFreeSpace(String path) {
    		StatFs statFs = new StatFs(path);
    		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
    			long sdAvailSize = statFs.getFreeBlocksLong()
    					* statFs.getBlockSizeLong();
    			return sdAvailSize;
    		} else {
    			@SuppressWarnings("deprecation")
    			double sdAvailSize = (double) statFs.getFreeBlocks()
    					* (double) statFs.getBlockSize();
    
    			return (long) sdAvailSize;
    		}
    	}
    
    	public static long getUsedSpace(String path) {
    		return getTotalSpace(path) - getFreeSpace(path);
    	}
    
    	@SuppressLint("NewApi")
    	public static long getTotalSpace(String path) {
    		StatFs statFs = new StatFs(path);
    		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
    			long sdTotalSize = statFs.getBlockCountLong()
    					* statFs.getBlockSizeLong();
    			return sdTotalSize;
    		} else {
    			@SuppressWarnings("deprecation")
    			double sdTotalSize = (double) statFs.getBlockCount()
    					* statFs.getBlockSize();
    
    			return (long) sdTotalSize;
    		}
    	}
    
    	/**
    	 * getSize()[0] is /mnt/sdcard. getSize()[1] is size of sd (example 12.0G),
    	 * getSize()[2] is used, [3] is free, [4] is blksize
    	 * 
    	 * @return
    	 * @throws IOException
    	 */
    	public static String[] getSize() throws IOException {
    		String memory = "";
    		Process p = Runtime.getRuntime().exec("df /mnt/sdcard");
    		InputStream is = p.getInputStream();
    		int by = -1;
    		while ((by = is.read()) != -1) {
    			memory += new String(new byte[] { (byte) by });
    		}
    		for (String df : memory.split("/n")) {
    			if (df.startsWith("/mnt/sdcard")) {
    				String[] par = df.split(" ");
    				List<String> pp = new ArrayList<String>();
    				for (String pa : par) {
    					if (!pa.isEmpty()) {
    						pp.add(pa);
    					}
    				}
    				return pp.toArray(new String[pp.size()]);
    
    			}
    		}
    		return null;
    	}
    
    }

    Next, I retrieve the used, free, and total space of each mount point. This is where KitKat breaks my app.

    CpuMemFragment :
    Code:
    public class CpuMemFragment extends Fragment {
    	// CPU
    	String devCpuInfo;
    	TextView tvCpuInfo;
    
    	// RAM
    	String devRamInfo;
    	TextView tvRamInfo;
    
    	// Storage
    	String devStorageA, devStorageB;
    	TextView tvStorageAName, tvStorageA, tvStorageB, tvStorageBName;
    
    	AdView adView;
    
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState) {
    
    		View rootView = inflater.inflate(R.layout.cpu_mem, container, false);
    
    		return rootView;
    	}
    
    	@Override
    	public void onActivityCreated(Bundle savedInstanceState) {
    		// TODO Auto-generated method stub
    		super.onActivityCreated(savedInstanceState);
    
    		// *** CPU ***
    		//
    		devCpuInfo = readCpuInfo();
    		//
    		// #################################
    
    		// *** RAM ***
    		//
    		devRamInfo = readTotalRam();
    		//
    		// #################################
    
    		// *** STORAGE ***
    		//
    
    		ArrayList<StorageInfo> storageInfoList = StorageUtils.getStorageList();
    
    		tvStorageAName = (TextView) getView().findViewById(R.id.tvStorageAName);
    
    		tvStorageBName = (TextView) getView().findViewById(R.id.tvStorageBName);
    
    		if (storageInfoList.size() > 0) {
    
    			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
    					&& !storageInfoList.get(0).internal) {
    				kitKatWorkaround(0);
    			}
    			tvStorageAName.setText(storageInfoList.get(0).path);
    
    			devStorageA = StorageUtils.getReadableFileSize(
    					(StorageUtils.getUsedSpace(storageInfoList.get(0).path)),
    					true)
    					+ "/"
    					+ StorageUtils.getReadableFileSize((StorageUtils
    							.getTotalSpace(storageInfoList.get(0).path)), true);
    
    			if (storageInfoList.size() > 1) {
    				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
    						&& !storageInfoList.get(0).internal) {
    					kitKatWorkaround(1);
    				}
    				tvStorageBName.setText(storageInfoList.get(1).path);
    
    				devStorageB = StorageUtils.getReadableFileSize(
    						StorageUtils.getUsedSpace(storageInfoList.get(1).path)
    								+ (StorageUtils.getUsedSpace("system/")), true)
    						+ "/"
    						+ StorageUtils.getReadableFileSize((StorageUtils
    								.getTotalSpace(storageInfoList.get(1).path)),
    								true);
    			} else {
    				devStorageB = "N/A";
    			}
    		} else {
    			devStorageA = "N/A";
    			devStorageB = "N/A";
    		}
    		//
    		// #################################
    
    		// CPU
    		tvCpuInfo = (TextView) getView().findViewById(R.id.tvCpuInfo);
    		tvCpuInfo.setText(devCpuInfo);
    		//
    		// #################################
    
    		// RAM
    		tvRamInfo = (TextView) getView().findViewById(R.id.tvRamInfo);
    		tvRamInfo.setText(devRamInfo);
    		//
    		// #################################
    
    		// STORAGE
    		tvStorageA = (TextView) getView().findViewById(R.id.tvStorageA);
    		tvStorageA.setText(devStorageA);
    
    		tvStorageB = (TextView) getView().findViewById(R.id.tvStorageB);
    		tvStorageB.setText(devStorageB);
    		//
    		// #################################
    
    		// Look up the AdView as a resource and load a request.
    		adView = (AdView) getActivity().findViewById(R.id.adCpuMemBanner);
    		AdRequest adRequest = new AdRequest.Builder().build();
    		adView.loadAd(adRequest);
    	}
    
    	@Override
    	public void onPause() {
    		if (adView != null) {
    			adView.pause();
    		}
    		super.onPause();
    	}
    
    	@Override
    	public void onResume() {
    		super.onResume();
    		if (adView != null) {
    			adView.resume();
    		}
    	}
    
    	@Override
    	public void onDestroy() {
    		if (adView != null) {
    			adView.destroy();
    		}
    		super.onDestroy();
    	}
    
    	private static synchronized String readCpuInfo() {
    		ProcessBuilder cmd;
    		String result = "";
    
    		try {
    			String[] args = { "/system/bin/cat", "/proc/cpuinfo" };
    			cmd = new ProcessBuilder(args);
    
    			Process process = cmd.start();
    			InputStream in = process.getInputStream();
    			byte[] re = new byte[1024];
    			while (in.read(re) != -1) {
    				System.out.println(new String(re));
    				result = result + new String(re);
    			}
    			in.close();
    		} catch (IOException ex) {
    			ex.printStackTrace();
    		}
    		return result;
    	}
    
    	public static synchronized String readTotalRam() {
    		String load = "";
    		try {
    			RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
    			load = reader.readLine();
    			reader.close();
    		} catch (IOException ex) {
    			ex.printStackTrace();
    		}
    		return load;
    	}
    
    // An attempt to workaround KitKat changes according to android documentation
    	public void kitKatWorkaround(int index) {
    		String path1 = Environment.getExternalStorageDirectory().getPath();
    
    		if (index == 0) {
    
    			tvStorageAName.setText(path1);
    
    			devStorageA = StorageUtils.getReadableFileSize(
    					(StorageUtils.getUsedSpace(path1)), true)
    					+ "/"
    					+ StorageUtils.getReadableFileSize(
    							(StorageUtils.getTotalSpace(path1)), true);
    		}
    		if (index == 1) {
    			tvStorageBName.setText(path1);
    
    			devStorageB = StorageUtils.getReadableFileSize(
    					StorageUtils.getUsedSpace(path1)
    							+ (StorageUtils.getUsedSpace("system/")), true)
    					+ "/"
    					+ StorageUtils.getReadableFileSize(
    							(StorageUtils.getTotalSpace(path1)), true);
    
    		}
    	}
    }

    The suspected error is right here in the logcat:

    Caused by: libcore.io.ErrnoException: statvfs failed: EACCES (Permission denied)

    is there any alternative to retrieving sdCards sizes, or should I simply display a dialog stating that I have no control over Google's decision in Android 4.4, and apologize for the inconvenince?
    Also, while I'm at it: On some models of Android devices, the sizes of the SDcard are all out of whack. Some showing more used space than total, and innacurate readings. This seems to only happen with internal/emulated storage. What is the most accurate way of getting sizes of all SDcard locations?

    Thank you kindly for your time, Happy Coding!