Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if the device has Low Storage on Android Oreo API 26 and Above

How can I check if the device has low storage on Android 8 Oreo. I saw in the Android Documentation that the Intent.ACTION_DEVICE_STORAGE_LOW is deprecated in API 26.

This constant was deprecated in API level 26. if your app targets O or above, this broadcast will no longer be delivered to any BroadcastReceiver defined in your manifest. Instead, apps are strongly encouraged to use the improved getCacheDir() behavior so the system can automatically free up storage when needed. - Android Documentation

They are encouraging me use getCacheDir() instead.

But I don't understand much of it, as getCacheDir() seems to return the system cache directory path as a FILE object, which can only be used to clear cache or some such.

But I need to check whether the device is running low on device storage. I hope someone will help me in this

like image 921
Arun K Babu Avatar asked Nov 15 '17 14:11

Arun K Babu


1 Answers

See Android's (AndroidX work) StorageNotLowTracker implementation for an example of how to receive system broadcasts when storage becomes low or OK.

Note that this is the implementation used by AndroidX work when using a 'storage not low constraint'. It uses deprecated intent filter broadcast actions, but it still works today.

I have created a similar implementation (not shared in this answer) that can be registered, unregistered and has two callbacks: on storage low and on storage OK.

See StorageNotLowTrackerTest for an example of how to test this.


Old answer kept for reference below

As correctly stated in the question, the API 26 Intent.ACTION_DEVICE_STORAGE_LOW is deprecated and Context#getCacheDir() is advised to be used instead to free up space from you application's cache.

There are multiple problems with this (enumerated below), but first: note that it is good practice to keep cache 'reasonably small' (e.g. 1 MB), I quote:

getCacheDir()

Returns a File representing an internal directory for your app's temporary cache files. Be sure to delete each file once it is no longer needed and implement a reasonable size limit for the amount of memory you use at any given time, such as 1MB.

Caution: If the system runs low on storage, it may delete your cache files without warning.

(source)

So, there are three problems here:

  1. We should clear the cache, but it is probably already reasonably small (e.g. 1 MB), so clearing it will probably not free enough space for the free storage to become OK again (similar to the also deprecated Intent.ACTION_DEVICE_STORAGE_OK that previously could be used for this)
  2. As quoted, the cache quite possibly has already been cleared by the system, because the storage is low and the system may clear your application's cache if it so decides. Therefore, clearing it yourself possibly does not free up any storage.
  3. The documentation does not specify at all how to actually detect if the device is low on storage.

So, clearing the cache doesn't seem to help, so I won't go into the details of how to do that.

However, as per this answer, we could assume that at 10% free storage the system enters the low storage state that we want to detect. This number is Android's default, but there's little preventing a device manufacturer (or ROM developer) from changing it, according to the linked answer.

At this point, to me, this 10% is a magic number and I'd like to know if I can determine this threshold programmatically. If you know how, please edit my answer, post an answer yourself or comment on my answer.

To do this using getCacheDir(), you could use the following:

Java, from a Context (e.g. Activity):

File cacheDir = getCacheDir();
if (cacheDir.getUsableSpace() * 100 / cacheDir.getTotalSpace() <= 10) { // Alternatively, use cacheDir.getFreeSpace()
  // Handle storage low state
} else {
  // Handle storage ok state
}

Kotlin, from a Context (e.g. Activity):

if (cacheDir.usableSpace * 100 / cacheDir.totalSpace <= 10) { // Alternatively, use cacheDir.freeSpace
  // Handle storage low state
} else {
  // Handle storage ok state
}

Now, whether to use the usable space or free space, that's not entirely clear to me. The difference is described here.

Diving into the Android source I found a system service, that I cannot access in my code, that checks for low storage: DeviceStorageMonitorService. It gets its lowBytes variable from StorageManager#getStorageLowBytes, which I cannot access either. If that would be possible in some non-hacky way, that would be a way to get the low storage bytes threshold. There you see the source uses getUsableSpace(), so that's why I chose that instead of getFreeSpace() too for my code snippets.

like image 196
Erik Avatar answered Nov 11 '22 00:11

Erik