文档提供者的持久 URI 权限

发布于 2025-01-19 12:02:46 字数 4118 浏览 6 评论 0原文

不确定我要做的是可能的。我有一个“代理”文档提供商,意思是使用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回调的“ increateresultLauncher”类是:

    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上失败。

问题是:

  • 这是在建筑上支持的吗?
  • 如果是这样,有人可以在上述片段中看到任何明显的错误吗?
  • 如果没有,谁能想到我可以解决建筑限制的方式?

感谢您阅读帖子。

Not sure if what I'm trying to do is possible or not. I have a "proxy" document provider meaning a document provider that exports aliases for other content using the SAF. I have a dialog fragment that allows a user to set up the alias by presenting an OPEN_DOCUMENT_TREE intent, capturing the URI, granting persistable permissions on that URI, and then passing that URI to the document provider to present that content.

The dialog fragment is able to read/write on the URI it receives from the intent but the provider portion is not able to. The error I always get is:

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

I've read many related questions and corresponding replies that folks have posted on working with these persistable READ_URI_PERMISSIONS and have tried all variations suggested in those posts. But I am unable to get this to work. I'm beginning to think that the persistable URI permissions are not available within a provider. Is that the case? Do I need to have the provider invoke an intent so that I can grant these permissions? Perhaps have the provider call into the main activity to access the content. I'd certainly rather access the content directly from the provider. I haven't found anywhere that states once persistable permissions are granted, who are they granted to? Is a provider portion of an app a separate process and is that the issue?

Let me provide relevant code snippets:

The 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>

The dialog fragment that obtains the URI is within the main activity and the code that uses the URI is within the provider.

The dialog fragment of the main activity invokes the OPEN_DOCUMENT_TREE intent using:

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

The "onCreateResultLauncher" class with the aswsociatd onActivityResult callback is:

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

Within the dialog fragment, I test the URI to see if I can read it:

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

We always can read. Now the provider tries to access the URI as follows:

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

The provider does not have read access. So when the provider eventually does a query on the URI, it will fail with the SecurityException shown above.

The questions are:

  • Is this supported architecturally?
  • If so, can anyone see any obvious mistake in the above snippets?
  • If not, can anyone think of a way I can work around the architectural limitation?

Thank you for reading through the post.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

执笏见 2025-01-26 12:02:46

我已经能够弄清楚这一点。我必须改变一些东西,但我遇到的主要问题是如何构建我传递的 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);

一切都如我所愿。它只需要把我的头撞在桌子上几下就可以了。

I've been able to figure this out. I had to change a few things but the main issue I had was with how I built the URI's that I passed around. Here's some of the most relevant change:

The app reads in a URI which wants to be exposed in a custom document provider that is part of the app. When launching the

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

and then launch the intent. After the user selects a directory to export to the custom document provider, the app will recieve the result callback:

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

The URI is then passed to the document provider which creates a root for it. The document provider does not need to explicitly take any permissions. It
just needs to make sure to build a correct URI. For example:

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

It all works as I had hoped. It just needed to smash my head on the desk a few times.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文