Virtual Files FAQ (Sorta)
I largely ignored virtual files with the N Developer Previews, because what little documentation exists is self-contradictory. I couldn’t figure out why anyone would care about them, and the lack of documentation suggests that Google does not care much about them, so why worry?
But, an Ian Lake Stack Overflow answer scared me, which is why I have poked around a bit at virtual files to come up with this set of fictionally-asked questions. Hopefully, these will help frame the role of virtual files plus highlight some of the places where we have “known unknowns” regarding them.
What is a Virtual File?
A virtual file is an extension on an existing under-discussed concept
in Android: a Uri
does not have to map to anything directly
usable by arbitrary apps.
Say What, Now?
A Uri
is a largely-opaque identifier, representing some piece of content
(or, occasionally, a collection of content). Roughly speaking, you can
divide the uses of a Uri
into two groups:
-
The app holding the
Uri
needs the actual content itself, such as needing the bytes of an image to decode into aBitmap
and show in anImageView
-
The app holding the
Uri
is merely an intermediary, getting theUri
from one place (e.g.,MediaStore
) and passing it to another place (e.g., anACTION_VIEW
-supporting activity that happens to handle the content type identified by thatUri
)
A Uri
representing a virtual file can only be used in the second scenario.
You cannot get at the content of a virtual file.
Why Does the Documentation Say That We Should Get a Stream, Then?
Our primary documentation on Android 7.0’s virtual files — all five paragraphs of it — has:
The virtual files feature allows your DocumentsProvider to return document URIs that can be used with an ACTION_VIEW intent even if they don’t have a direct bytecode representation.
It also has:
Since an app cannot directly open a virtual file by using the openInputStream() method, your app does not receive any virtual files if you include the CATEGORY_OPENABLE category.
However, it then has:
Your app can retrieve the URI of the virtual file and get an input stream
Other evidence, such as puttering around the Android source code, suggests that the first two statements are correct. Besides, there are two of those, and only one contradicting it, and so majority rules, right?
Perhaps the documentation will be clarified someday.
What Can You Do With a Virtual File?
Use it with an ACTION_VIEW
Intent
. No other use cases are documented,
as far as I can tell.
What’s This About CATEGORY_OPENABLE
?
The Storage Access Framework documentation
has long suggested that you
add CATEGORY_OPENABLE
to your ACTION_OPEN_DOCUMENT
and related
Intents
.
The documentation for CATEGORY_OPENABLE
indicates that the
role of this category is to limit responses to those where you get a Uri
that supports openInputStream()
and related methods on ContentResolver
.
What is implied by the virtual files “documentation” is that if you only
need the Uri
, and not the content, you could skip CATEGORY_OPENABLE
,
and the user might have more choices of where to pick content from.
I am Using ACTION_GET_CONTENT
, So This Doesn’t Concern Me, Right?
Wrong. That’s what scared me about
Ian Lake’s Stack Overflow answer
that I mentioned earlier. According to him,
ACTION_GET_CONTENT
without CATEGORY_OPENABLE
might return a Uri
that represents a virtual file.
This is perfectly reasonable from the documentation, but I have not seen
very many examples of ACTION_GET_CONTENT
with CATEGORY_OPENABLE
, and
I have not seen where anyone ran into a case where ACTION_GET_CONTENT
returned
a Uri
that could not be opened.
Are There Other Intent Actions For Which CATEGORY_OPENABLE Applies?
ACTION_GET_CONTENT
, ACTION_OPEN_DOCUMENT
,
and ACTION_CREATE_DOCUMENT
are the three Intent
actions for which CATEGORY_OPENABLE
is documented to apply.
Beyond that, I have no idea.
In principle, any Intent
action that is documented to return a Uri
, such as ACTION_PICK
,
should be subject to the same rules. However, some developers may not have CATEGORY_OPENABLE
on their
<intent-filter>
, even though they return openable Uri
values.
Also, note that there are Uri
values other than those for virtual
files that cannot be opened. For example, the Uri
for a contact
is not openable. Hence, the standard Contacts app does not have
CATEGORY_OPENABLE
on its ACTION_PICK
activity’s <intent-filter>
.
Is Anyone Serving Virtual Files?
Until this gets better documentation, hopefully nobody is.
In reality, Google rarely adds something to Android without a vision in mind, so I would not be the least bit surprised if an app like Google Drive started offering up virtual files.
Why Would I Want to Receive a Virtual File?
If your app supports the notion of linking your own content to other content accessible to the user, expanding your scope to include virtual files may give the user more possible things to link.
However, since you do not have access to the actual content, you cannot upload that content to a server. Hence, this is only good for an on-device link feature, not an “attach”, “import”, or other verbs that implies that your app has access to the content itself. In particular, it is not useful for cases where the user might be working with your app’s data somewhere other than the original device (e.g., a Web app).
OK, So What Really Changed in Android 7.0?
A DocumentsProvider
— the source of the content that the user
browses in the Storage Access Framework UI —
can include FLAG_VIRTUAL_DOCUMENT
for the metadata on a document, to indicate that it is a virtual document
and should be filtered out for clients requesting openable Uri
values.
So, What Should I Do?
If you are using ACTION_GET_CONTENT
, and you are trying to use the
content itself (e.g., you pass the Uri
to Picasso to populate an
ImageView
), add CATEGORY_OPENABLE
to your Intent
.
Experts might start contemplating where requesting virtual files, or
possibly serving them from ACTION_GET_CONTENT
or a DocumentsProvider
,
might be useful for their apps.
Beyond that, sit tight, and wait for more documentation.
(note: it might be a long wait, so be sure to pack a lunch)