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() for READ_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 a file:// Uri pointing to a file on external storage

  • The recipient app is buggy, by not handling runtime permissions properly in its ACTION_SEND-handling Activity, 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 to getExternalFilesDir(), when it really points to Environment.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 a Uri is a file and attempt to get a local filesystem path from MediaStore. Such developers are misguided, as there is no requirement that the Uri be from MediaStore, 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 subclass FileProvider and put a null DATA column in the results returned from query(). My LegacyCompatCursorWrapper 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.