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 anIntent
-
Add
Intent.FLAG_GRANT_READ_URI_PERMISSION
and/orIntent.FLAG_GRANT_WRITE_URI_PERMISSION
to thatIntent
-
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 usingtakePersistableUriPermission()
and have not released usingreleasePersistableUriPermission()
The entire series of “Scoped Storage Stories” posts includes posts on:
- The basics of using the Storage Access Framework
- Getting durable access to the selected content
- Working with
DocumentFile
for individual documents - Working with document trees
- Working with
DocumentsContract
- Problems with the SAF API
- A specific problem with
listFiles()
onDocumentFile
- Storing content using
MediaStore
- Reading content from the
MediaStore
- Modifying
MediaStore
content from other apps - Limitations of
MediaStore.Downloads
- The undocumented
Documents
option - More on
RecoverableSecurityException
- How to modify more metadata in
MediaStore