ajayel
ajayel

Reputation: 3085

Why is this code incorrectly reporting free space on my Android device?

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

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 I discover the true free space correctly?

Upvotes: 3

Views: 899

Answers (2)

Has AlTaiar
Has AlTaiar

Reputation: 4152

Try this:

StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
long bytesAvailable = (long)stat.getBlockSize() *(long)stat.getBlockCount();
long megAvailable = bytesAvailable / 1048576;
System.out.println("Megs :"+megAvailable);

getBlockCount() - return size of SD card; getAvailableBlocks() - return the number of blocks that are still accessible to normal programs

Details in this link

Upvotes: 1

blalasaadri
blalasaadri

Reputation: 6198

The reason for this behaviour is probably that in the case of your phone the SD card is not considered the main external storage device. The description of getExternalStorageDirectory() 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're probably getting some other system that really is 0.6 GB big.

Luckily, chances are pretty high that the device you're looking for is mounted in the same parent directory as the one you're getting. So first of all, I'd check whether this is the device you're looking for with something like isExternalStorageRemovable() and if not so, try other devices 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
}

This asumes, that there are only two devices mounted. In some rare cases, this may not be true (e.g. because a further storage device is mounted via USB-OTG); you'll have to find another workaround here. One idea may be to run the mount command which is described here via a process and work through those results to find the correct device. This however will become quite complicated. It also asumes that all external storage devices are mounted under the same parent directory (usually /mnt/). If that isn't the case the mount command is once again your best hope.

EDIT 1: There might be some devices which mount to /sdcard without using the /mnt directory (though that might in some cases be a reference to /mnt/sdcard in which case you'll be fine). So in those cases you'll have to use the output of mount.

EDIT 2: I checked and found, that on my phone the /mnt contains quite a few directories and I only want one of those. 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 what we can check for. Sadly the shortest method to check a files ownership within Android seems to be... Well, not very intuitive. But hopefully, this should work indepently of where exactly the various storage devices are mounted.

Upvotes: 3

Related Questions