Persistent URI permissions in Android

While working on our latest app Infinite Soundboards I found myself banging my head against the wall with permission issues. Permission issues are normally pretty trivial, just get the right permissions and I’m set right? Not the case with this instance. The exception would only occur in the app after a device reboot when you tried to play a sound. The exception was coming from the code that would try to look up a sound file based on its URI.

This little bug was particularly bothersome because the stack trace said to consult my Android manifest. Now I had checked it several times and was positive I had listed the correct permissions. So where was this issue coming from? The only place I was aware of to do any type of permissioning was the manifest, surely I had missed something.

Well it turns out that in Android URIs have what are called persistent permissions. When an Android app opens a file the android system grants that app a URI permission. These permissions only last until the device is restarted. With persistent URI permissions your app essentially takes these permissions and holds them through device restarts. That way it can access any files it has accessed before the restart. Unlike normal permissions that are set in the manifest, persistent permissions have to be set in the code. Let’s take the code from Infinite Soundboards for an example:

There are 2 parts to giving your app persistent URI permissions. First you need to set the flags for the permissions your app requires, and second you need to have your apps content resolver take those permissions.

private void launchFileBrowser(){
        final int KITKAT_VALUE =1002;
        if (Build.VERSION.SDK_INT < 19){
            intent = new Intent();
            intent.setAction(Intent.ACTION_GET_CONTENT);
        } else {
            intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
        }
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setType("audio/*");
        startActivityForResult(intent, KITKAT_VALUE);
    }

This is the method used to launch the file browser when you add a new sound to a soundboard. Here we add 2 flags to our intent if the device is running on Kitkat or above. If the device is running on anything below Kitkat we only add the second flag. The flags are FLAG_GRANT_PERSISTABLE_URI_PERMISSION and FLAG_GRANT_READ_URI_PERMISSION. The first flag allows the app to persist it’s permissions through restarts. The second flag grants the app permission to read from the URI. There is a 3rd flag that can be granted as well if your app needs it, FLAG_GRANT_WRITE_URI_PERMISSION will give your app write permissions on the URI.

        int takeFlags = intent.getFlags();
	ContentResolver resolver = getActivity().getContentResolver();
        resolver.takePersistableUriPermission(fileUri,takeFlags);

This code tells your content resolver to take the uri permissions. You’ll need the URI of the file you want the permissions on, as well as the intent you set up with the appropriate flags. In Infinite Soundboards this is handled after the file browser activity is complete. And that’s it your app can now use file URIs across device restarts.

You can check out google’s documentation for Persistent permissions here.

Infinite Sounds can be downloaded here and you can check out its source code here.