The CommonsBlog
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 yourtargetSdkVersion
is 23 or higher. This does not directly affect the sending app in an ACTION_SEND
scenario, as that app could be writing togetExternalFilesDir()
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 haveREAD_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.
Copyright © 2015 CommonsWare, LLC — All Rights Reserved