android-fragments 文档提供程序的持久性URI权限

lmyy7pcs  于 2022-11-14  发布在  Android
关注(0)|答案(1)|浏览(173)

我不确定我尝试做的事情是否可行。我有一个“代理”文档提供程序,它是指使用SAF导出其他内容别名的文档提供程序。我有一个对话框片段,它允许用户通过以下方式设置别名:显示OPEN_DOCUMENT_TREE Intent、捕获URI、授予对该URI的持久权限,然后将该URI传递给文档提供程序以显示该内容。
对话框片段可以在从Intent接收的URI上读/写,但提供程序部分不能。我总是得到的错误是:

java.lang.SecurityException: Permission Denial: reading com.android.externalstorage.ExternalStorageProvider uri

我已经阅读了很多关于使用这些持久性READ_URI_PERMISSIONS的相关问题和回复,并尝试了那些帖子中建议的所有变体。但是我无法让它工作。我开始认为持久性URI权限在提供者中是不可用的。是这样吗?我需要让提供者调用一个Intent来授予这些权限吗?也许让提供者调用主活动来访问内容。我当然更愿意直接从提供者访问内容。我还没有找到任何地方说明一旦授予持久权限,它们将被授予谁?应用程序的提供者部分是一个单独的进程吗?这是问题所在吗?
让我提供相关的代码片段:
Android信息清单. xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.connectedway.connectedsmb">

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <application
        ...
        <activity ...>
            ...
        </activity>
        <provider ...
           android:permission="android.permission.MANAGE_DOCUMENTS">
        </provider>
    </application>
</manifest>

获取URI的对话片段位于主Activity中,而使用URI的代码位于提供程序中。
主Activity的对话框片段使用以下命令调用OPEN_DOCUMENT_TREE Intent:

StorageManager sm = (StorageManager)
        getContext().getSystemService(Context.STORAGE_SERVICE);
    StorageVolume sv = sm.getPrimaryStorageVolume();
    Intent intent = sv.createOpenDocumentTreeIntent();
    intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    onCreateResultLauncher.launch(intent);

具有aswsociatd onActivityResult回调的“onCreateResultLauncher”类为:

ActivityResultLauncher<Intent> onCreateResultLauncher =
        registerForActivityResult
        (new ActivityResultContracts.StartActivityForResult(),
         new ActivityResultCallback<ActivityResult>() {
            @Override
                public void onActivityResult(ActivityResult result) {

                if (result.getResultCode() == Activity.RESULT_OK) {
                    Intent data = result.getData();
                    Uri uri = data.getData() ;
                    getActivity().grantUriPermission(
                        getActivity().getPackageName(), uri,                                                      
                        Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
                        Intent.FLAG_GRANT_READ_URI_PERMISSION);

                    final int takeFlags =
                        data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION);

                    getContext().getContentResolver().takePersistableUriPermission(
                        uri, takeFlags);

                    // PASS THE URI OFF TO THE PROVIDER
                    }
                }
            }
        });

在对话框片段中,我测试URI以查看是否可以读取它:

DocumentFile file = DocumentFile.fromTreeUri(getContext(), uri);
    if (file.canRead())
        System.out.println ("Can Read");

我们总是可以读取。现在提供程序尝试访问URI,如下所示:

@Override
        public Cursor queryDocument(String documentId, String[] projection)
        throws FileNotFoundException {

        // convert the documentId passed in to a uri.  The conversion process
        // results in the same URI as that received by the OPEN_DOCUMENT_TREE 
        // intent invoked and tested above.

        DocumentFile file = DocumentFile.fromTreeUri(getContext(), uri);

        if (file.canRead())
            System.out.println ("Can Read");

提供程序没有读取权限。因此,当提供程序最终对URI执行查询时,它将失败,并显示上面显示的SecurityException。
问题是:

  • 这是否在体系结构上得到支持?
  • 如果是这样的话,有人能在上面的片段中看到任何明显的错误吗?
  • 如果没有,谁能想出一个方法来解决架构限制?

感谢您阅读全文。

pdsfdshx

pdsfdshx1#

我已经能够解决这个问题了。我不得不改变一些东西,但主要的问题是我如何构建我传递的URI。下面是一些最相关的改变:
应用程序读入要在应用程序的自定义文档提供程序中公开的URI。启动

Intent intent = sv.createOpenDocumentTreeIntent();
    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);

然后启动Intent。在用户选择要导出到自定义文档提供程序的目录后,应用将收到结果回调:

Uri uri = data.getData() ;
    ContentResolver cr = getContext().getContentResolver();
    cr.takePersistableUriPermission
        (uri,
         Intent.FLAG_GRANT_READ_URI_PERMISSION |
         Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

然后,URI被传递给文档提供程序,文档提供程序为其创建一个根。文档提供程序不需要显式地获取任何权限。它只需要确保生成正确的URI。例如:

Uri treeUri = Uri.parse(otgMap.getPath().getPath());
    String externalDocId = // DocId to query
    Uri externalUri = DocumentsContract.buildDocumentUriUsingTree
        (treeUri, externalDocId);

    ContentResolver cr = getContext().getContentResolver();

    Cursor cursor = cr.query(externalUri, projection,
                             null, null, null);

这一切都像我希望的那样工作。它只需要把我的头撞在table上几次。

相关问题