Android SAF:相对于先前授权的文件夹,DocumentsContract.getTreeDocumentId中的文件夹(磁盘上)的Uri具有不同的形式

时间:2019-08-20 09:14:59

标签: android directory permissions document storage-access-framework

我正在为我的应用程序测试存储访问框架。

在我的应用程序中,用户选择一个文件夹作为应用程序本身对文件系统的访问点。

使用此方法:

static public void openPickerForFolderSelection(Activity activity, int requestCode)
{
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); //useful?
    activity.startActivityForResult(intent, requestCode);
}

在onActivityResult方法中,以下代码被调用:

static public String takePermanentReadWritePermissions(Activity activity, Intent data)
{
int takeFlags = data.getFlags()
        &
        (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
ContentResolver resolver = activity.getContentResolver();
resolver.takePersistableUriPermission(data.getData(),takeFlags);
return data.getData().toString();

}

例如,Uri是:

content://com.android.providers.downloads.documents/tree/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Faccesspoint1

实际上,Uri恰好是通过此方法检测到的授权:

static boolean arePermissionsGranted(Activity activity, String uriString)
{
    ContentResolver resolver = activity.getContentResolver();
    List<UriPermission> list = resolver.getPersistedUriPermissions();
    for (int i = 0; i < list.size(); i++){
    Log.d("compare",uriString+" "+Uri.decode(list.get(i).getUri().toString())+" "+String.valueOf(list.get(i).isWritePermission())+" "+String.valueOf(list.get(i).isReadPermission()));
    if(((Uri.decode(list.get(i).getUri().toString())).equals( uriString)) && list.get(i).isWritePermission()&& list.get(i).isReadPermission()){
            return true;
        }

然后,当需要在所选文件夹中创建一个新文件夹时,将调用以下方法,但是在尝试创建该文件夹之前出现错误:

static public boolean createFolderInFolder(Activity activity,String parentFolderUriString,String folderName)
{
boolean result=false;
ContentResolver contentResolver;
Uri parentFolderUri=null;
String id=DocumentsContract.getTreeDocumentId(Uri.parse(parentFolderUriString));
Log.d("id ",id); 
parentFolderUri= DocumentsContract.buildTreeDocumentUri(PROVIDER_AUTHORITY,id);
try {
//error here
    DocumentsContract.createDocument(contentResolver,parentFolderUri,DocumentsContract.Document.MIME_TYPE_DIR,folderName);
            result=true;
} catch (FileNotFoundException e) {
    result =false;
}
return result;
}

我得到:

java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.docs.storagebackend.StorageBackendContentProvider from ProcessRecord{1426e0c 13943:com.example.app/u0a439} (pid=13943, uid=10439) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs

(请注意,使用

parentFolderUri=Uri.parse(parentFolderUriString); 

直接导致“无效的Uri”错误)

事实是,与在DocumentsContract.getTreeDocumentId(Uri.parse(parentFolderUriString));

上的权限不同,该权限是在不同的Uri上授予的。

这是日志:

D/compare: content://com.android.providers.downloads.documents/tree/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Faccesspoint1 content://com.android.providers.downloads.documents/tree/raw:/storage/emulated/0/Download/accesspoint1 true true

实际上,id值为:

raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Faccesspoint1

得出最终Uri值

content://com.google.android.apps.docs.storage/tree/raw%253A%252Fstorage%252Femulated%252F0%252FDownload%252Faccesspoint1

因此,如果我不希望发出任何安全异常(带有拒绝权限),我似乎也应该对此Uri的不同版本请求权限?但是似乎不可能以其他形式的uri来获得许可。

如何解决这个问题?

1 个答案:

答案 0 :(得分:0)

题外话:'intent.addFlags()'方法中不需要openPickerForFolderSelection

现在就您的主题了-我认为您只需要使用DocumentContract.buildDocumentUriUsingTree()即可获取所需的uri(当我尝试以这种方式创建文件/目录时,这至少对我有用),所以就像:

Uri oldParentUri = Uri.parse(parentFolderUriString);
String id = DocumentsContract.getTreeDocumentId(oldParentUri );
Uri parentFolderUri= DocumentsContract.buildChildDocumentsUriUsingTree(oldParentUri , id);

另一方面,我建议您使用DocumentFile API-imo更加容易。您的“ createFolderInFolder”将如下所示:

Uri parentFolderUri = Uri.parse(parentFolderUriString);
DocumentFile parentFolder = DocumentFile.fromTreeUri(context, parentFolderUri);
parentFolder.createDirectory(folderName);

然后,您可以轻松获取创建的文件夹uri并从那里进行操作。