Android 如何使用FLAG\u grant\u READ\u URI\u权限授予对自定义内容提供程序的临时访问权限

Android 如何使用FLAG\u grant\u READ\u URI\u权限授予对自定义内容提供程序的临时访问权限,android,android-intent,android-contentprovider,Android,Android Intent,Android Contentprovider,我正在尝试从另一个应用程序(应用程序B)查询自定义内容提供商(应用程序A) 当内容提供商没有权限保护时,我可以这样做。具体来说,我在应用程序A上构建自定义内容提供者,并将包含URI的意图发送给应用程序B。 以下是应用程序A中的发送部分 class InsertOnClickListener implements OnClickListener{ public void onClick(View v) { ContentValues valu

我正在尝试从另一个应用程序(应用程序B)查询自定义内容提供商(应用程序A)

当内容提供商没有权限保护时,我可以这样做。具体来说,我在应用程序A上构建自定义内容提供者,并将包含URI的意图发送给应用程序B。 以下是应用程序A中的发送部分

class InsertOnClickListener implements OnClickListener{        
        public void onClick(View v) {
            ContentValues values = new ContentValues();
            values.put(DataBaseConfiguation.TableConfiguation.USER_NAME, "Jack");
            Uri uri = getContentResolver().insert(DataBaseConfiguation.TableConfiguation.CONTENT_URI, values);
            System.out.println("uri------------------->" + uri);
            // the uri above should be like "content://com.catking.contentprovider.MyContentProvider/user"
            Uri uri2 = Uri.parse("content://com.catking.contentprovider.MyContentProvider/user");
              Cursor c = managedQuery(uri2, null, null, null, null);
              String sendvalue = null;
               if (c.moveToFirst()) {
                  do{
                     System.out.println("User name:"+c.getString(c.getColumnIndex(DataBaseConfiguation.TableConfiguation.USER_NAME)).toString());               
                     sendvalue = c.getString(c.getColumnIndex(DataBaseConfiguation.TableConfiguation.USER_NAME)).toString();
                  } while (c.moveToNext());
               }
            Intent sendIntent = new Intent();
            sendIntent.setClassName("com.android.web", "com.android.web.Provid");
            sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            sendIntent.putExtra("name", uri2.toString());
            sendIntent.setType("text/plain");
            startActivity(sendIntent);
        }
}
<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:name=".ContentProviderTestActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <provider android:authorities="com.catking.contentprovider.MyContentProvider"
        android:exported="true"
        android:grantUriPermissions="true"
        android:name="com.catking.contentprovider.MyContentProvider" 
        android:readPermission="android.permission.permRead"
        android:writePermission="android.permission.permWrite" >
    </provider>
</application>
然后是应用程序A的清单文件

class InsertOnClickListener implements OnClickListener{        
        public void onClick(View v) {
            ContentValues values = new ContentValues();
            values.put(DataBaseConfiguation.TableConfiguation.USER_NAME, "Jack");
            Uri uri = getContentResolver().insert(DataBaseConfiguation.TableConfiguation.CONTENT_URI, values);
            System.out.println("uri------------------->" + uri);
            // the uri above should be like "content://com.catking.contentprovider.MyContentProvider/user"
            Uri uri2 = Uri.parse("content://com.catking.contentprovider.MyContentProvider/user");
              Cursor c = managedQuery(uri2, null, null, null, null);
              String sendvalue = null;
               if (c.moveToFirst()) {
                  do{
                     System.out.println("User name:"+c.getString(c.getColumnIndex(DataBaseConfiguation.TableConfiguation.USER_NAME)).toString());               
                     sendvalue = c.getString(c.getColumnIndex(DataBaseConfiguation.TableConfiguation.USER_NAME)).toString();
                  } while (c.moveToNext());
               }
            Intent sendIntent = new Intent();
            sendIntent.setClassName("com.android.web", "com.android.web.Provid");
            sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            sendIntent.putExtra("name", uri2.toString());
            sendIntent.setType("text/plain");
            startActivity(sendIntent);
        }
}
<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:name=".ContentProviderTestActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <provider android:authorities="com.catking.contentprovider.MyContentProvider"
        android:exported="true"
        android:grantUriPermissions="true"
        android:name="com.catking.contentprovider.MyContentProvider" 
        android:readPermission="android.permission.permRead"
        android:writePermission="android.permission.permWrite" >
    </provider>
</application>
}

这是应用程序B的清单文件

<uses-permission android:name="android.permission.INTERNET" />
<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:name="._GetWebResoureActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.SEND" >
            </action>
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="*/*" />
        </intent-filter>
    </activity>

    <receiver android:name="StaticReceiver11" >
        <intent-filter>
            <action android:name="android.intent.action.MYSEND" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </receiver>

    <activity
        android:name="Provid"
        android:label="@string/title_activity_provid" >
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="*/*" />
        </intent-filter>

    </activity>
</application>
应用程序B似乎没有使用临时访问权限。换句话说,如何利用来自应用程序B的标志\u GRANT\u READ\u URI\u权限

我还尝试直接将Uri添加到intent(使用setData()),而不是Uri.toString()(使用putExtra()

Uri userNameuri = getIntent().getData(); 
但是在应用程序B中获得的“userNameuri”为空

我完全糊涂了

已更新

根据之前的帖子,我尝试了“grantUriPermission”(“com.android.getCPaccess”,uri2,Intent.FLAG\u GRANT\u READ\u URI\u PERMISSION)”

它确实有效。它可以在不使用FLAG\u GRANT\u READ\u URI\u权限的情况下工作。但这种许可并非“临时性的”。必须通过revokeUriPermission()手动结束


所以,我想知道是否有一种方法可以像FLAG_grant_READ_URI_permission中介绍的那样授予临时权限,或者这是一个bug?

似乎
FLAG_grant_READ_URI_permission
只影响
URI Intent.mData
而不影响额外的URI

我在玩
ACTION\u SEND
时发现了类似的问题,它在
EXTRA\u流
中使用uri。在
setData()
中提供相同的uri会起作用,但不符合规则并导致意外行为(例如Gmail收件人)

果冻豆的意图可以包含ClipData,这应该可以解决问题。对于
ACTION\u SEND
,它是从附加程序自动生成的

grantUriPermission
将起作用,但需要
revokeUriPermission
。要执行与
startActivity(Intent.createChooser(Intent,title))
相同的工作,您必须选择目标(
ACTION\u pick\u ACTIVITY
),向其包授予权限,并在不再需要时撤销它们(onActivityResult?)

下面是一些代码:

接收器应用程序:

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

    <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" >
        …
        <activity
            android:name=".ActivityView"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="*/*"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

在你的应用程序中是否有一个用于android.permission.permRead的程序来实现提供者和选取者活动?嗨,Jens。我也试过了。当两个应用程序都在清单文件中时,它可以正常工作。但这是一种永久的许可。我想用FLAG_GRANT_READ_URI_permission(如googleandroid开发者所述)来计算临时访问权限。因此,应用程序B不会有android.permission.permRead,相反,它会从应用程序A接收带有标志“true”的意图。但这失败了。你应该只需要一个
使用权限
,该权限在声明调用
的选择器活动的应用程序中声明,addFlags(intent.FLAG\u GRANT\u READ\u URI\u permission)
。您可能应该在问题中包含更多的清单和代码。当然。我编辑了这个问题。是,应用程序A不需要使用权限。但是应用程序B也不应该有,因为如果有,就不需要添加标志(Intent.FLAG\u GRANT\u READ\u URI\u PERMISSION)。否-定义提供程序的应用程序A还应该定义用于读/写的权限。它还必须为读和写声明一个uses权限(如果您将来还希望授予写权限,则为(&write)),您不能授予自己不拥有的权限。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.handler" >

    <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" >
        …
        <activity
            android:name=".ActivityView"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="*/*"/>
            </intent-filter>
        </activity>
    </application>
</manifest>
protected void onCreate(Bundle savedInstanceState) {
    Intent intent = getIntent();
    if ((intent != null)) {
        procUri(intent.getData());
        procUri(intent.<Uri>getParcelableExtra(Intent.EXTRA_STREAM));
    }
}

private void procUri(Uri uri) {
    if (uri != null) {
        InputStream i;
        try {
            i = getContentResolver().openInputStream(uri);
            byte[] b = new byte[i.available()];
            i.read(b);
            …
        } catch (Throwable e) {
            …
        }
    }
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.sender" >

    <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" >
        …
        <activity
            android:name=".ActivitySend"
            android:label="@string/app_name" >
        </activity>
        <provider
            android:name=".MyContentProvider"
            android:authorities="com.example.sender.content"
            android:enabled="true"
            android:exported="false"
            android:grantUriPermissions="true" >
        </provider>
    </application>
</manifest>
// OK!
private void doTestView(Uri uri, String type) {
    startActivity(
            new Intent(Intent.ACTION_VIEW)
                .setDataAndType(uri, type)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        );
}

// error prior to JellyBean
// ok on JellyBean, even without explicit FLAG_GRANT_READ_URI_PERMISSION (due to generated ClipData)
private void doTestSend(Uri uri, String type, CharSequence title) {
    startActivity(
            Intent.createChooser(
                new Intent(Intent.ACTION_SEND)
                    .setType(type)
                    .putExtra(Intent.EXTRA_STREAM, uri)
                    .putExtra(Intent.EXTRA_SUBJECT, title)
                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
                    .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                ,
                title
            )
        );
}

// working but not ok, unexpected results!
private void doTestSend2(Uri uri, String type, CharSequence title) {
    startActivity(
            Intent.createChooser(
                new Intent(Intent.ACTION_SEND)
                    .setDataAndType(uri, type)
                    .putExtra(Intent.EXTRA_STREAM, uri)
                    .putExtra(Intent.EXTRA_SUBJECT, title)
                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
                    .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                ,
                title
            )
        );
}