13

I use Android Storage Access Framework to access some files on SD card. It is important for me to persists this permissions, to be able to edit files after reboot of device.

So according to Storage Access Framework documentation I use Persist permissions that gives the user continued access to the files through your app, even if the device has been restarted.

However, after some time I noticed that for some users permissions were somehow revoked. Because when I am trying to use SAF for writing I get android.system.ErrnoException: open failed: EACCES (Permission denied) after that exception I check what permissions do I have using mContext.getContentResolver().getPersistedUriPermissions() however it return empty list. I am sure that user provide me with the right permissions and that permissions are for the currently installed Sd card, because I track this actions in the database.

Currently, in this case I am showing document picker so user can provide me one more time with new permissions, but asking user to perform same action often is not user friendly.

What can cause the revoke of permissions? And how I can prevent this revoke?

I have tested on all my devices multiple reboots of the phone, changing time, removing inserting sd card, but was not able to lost any of saf permissions.

I have next code for obtaining permissions:

private void openDocumentTree() {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        startActivityForResult(intent, REQUEST_CODE);
    }

public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE) {
            Uri treeUri = data.getData();
            final int takeFlags = data.getFlags() & ( 
                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            mContext.getContentResolver().takePersistableUriPermission(treeUri,
                    takeFlags);

                    //this is my internal class for saving and restoring tree uri permissions.
                    final Permission permission = Permission.from(treeUri);
                    mPermissionDao.save(permission);
        }
    }
Rostyslav Roshak
  • 3,859
  • 2
  • 35
  • 56
  • 1
    "I use Android Storage Access Framework to access some files on SD card" -- no, you are using SAF to work with documents. Whether those documents are stored is up to the user, not you. They could be managed by any provider (Google Drive, Dropbox, etc.). "I am sure that user provide me with the right permissions and that permissions are for the currently installed Sd card, because I track this actions in the database" -- the documents that the user chooses do not have to be on an SD card. "What can cause the revoke of permissions?" -- perhaps the user deleted the file from the provider. – CommonsWare Sep 14 '15 at 11:44
  • I am using Saf only to work with files on the SD cards, because of specific of app, there is only reason to work with with external storage provider. I check that tree uri is in format of external storage. – Rostyslav Roshak Sep 14 '15 at 11:51
  • Perhaps the user deleted the file from the provider - it is not possible, because, I received the tree uri that represents the root path of external storage. There is no way to delete it. Also, as I mention, I was not able to create new files (it was throwing `android.system.ErrnoException`) and `getContentResolver().getPersistedUriPermissions()` returned empty list. So user somehow revoked permissions. – Rostyslav Roshak Sep 14 '15 at 11:54
  • 3
    "I check that tree uri is in format of external storage" -- since there is not documented "format of external storage", and since device manufacturers and custom ROM builders are welcome to change Android, your approach is not reliable. "I received the tree uri that represents the root path of external storage" -- by your own admission, you do not know that, since you cannot reproduce the problem. The user is welcome to choose whatever document tree that the user wants, and you have no means of determining reliably whether or not it is "the root path of external storage". – CommonsWare Sep 14 '15 at 11:57
  • Yes, your are right that approach is not relaible, but it is the only way how to get access to the SD card after Android 4.3 update. – Rostyslav Roshak Sep 14 '15 at 12:11
  • Then you will need to change your app to take into account that the user can choose whatever the user wants, and that "whatever the user wants" may no longer exist at some point in the future. – CommonsWare Sep 14 '15 at 12:19

1 Answers1

9

You need additional flags when requesting ACTION_OPEN_DOCUMENT_TREE.

openDocumentTree should look like

private void openDocumentTree() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    intent.addFlags(
        Intent.FLAG_GRANT_READ_URI_PERMISSION
        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
    startActivityForResult(intent, REQUEST_CODE);
}

Review docs for FLAG_GRANT_PERSISTABLE_URI_PERMISSION and FLAG_GRANT_PREFIX_URI_PERMISSION

Peter Tran
  • 1,626
  • 1
  • 17
  • 26