The Storage Situation: External Storage

Back in 2014, I wrote a series of blog posts to try to clear up confusion around where you can read and write files in Android. I updated them in 2017 to reflect changes in Android… but Android keeps changing.

This post is an updated edition of the 2017 post on external storage.


Working with files on the filesystem in Android is seriously confusing. It was confusing in 2008, and it has only gotten more confusing as the years rolled on. There are countless Stack Overflow questions and the like where they clearly do not quite grok the various pieces of Android’s storage model.

This is the second post in a series covering this storage model, to help clarify what is going on. Yesterday, we looked at internal storage. Today, we will look at external storage.

What Your Users Think “External Storage” Means

Many Android device users will have no idea what “external storage” means. There is nothing in the device UI that will necessarily use that term. At best, your users will think that an SD card is external storage. That’s not quite right. Except on Android 4.4+, in which case it is only partially right.

Did I mention that this stuff is confusing?

What Google Thinks “External Storage” Means

The Android SDK documentation has this to say in terms of a definition of “external storage”:

Every Android device supports a shared “external storage” space that you can use to save files. This space is called external because it’s not guaranteed to be accessible—it is a storage space that users can mount to a computer as an external storage device, and it might even be physically removable (such as an SD card).

In the halcyon days of yesteryear, there was a single volume known as “external storage”, and it was effectively defined as “the stuff that shows up when the user plugs their device into a computer using a USB cable”. Even that wasn’t completely accurate, as some manufacturers would also allow access to their devices’ removable media via the USB cable as well. And Android 4.4 added yet more wrinkles in terms of removable media… which is why removable media gets its own blog post tomorrow. Android 10 further “upset the apple cart” with all of this.

For the purposes of this blog post – and to line up with what most other written material will refer to – “external storage” is defined as the directory tree returned by Environment.getExternalStorageDirectory().

The Many Paths Under Which External Storage is Stored

For us as developers, the actual path under which this magical “external storage” was accessed varied over the years, from /sdcard, to locations under /storage, to /mnt/shell/emulated/0, then back to locations under /storage (e.g., /storage/emulated/0). And just as secondary users of an Android 4.2+ tablet get their own internal storage, they get their own external storage, with its own root directory.

Hence, as I mentioned previously:

NEVER HARDCODE PATHS

Use various methods to get the base directory to work from… though this too has gotten more complicated over the years.

The Many APIs for Finding External Storage Locations

In the beginning, everyone used Environment.getExternalStorageDirectory(), which pointed to the root of external storage. This led to external storage being just a big basket of random content.

Later, Google offered more organization:

  • getExternalFilesDir() and getExternalCacheDir() on Context, pointing to an application-specific directory on external storage, one that would be deleted when the app is uninstalled

  • Environment.getExternalStoragePublicDirectory(), for centralized places to store well-known file types, like photos and movies

Note that the Context methods have plural forms on Android 4.4+ (getExternalFilesDirs() and getExternalCacheDirs()), which ties into removable media, which we will get into more tomorrow.

And note that on Android 10, the methods on Environment are officially deprecated and are no longer accessible by default — more in this in a bit.

External Storage and Permissions

Just as the location – physically and logically – of external storage keeps changing, so does our ability to work with it.

Originally, apps could do whatever they wanted.

Android 1.5 added the WRITE_EXTERNAL_STORAGE permission, which apps had to hold to be able to write files to external storage. That way, the user would be informed at install time that the app intended to modify external storage contents. Any app could still read from external storage, though.

Android 4.4 started enforcing a READ_EXTERNAL_STORAGE permission, so you cannot even read from external storage if you do not hold some permission. Note that WRITE_EXTERNAL_STORAGE implies READ_EXTERNAL_STORAGE, so you only need one of those, not both. And, for getExternalFilesDir() and getExternalCacheDir(), you do not need either of those permissions – you can read and write in those directories with impunity. Android now has an android:maxSdkVersion attribute on <uses-permission>, specifically so that you can drop WRITE_EXTERNAL_STORAGE if you no longer need it, because you are only working with getExternalFilesDir() and getExternalCacheDir().

Android 6.0 made developers have to start asking for READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE have a via runtime permissions (e.g., checkSelfPermission(), requestPermissions()). That is because these permissions have a protectionLevel of dangerous.

What Android 10 Did

By and large, external storage is no longer accessible by default.

You still have access to getExternalFilesDir(), getExternalCacheDir(), and getExternalMediaDir() methods on Context and their associated filesystem paths. You do not need any permissions to work in those directories. However, they are in an inconvenient location for the user (Android/data/.../files/, where ... is your application ID). Plus, the content in those directories is removed when your app is uninstalled.

While there are ways of working around this limitation for Android 10, those workarounds are slated to be removed with 2020’s Android release.

This blog post has much more detail on what Android 10 did to external storage.

Poking Around External Storage

As a developer, assuming that you can find where external storage really resides for your version of Android, you have unfettered access to it from the Device File Explorer in Android Studio, for both emulators and production devices.

You can also use a USB cable, much like your users will use. However, bear in mind that what is presented to the USB interface is not what is on external storage… but, instead, is what has been indexed on external storage in the MediaStore. Hence, unless you take steps to ensure that new files that you create get indexed, they may not be immediately visible.

Under the covers, Android is using the Media Transfer Protocol for its USB communications. This enables a lot more flexibility than does Mass Storage Mode (a.k.a., what thumb drives use) that Android used originally. However, some MTP clients may cache directory listings, so even after you get your file indexed by MediaStore, an already-attached client may need to be refreshed.

How Do I Secure My Files on External Storage?

In general, you don’t. The user has complete access to external storage. Also, other apps can get to external storage if they hold the right permissions or the user chooses to grant them access to content (e.g., via ACTION_OPEN_DOCUMENT from the Storage Access Framework).

One thing that you can do is use something like Facebook’s Conceal. This encrypts your files on external storage, but uses a generated encryption key kept on internal storage. From a security standpoint, the net effect is to make external storage closer to internal storage in terms of read access. Note, though, that Conceal cannot prevent other apps, or the user, from deleting your files on external storage, or perhaps trying to write to them and corrupting the files as a result.

Should I Hardcode Paths in My App?

No.

This series includes:


Interested in learning Kotlin? Check out the Klassbook for Kotlin language lessons that you can run right in your browser!