Runtime Permissions, Files, and ACTION_SEND
There has been pressure over the past couple of years for developers to
move to content://
Uri
values instead of file://
Uri
values,
particularly for things like ACTION_SEND
. Android 6.0’s runtime
permission system increases that pressure a bit further.
READ_EXTERNAL_STORAGE
is a dangerous
permission on Android 6.0,
and therefore is subject to the runtime permission system if your
targetSdkVersion
is 23 or higher. This does not directly affect
the sending app in an ACTION_SEND
scenario, as that app could
be writing to getExternalFilesDir()
or getExternalCacheDir()
,
neither of which require any permissions as of Android 4.4. However,
those locations are app-specific, and so while the sender’s locations
are accessible by other apps handling ACTION_SEND
, the recipient needs
to have READ_EXTERNAL_STORAGE
. Some developers will be trying to
move away from having such a permission, as they have to prompt users
for it.
Worse, there is a corner case that will trip up many implementers
of ACTION_SEND
. Suppose the following occurs:
-
The user installs the recipient app, but does not run it, or does not do anything when running it that would trigger
requestPermissions()
forREAD_EXTERNAL_STORAGE
-
The user then uses some other app’s
ACTION_SEND
capability to try to send some content to the recipient app, in the form of afile://
Uri
pointing to a file on external storage -
The recipient app is buggy, by not handling runtime permissions properly in its
ACTION_SEND
-handlingActivity
, believing (incorrectly) that the permission must already have been granted by this point
Stack Overflow user Daniele B ran into this, where Gmail and Hangouts were recipient apps with the bug. If Google can’t get this right, many other developers won’t get it right either.
If you are implementing an ACTION_SEND
-handling Activity
, and
you plan on supporting file://
Uri
values, you need to include
the appropriate runtime permission checks in that Activity
.
However, if you are the sender of ACTION_SEND
Intents
, this is a
fine reason to move off of file://
Uri
values and use content://
Uri
values instead. This eliminates the whole READ_EXTERNAL_STORAGE
requirement in the first place.
FileProvider
provides a canned ContentProvider
implementation for serving up files.
However:
-
The documentation is wrong, claiming that
<external-path>
points togetExternalFilesDir()
, when it really points toEnvironment.getExternalStorageDirectory()
. Since I filed the issue two years ago, and it is still outstanding, it is unlikely that Google will ever fix the documentation. -
Many recipients of
ACTION_SEND
will incorrectly assume that aUri
is a file and attempt to get a local filesystem path fromMediaStore
. Such developers are misguided, as there is no requirement that theUri
be fromMediaStore
, let alone have a filesystem path that the recipient can access (e.g., the file is on removable storage on Android 4.4+). However, you as the sender have to put up with misguided developers. Some apps will behave better if you subclassFileProvider
and put anull
DATA
column in the results returned fromquery()
. MyLegacyCompatCursorWrapper
can help with this, as seen in this sample app covered in this really nifty book.
We are likely to stumble over these sorts of cases from time to time
over the next year, as we collectively apply the runtime permissions
system to our apps and see where things go haywire. When interacting
with other apps, the fewer dangerous
permissions you can require
them to have, the better off you will be in general.