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!