Count Your SAF Uri Persisted Permissions!
Now that everyone (cough) is using the Storage Access Framework (SAF), we are starting to push the boundaries of what it can and cannot do. As developers have yelped about over the years, the SAF has its limits.
And, as one of my subscribers pointed out recently, one of those limits is how many persistable permission grants you can take.
As I wrote about last year,
you can call takePersistableUriPermission()
on ContentResolver
, passing
in the Uri
that you get back from an SAF request like ACTION_OPEN_DOCUMENT
.
This indicates your interest in long-term access to the content, and typically
that will be granted (though, technically, it depends on the DocumentsProvider
).
However, there is a cap of 128 persisted permission grants that you can obtain.
UPDATE 2020-11-09: Android 11 updated that limit to 512.
So, once you set your minSdkVersion
to 30
(2025? some later year?), you will
have a bit more “elbow room” for these permissions.
That is baked into the OS in UriGrantsManagerService
in current versions of Android and
in ActivityManagerService
in older versions.
If you reach the limit, future requests fail.
I agree with the analysis in the associated issue: 128 is rather low. However, since AFAIK this limit has been there since the outset, even if it changed for Android 11 (or 12 or …), we still have to cope with the limit on older devices.
You can find out how many grants you have by calling getPersistedUriPermissions()
on a ContentResolver
. If you are near the limit, you may need to release an older
grant by means of calling releasePersistableUriPermission()
on a ContentResolver
.
That could be based on a user selection of some past document in your UI, or perhaps
chosen automatically via an LRU algorithm. For those that you release, AFAIK you would need
to ask the user to re-select the document in the SAF in order to get rights to it
again in the future.
Also, AFAIK, it is possible for the user to choose over 128 documents if
you use ACTION_OPEN_DOCUMENT
with EXTRA_ALLOW_MULTIPLE
. Hence, you might not be able to
assume that you can just spin through the returned Uri
roster and take persistable
permissions for each of them. This may not be limited to some accumulation of
grants over time — you could run into the problem right away.
Ideally, you aim to request fewer persisted permission grants. For example, if you are going to need a bunch of documents from the user that might be in the same tree, ask the user to give you access to the tree, not the individual documents in that tree.
Or, consider whether you need to change the semantics of your app’s use of selected documents. Instead of some sort of “link” action that implies that you are retaining access to the original document, you may need to offer “import” and make your own copy of the document. This takes up more disk space, but it avoids having to shuffle around permission grants to keep under the limit. Or, if you reach the limit, give the user a choice of dropping access to an older document or importing it, so you can work with the copy.
There may be other strategies as well. If you have come up with an interesting workaround, post it as a comment on the issue!