Java – Why does this code incorrectly report free space on my Android device?

Why does this code incorrectly report free space on my Android device?… here is a solution to the problem.

Why does this code incorrectly report free space on my Android device?

My Samsung X-Cover2 GT-S7710 with 32GB SD card reports the correct space in Settings > Storage, but with this code or How can I check how much free space an SD card mounted on an Android device has? is only 0.6 GB:

sdStorageDirectory = Environment.getExternalStorageDirectory().getPath();
StatFs statSd = new StatFs(sdStorageDirectory);
Log.d(TAG, "gb available " + String.valueOf((double) (statSd.getAvailableBlocks() * statSd.getBlockSize()) / 1073741824));

Why is this, and how can the real free space be correctly discovered?

Solution

The reason for this behavior may be that in your phone, the SD card is not considered the primary external storage device. getExternalStorageDirectory The description of () explains this:

Note: don’t be confused by the word “external” here. This directory can better be thought as media/shared storage. It is a filesystem that can hold a relatively large amount of data and that is shared across all applications (does not enforce permissions). Traditionally this is an SD card, but it may also be implemented as built-in storage in a device that is distinct from the protected internal storage and can be mounted as a filesystem on a computer.

So you probably get some other systems that really have a 0.6 GB size.

Luckily, there is a high probability that the device you are looking for is installed in the same parent directory as the device you are looking for. So first, I’ll check if this is the device you’re looking for, similar isExternalStorageRemovable () If not, try a different device in the same parent directory:

File sdStorage;
if(sdStorage.isExternalStorageRemovable()) {
     sdStorage = Environment.getExternalStorageDirectory();
} else {
    List<File> storages = Environment.getExternalStorageDirectory().getParentFile().listFiles();
    for(File storage : storages) {
        if(! storages.equals(Environment.getExternalStorageDirectory()) {
            sdStorage = storage;
            break;
        }
    }
}
if(sdStorage == null) {
     No non-removable storage device was found
}

Suppose only two devices are installed. In rare cases, this may not be true (for example, because another storage device is installed via USB-OTG); You have to find another workaround here. One idea might be to run the mount command, which is described as here through a process and through those results to find the right device. However, this will become quite complicated.
It also assumes that all external storage devices are installed in the same parent directory (usually /mnt/). If this is not the case, the mount command is once again your best hope.

Edit 1: There may be devices that mount to /sdcard without using the /mnt directory (although in some cases this may be a reference to /mnt/sdcard, in which case you’ll be fine). Therefore, in these cases, you must use the output of mount.

Edit 2: I checked and found that on my phone, /mnt contains a lot of directories, and I only want one of them. So I came up with this solution instead of the one above :

File sdStorage;
if(sdStorage.isExternalStorageRemovable()) {
     sdStorage = Environment.getExternalStorageDirectory();
} else {
    Process process = null;
    Scanner scan = null;
    try {
        process = new ProcessBuilder().command("ls", "-l", sdStorage.getParent()).redirectErrorStream(true).start();
        scan = new Scanner(process.getInputStream());
        while(scan.hasNextLine()) {
            String line = scan.nextLine();
            String[] parts = line.split("\\s");
            if(parts.length > 2 && parts[2].equals("sdcard_rw") 
              && ! Environment.getExternalStorageDirectory().equals(Environment.getExternalStorageDirectory().getParent() + File.seperator + parts[3]) {
                sdStorage = new File(Environment.getExternalStorageDirectory().getParent() + File.seperator + parts[3]);
                break;
            }
        } catch(IOException e) {
             Do something here
        } finally {
            if(scan != null)
                scan.close();
            if(process != null)
                process.destroy();
        }
    }
}

The background here is that all external storage devices seem to belong to the user sdcard_rw so this is something we can check. Unfortunately, the shortest way to check file ownership in Android seems to be… Well, not very intuitive. But hopefully, this should be independent of the exact installation location of the various storage devices.

Related Problems and Solutions