The Death of External Storage: The End of the Saga(?)
If Q Beta 4 really does have the final APIs, then we may now have the final implementation of scoped storage. While external storage as we know it is still going away, it will not be for a while, and the user experience should be reasonable.
So, let’s review where we are now, with another set of fictionally-asked questions (FAQs):
What Are the Options?
Apps can either have normal or legacy storage.
With legacy storage, everything behaves as it did in Android 4.4 through 9.0:
You can use
getExternalFilesDir()and similar directories without permissions
You can work with the rest of external storage if you hold
Without legacy storage, apps still can use
getExternalFilesDir() and similar directories without permissions.
However, the rest of external storage appears to be inaccessible via filesystem APIs. You can neither read
nor write. This includes both files created by other apps and files put on the device
by the user (e.g., via a USB cable).
It is conceivable that there are some types of content that are still visible via the filesystem, as the documentation has:
An app that has a filtered view always has read/write access to the files that it creates, both inside and outside its app-specific directory
So, I cannot rule out scenarios where the app can work outside of
and similar directories via the filesystem APIs. I just have not found one yet.
What Changed From Q Beta 3?
Q Beta 3 also had two modes: legacy and sandboxed. Apps with sandboxed external
storage could read and write everywhere on external storage… because they were
not working with the real external storage. Instead, they would read and write
from a sandbox. While this allowed existing code to keep working, it was costly
from a user experience standpoint, as many users would not know to wander
Android/sandboxes/ directory to find an app’s sandboxed edition of
Now, instead of apps having a “sandboxed” separate bit of external storage, they have a “filtered” view of the real external storage.
What Changed From Q Beta 1 and 2?
Too much changed to list here. Can’t we focus on more pleasant topics?
What is the User Experience?
Whether apps have normal or legacy external storage does not matter to the user,
to a large degree. Files show up wherever they would have shown up originally.
This is particularly important for apps with a lower
targetSdkVersion that may
never get updated — users can use those apps the same way they have for
What Am I Supposed to Use, Then?
You are welcome to continue using
if you were using those before.
However, the Storage Access Framework (e.g.,
ACTION_OPEN_DOCUMENT) is the primary way
that apps should work with user-supplied content.
Apps that have a focus on media — audio, video, and images — can use
MediaStore. Note, though, that you need
READ_EXTERNAL_STORAGE to be able
to see other apps’ content in the
One thing that you are not supposed to use is an
file scheme. You will not be able to read files written by other apps, so
if you get a
Uri like that, probably it is useless to you. The technique
that I wrote about previously,
<activity-alias> to support
file only on older devices, should still
I Don’t Like Change — How Do I Stick With What Worked Before?
For Android Q, you can add
android:requestLegacyExternalStorage="true" to your
<application> element in the manifest. This opts you into the legacy storage
model, and your existing external storage code will work.
Technically, you only need this once you update your
Apps with lower
targetSdkVersion values default to opting into legacy storage
and would need
android:requestLegacyExternalStorage="false" to opt out.
What Happens Next Year?
The documentation still has:
Scoped storage will be required in next year’s major platform release for all apps, independent of target SDK level.
IMHO, this is unwise. Saying that it is required for
targetSdkVersion 29 and
higher is reasonable. Saying that it is required for all
values means that lots of legacy apps will crash, as while they hold
they would be ineligible to write to previously-valid locations.
My hope is that Android R will “only” deprecate and ignore
while setting the defaults to be:
targetSdkVersion 28and older
That ensures the maximum compatibility with legacy apps while still enforcing the new rules for actively-maintained apps.
It is unlikely that Android Q itself will change, though I cannot rule out an Android 10.1 (Q MR1) that messes with this stuff.
So, by August 2020 or thereabouts — whenever Android R ships — you will need to adapt to the new normal.
The idea is that you start adapting now. For some apps, switching to the
Storage Access Framework will be easy. For some apps, it will be painful.
Do not wait until 2020. Start migrating your apps now to using the alternative
approaches. The Storage Access Framework (mostly) works back to Android 4.4, for
example, and so many apps will have access to that set of
You can create a dedicated build type or product flavor where you set
android:requestLegacyExternalStorage="false" and opt out of the legacy storage
support. There, you can see what breaks and start creating plans to fix it.
Hey, Why Am I Seeing Deprecation Warnings?
If your code refers to
Environment.getExternalStoragePublicDirectory(), you will see that they are
deprecated. They still work, but the deprecation warning is yet another nudge
to remind you that you need to stop using those.
Once you no longer have the legacy storage model, those directories are unusable, which (presumably) is why they are deprecated.
How Can My Library Know What To Do?
In a library, you do not control whether the app is in normal or legacy mode.
If you need different code for those two cases, you can call
Environment.isExternalStorageLegacy(), which will return
true if the app
is in legacy mode,
What Happens When the App is Uninstalled?
Any files that you wrote to external storage in
getExternalFilesDir() get removed,
If you were thinking of switching your
Environment.getExternalStoragePublicDirectory() code to use
that will work, but the cost is that your files go away when the app is uninstalled.
For files that are owned by the user and should remain after your app is removed,
use the Storage Access Framework or
So, There Is Nothing I Need to Worry About Today?
Well, there may be. Some
script-kiddie workarounds to avoid existing limits may
cause your app to break on Android Q. Here are two examples:
Back in Android 7.0,
Google added logic to
StrictMode to see if you have a
Uri in your
that has the
file scheme. If it does, and you use that
Intent for something
startActivity(), your app would crash with a
The right solution is to use
FileProvider or otherwise get a
Uri. However, some developers elected to reconfigure
StrictMode to block
that check and prevent the exception.
Technically, that hack still works, in that you should not crash with the exception.
However, apps that opt out of the legacy filesystem support cannot access your
file. So they crash when they try to use your
and your users lose whatever functionality you were trying
to offer by starting that third-party activity.
Some developers, particularly for
ACTION_PICK from the
MediaStore and read the
DATA column to try to get a filesystem path
corresponding to the picked content. That has not been reliable in quite some
time, but I am sure that some developers are still using it.
Well, in Android Q, the
DATA column is blocked, and you will not be able to get
You will need to use the
Uri that you get as intended, with a mix of
Is There Anything Else? This Is Getting Rather Long.
Keep an eye out for future Q beta releases, as while the API is supposed to be stable, there still might be functionality changes.