Scoped Storage Stories: Durable Access
Android 10 is greatly restricting access to external storage
via filesystem APIs. Instead, we need to use other APIs to work with content.
This is the second post in a series where we will explore how to work with those
alternatives, starting with the Storage Access Framework (SAF).
So, you used ACTION_OPEN_DOCUMENT or ACTION_CREATE_DOCUMENT to get a Uri
that you can use to read and/or write some content. Great!
You got that Uri in the scope of some Activity. Perhaps you called startActivityForResult()
directly on the Activity. Or, perhaps you called startActivityForResult()
on some Fragment which, in turn, is managed by some Activity.
The good news is that your Activity should be able to work with that Uri,
even across configuration changes.
The bad news is that your rights to that Uri content end there, by default.
Using the Uri in Other Components
You might try using that Uri from other components in your app. Perhaps you
stick that Uri in some shared repository and have another Activity get that
Uri and try using it. Or, perhaps you try passing the Uri to some Service,
or try using it via WorkManager.
You will find out fairly quickly that your Uri rights vanish. You have access
to the content identified by the Uri from the Activity that receives the
Uri, and that���s it.
You can pass along those rights to other components of your app, or even
components of other apps. To do that, though, you need to:
Pass the Uri in the ���data��� facet of an Intent
Add Intent.FLAG_GRANT_READ_URI_PERMISSION and/or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
to that Intent
Use that Intent to start the other component (e.g., startActivity(),
startService())
val intent = Intent(this, TheOtherActivity::class.java)
.setData(theUri)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)
The Intent flags indicate that you want to pass along the read or write
rights that your component has to the component that you are starting.
Note that this works for any content Uri, not just those from the Storage
Access Framework. ACTION_GET_CONTENT, for example, works the same as does
ACTION_OPEN_DOCUMENT in terms of these component-level access rights.
Getting Long-Term Access
Using the Intent flags has clear limitations:
Not everything offers you a place to put those flags (e.g., WorkManager)
Eventually, your process gets terminated, at which point all of your rights
lapse
For Storage Access Framework Uri values, you can request long-term access
to the content via takePersistableUriPermission() on a ContentResolver.
You just pass in the Uri along with
Intent.FLAG_GRANT_READ_URI_PERMISSION and/or Intent.FLAG_GRANT_WRITE_URI_PERMISSION.
This will give you the ability to work with that Uri not only between components,
but between process invocations. You can save the String representation of
the Uri somewhere (e.g., SharedPreferences) and use it again in the future.
However, bear in mind that there is no guarantee that the underlying content
itself is durable. The user might use some other app to move or delete that
content, at which point your persisted Uri becomes pointless. You will need to
be able to deal with the FileNotFoundException and related forms of IOException
that you will get when this sort of thing occurs.
In addition to takePersistableUriPermission(), there is:
releasePersistableUriPermission(), to say that you no longer need durable
access; and
getPersistedUriPermissions(), to retrieve information about all outstanding
persisted permissions that you took using takePersistableUriPermission() and
have not released using releasePersistableUriPermission()
What Else Is There?
Previously, I covered
the basics of using the Storage Access Framework.
Upcoming posts in this series will include:
The role of DocumentFile
How to work with document trees from ACTION_OPEN_DOCUMENT_TREE
And more!


