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 occasionally openOutputstream()) on ContentResolver

  • Find their MIME type via getType() on ContentResolver

  • Find out their DISPLAY_NAME and SIZE — the OpenableColumns — via a query() on the Uri (again, using a ContentResolver, or in theory a CursorLoader)

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.