The Death of External Storage: More of the Story
A month ago, I wrote a blog post about the changes in external storage on Android Q.
I intentionally skipped one finding, because it really seemed like a bug. However, apparently, it is working as intended.
So, let’s revisit what you can do with the external storage filesystem in Android Q.
And, as a reminder: unless Google changes things, these rules affect all apps,
regardless of targetSdkVersion
(except for apps already installed on a device that
gets the Q upgrade).
UPDATE 2019-05-13: Google changed things.
UPDATE 2019-06-08: With Q Beta 4 out, the story is now much simpler. I am leaving the original post here for historical reasons.
Where Can You Write?
UPDATE 2019-04-28: The behavior of Environment.getExternalStoragePublicDirectory()
is more complicated than that.
However, you can:
-
Create files in the root of external storage (
Environment.getExternalStorageDirectory()
) -
Create directories in the root of external storage
-
Create files in those directories that you created
Basically, if it already exists — such as directories that were created as part of setting up the device — you have no write access. If you create it yourself, though, you do have write access.
What Can You Read?
If you wrote it, you can read it.
However, that is it. You cannot read the contents of files or directories that were created by other means, using filesystem APIs.
What Permissions Do You Need?
None.
No, Seriously, What Permissions Do You Need?
None. This was the part that I thought was a security bug. ¯\_(ツ)_/¯
If you have targetSdkVersion 'Q'
, or if you have targetSdkVersion 28
, you can
write to the root of external storage and read back what you wrote, without any
permissions. Presumably,
this holds true for at least some older targetSdkVersion
values, but I only
tested these two.
The reason why READ_EXTERNAL_STORAGE
and WRITE_EXTERNAL_STORAGE
are deprecated
is that all of external storage — except for existing files and directories —
works like getExternalFilesDir()
:
- You do not need any permissions
- What you create is removed when the app is uninstalled or if the user clears storage for the app
What Can Other Apps Do With Your Files?
What you create and write in external storage via the filesystem is inaccessible to other apps via the filesystem, no matter where it is.
What you create in getExternalFilesDir()
will be accessible via the Storage
Access Framework, but what you create in the root of external storage is not.
As a result, anything you put in the root of external storage (or custom directories
off of the root) is private to your app, except to the extent that you expose
them via FileProvider
or something.
What About the User and a USB Cable?
Similarly, what happens here depends on which portion of the external storage filesystem that you use.
For getExternalFilesDir()
and kin:
-
Files and directories that you create are visible to the user
-
Files and directories that the user creates are visible to you
For the root of external storage:
-
Files and directories that you create are not visible to the user (
MediaScannerConnection
seems to not work for those files, andACTION_MEDIA_SCANNER_SCAN_FILE
has been deprecated with no stated replacement) -
Files and directories that the user creates are not visible to you
Even as a developer, adb
does not show app-created files in the external storage root
directory. Nor does Android Studio’s Device File Explorer.
So, What Should I Do Now?
In general, my previous recommendations still hold up:
-
Use the methods on
Context
likegetExternalFilesDir()
to find places to write, if you need to make the files available to the user via USB -
Use the Storage Access Framework
-
Use the
MediaStore
for media -
Use
FileProvider
to make your content available to other apps for outbound requests (e.g.,ACTION_VIEW
andACTION_SEND
Intent
s) -
If applicable, support
ACTION_VIEW
and/orACTION_SEND
in your manifest, so other apps can use thoseIntent
actions to send you content
If you have existing code that writes files to the root of external storage or
some custom directory off of it, that code will continue to run. However, the user
will be unable to access those files, except through your app. In general, it
will be better to migrate those to getExternalFilesDir()
, if user access to those
files is important.
It is entirely possible that there are yet other combinations, scenarios, and mysteries yet to be discovered. The key is that you should test your app early and often on Android Q.