我正在尝试编写一个自定义的DocumentsProvider,它允许其他应用程序对它提供的uri获取持久权限。
我在我的DocumentsProvider中声明如下

<provider
   android:name="com.cgogolin.myapp.MyContentProvider"
   android:authorities="com.cgogolin.myapp.MyContentProvider"
   android:grantUriPermissions="true"
   android:exported="true"
   android:permission="android.permission.MANAGE_DOCUMENTS"
   android:enabled="@bool/atLeastKitKat">
  <intent-filter>
    <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
  </intent-filter>
</provider>

我的应用程序有AndroidManufest.xml权限集
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />

(显然这不是必要的,但是添加/删除它也不重要)。
当我用
Intent openDocumentIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
openDocumentIntent.addCategory(Intent.CATEGORY_OPENABLE);
openDocumentIntent.setType("application/pdf");
openDocumentIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
startActivityForResult(openDocumentIntent, EDIT_REQUEST);

然后,在那里从我的提供商那里选择一个文件后,在我的应用程序的MANAGE_DOCUMENTS方法中,我可以通过ACTION_OPEN_DOCUMENTonActivityResult()获取的DocumentsProvider成功打开Uri提供的文件。
但是,尝试使用
getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);


getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

总是失败,除了
No permission grant found for UID 10210 and Uri content://com.cgogolin.myapp.MyContentProvider/document/tshjhczf.pdf

如果我从google驱动器中选择一个文件,或者在picker用户界面中下载一个提供程序,以这种方式获取权限是可行的。所以我认为问题出在我的供应商身上。
尽管我指定了intent.getData(),为什么没有创建权限授予?
我怎样才能说服android为我创建这样的许可授权呢?
毕竟我认为我自己做不到,因为我不知道打开picker ui的进程的android:grantUriPermissions="true",或者至少我不知道如何打开。

最佳答案

编辑:
我以前的回答不好。出于安全原因,您应该使用“android.permission.manage_documents”。
只有系统用户界面选择器才能列出您的文档。
但在打开文档的应用程序清单中不需要此权限。
实际上,您不应该获得此权限,因为它是系统权限。
我刚刚测试过它,并调用了TakePersistableUriPermission表单onActivityResult成功。
我使用documentprovider来模拟数据(一个根,3个txt文档)。
如果它仍然不适合您,则可能与您的文档提供商有一些问题。
编辑2:
样例代码

package com.example.test;

import android.database.Cursor;
import android.database.MatrixCursor;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsProvider;

import java.io.FileNotFoundException;

public class MyContentProvider extends DocumentsProvider {

    private final static String[] rootColumns = new String[]{
            "_id", "root_id", "title", "icon"
    };
    private final static String[] docColumns = new String[]{
            "_id", "document_id", "_display_name", "mime_type", "icon"
    };

    MatrixCursor matrixCursor;
    MatrixCursor matrixRootCursor;

    @Override
    public boolean onCreate() {

        matrixRootCursor = new MatrixCursor(rootColumns);
        matrixRootCursor.addRow(new Object[]{1, 1, "TEST", R.mipmap.ic_launcher});

        matrixCursor = new MatrixCursor(docColumns);
        matrixCursor.addRow(new Object[]{1, 1, "a.txt", "text/plain", R.mipmap.ic_launcher});
        matrixCursor.addRow(new Object[]{2, 2, "b.txt", "text/plain", R.mipmap.ic_launcher});
        matrixCursor.addRow(new Object[]{3, 3, "c.txt", "text/plain", R.mipmap.ic_launcher});

        return true;
    }

    @Override
    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
        return matrixRootCursor;
    }

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

        return matrixCursor;
    }

    @Override
    public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
                                      String sortOrder)
            throws FileNotFoundException {

        return matrixCursor;
    }

    @Override
    public ParcelFileDescriptor openDocument(String documentId, String mode,
                                             CancellationSignal signal)
            throws FileNotFoundException {

        int id;
        try {
            id = Integer.valueOf(documentId);
        } catch (NumberFormatException e) {
            throw new FileNotFoundException("Incorrect document ID " + documentId);
        }

        String filename = "/sdcard/";

        switch (id) {
            case 1:
                filename += "a.txt";
                break;
            case 2:
                filename += "b.txt";
                break;
            case 3:
                filename += "c.txt";
                break;
            default:
                throw new FileNotFoundException("Unknown document ID " + documentId);
        }

        return ParcelFileDescriptor.open(new File(filename),
                ParcelFileDescriptor.MODE_READ_WRITE);
    }
}

注:
您可以使用documentscontract.document和documentscontract.root中的常量。
我不确定是否需要“你的身份证”。
EdTe3:
更新了示例代码以从/sdcard打开文档。
添加了读/写外部存储权限。
androidmanifest.xml文件
<?xml version="1.0" encoding="utf-8"?>
<manifest
    package="com.example.test"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name">

        <provider
            android:name="com.example.test.MyContentProvider"
            android:authorities="com.example.test.document"
            android:enabled="true"
            android:exported="@bool/atLeastKitKat"
            android:grantUriPermissions="true"
            android:permission="android.permission.MANAGE_DOCUMENTS">
            <intent-filter>
                <action android:name="android.content.action.DOCUMENTS_PROVIDER"/>
            </intent-filter>
        </provider>
    </application>

</manifest>

客户端应用程序
具有空活动的新项目,未添加权限。
打开文档
Intent openDocumentIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
openDocumentIntent.addCategory(Intent.CATEGORY_OPENABLE);
openDocumentIntent.setType("text/plain");
openDocumentIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                    | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
startActivityForResult(openDocumentIntent, 1);

活动结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case 1: // TODO: Use constant
            if (resultCode == RESULT_OK) {
                if (data == null) return; // TODO: Show error
                Uri uri = data.getData();
                if (uri == null) return; // TODO: Show error
                getContentResolver().takePersistableUriPermission(uri,
                        Intent.FLAG_GRANT_READ_URI_PERMISSION);

                InputStream is = null;
                try {
                    is = getContentResolver().openInputStream(uri);

                    // Just for quick sample (I know what I will read)
                    byte[] buffer = new byte[1024];
                    int read = is.read(buffer);
                    String text = new String(buffer, 0, read);

                    ((TextView) findViewById(R.id.text)).setText(text);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (is != null) try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            break;
    }
}

09-25 22:21