A Uri Is Not (Necessarily) a File
Google is advising developers to stop sending file:/// Uri values to other apps,
whether via the data field in an Intent (e.g., ACTION_VIEW) or via an extra
(e.g., EXTRA_STREAM with ACTION_SEND). There is no guarantee that the other app
will have the ability to access that file, and making the file MODE_WORLD_READABLE is
not great from a security standpoint.
Instead, you will increasingly need to handle the receipt of content:// Uri values.
Ideally, nowadays, those Uri values are “openable”. You can perform the following
operations on such Uri values:
-
Open them via
openInputStream()(and occasionallyopenOutputstream()) onContentResolver -
Find their MIME type via
getType()onContentResolver -
Find out their
DISPLAY_NAMEandSIZE— theOpenableColumns— via aquery()on theUri(again, using aContentResolver, or in theory aCursorLoader)
FileProvider from the Android Support library serves openable Uri values from files
on internal and external storage. My StreamProvider
serves openable Uri values from
more locations, including assets and raw resources.
The Storage Access Framework
is designed around openable Uri values. And you can create your own
ContentProvider that serves up openable Uri values.
The legacy convention was that a content:// Uri might not be openable directly. Instead,
it pointed to a database row, retrievable via query(), and you would look in the _DATA column
for how to access the actual data. Some providers no doubt continue to use this pattern. The
rules for what the _DATA column would be were not well documented, but by convention they
tended to be a path to a file. The problem is that this runs afoul of Google’s current guidance,
as there is no guarantee that other apps can access such a file.
Do not blindly assume that if you get a content:// Uri that it is for the _DATA pattern.
If you query() the Uri and do not get a _DATA value back, try using the Uri directly
with ContentResolver. Or, perhaps do the inverse: try to open a stream on the Uri, and
if that fails, then see if the _DATA pattern is in play.
An increasing percentage of the content:// Uri values you get will be openable, as more
and more developers adopt FileProvider, the Storage Access Framework, and other things that adopt
the openable-Uri pattern. Ignoring such Uri values, such as by crashing or rejecting a request
when you do not find a _DATA value, will diminish the value of your app.
A hat tip to Stack Overflow users iforce2d
and matiash for
spurring me to do more analysis of some prominent Android apps that crashed when accessing
FileProvider.

