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_NAME
andSIZE
— 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
.