繁体   English   中英

文档提供者的持久 URI 权限

[英]Persistable URI Permissions for a Document Provider

不确定我正在尝试做的事情是否可行。 我有一个“代理”文档提供者,意思是使用 SAF 为其他内容导出别名的文档提供者。 我有一个对话框片段,它允许用户通过呈现 OPEN_DOCUMENT_TREE 意图、捕获 URI、授予对该 URI 的持久权限,然后将该 URI 传递给文档提供者以呈现该内容来设置别名。

对话片段能够读/写它从意图接收的 URI,但提供者部分不能。 我总是得到的错误是:

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

我已经阅读了许多相关问题和人们发布的关于使用这些持久性 READ_URI_PERMISSIONS 的相应回复,并尝试了这些帖子中建议的所有变体。 但我无法让它发挥作用。 我开始认为持久性 URI 权限在提供商中不可用。 是这样吗? 我是否需要让提供者调用意图以便我可以授予这些权限? 也许让提供商调用主要活动来访问内容。 我当然更愿意直接从提供商那里访问内容。 我还没有找到任何地方说明一旦授予持久权限,他们将授予谁? 应用程序的提供者部分是一个单独的过程吗?这就是问题所在吗?

让我提供相关的代码片段:

AndroidManifest.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 的对话片段在主活动中,使用 URI 的代码在提供程序中。

主要活动的对话片段使用以下方式调用 OPEN_DOCUMENT_TREE 意图:

    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”class 是:

    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。

问题是:

  • 这在架构上受支持吗?
  • 如果是这样,任何人都可以在上面的片段中看到任何明显的错误吗?
  • 如果没有,谁能想出一种方法来解决架构限制?

感谢您阅读这篇文章。

我已经弄明白了。 我不得不改变一些东西,但我遇到的主要问题是我如何构建我传递的 URI。 以下是一些最相关的变化:

该应用程序读取一个 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);

然后启动意图。 用户选择导出到自定义文档提供者的目录后,应用会收到结果回调:

    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);

一切都如我所愿。 它只需要在桌子上砸我的头几次。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM