如何使用FLAG_GRANT_READ_URI_PERMISSION授予对自定义内容提供程序的临时访问权限

Jin*_*Jin 9 android android-intent android-contentprovider

我试图从另一个应用程序(应用程序B)查询自定义内容提供程序(应用程序A).

当内容提供商没有权限保护时,我可以这样做.具体来说,我在App A上构建自定义内容提供程序,并将包含URI的intent发送到App B.这是App A中的intent-sending部分.

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);
        }
}
Run Code Online (Sandbox Code Playgroud)

然后是App A的清单文件.

<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>
Run Code Online (Sandbox Code Playgroud)

然后App B(类Provid)获取URI并查询内容提供者中的相应数据(使用以下代码).

public class Provid extends Activity {

public void onCreate(Bundle savedInstanceState) {
    Bundle extras = getIntent().getExtras(); 
    String userNameuri;
    if (extras != null) {
        userNameuri = extras.getString("name");
      Uri allTitles = Uri.parse(userNameuri);
      Cursor c = managedQuery(allTitles, null, null, null, null);
       if (c.moveToFirst()) {
          do{
             System.out.println("Name is"+c.getString(c.getColumnIndex(DataBaseConfiguation.TableConfiguation.USER_NAME)).toString());               
          } while (c.moveToNext());
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

}

这是App 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>
Run Code Online (Sandbox Code Playgroud)

但是,当我从App B查询内容提供程序时,会发生错误:

java.lang.RuntimeException: Unable to start activity ComponentInfo {com.android.web/com.android.web.Provid}: java.lang.SecurityException: Permission Denial: opening     provider com.ck.contentprovider.MyContentProvider from ProcessRecord{426c6ea8 17032:com.android.web/u0a95} (pid=17032, uid=10095) requires android.permission.permRead or android.permission.permWrite   
Run Code Online (Sandbox Code Playgroud)

似乎App B没有使用临时权限来访问.换句话说,如何从App B中使用FLAG_GRANT_READ_URI_PERMISSION?

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

sendIntent.setData(uri2);
Run Code Online (Sandbox Code Playgroud)

Uri userNameuri = getIntent().getData(); 
Run Code Online (Sandbox Code Playgroud)

但是App B中的"userNameuri"为空.

我完全糊涂了......

更新

我根据上一篇文章尝试了"grantUriPermission("com.android.getCPaccess",uri2,Intent.FLAG_GRANT_READ_URI_PERMISSION)"
将敏感应用数据作为电子邮件附件发送时,正确的权限处理是什么?

它确实有效.它可以在不使用FLAG_GRANT_READ_URI_PERMISSION的情况下工作.但许可不是"暂时的".它必须由revokeUriPermission()手动结束.

所以,我想知道是否有办法授予FLAG_GRANT_READ_URI_PERMISSION中引入的临时权限,或者这是一个错误?

mar*_*arx 12

似乎FLAG_GRANT_READ_URI_PERMISSION只会影响Uri Intent.mData额外的uris而不是uris.

我在玩的时候发现了类似的问题ACTION_SEND,这需要一个uri EXTRA_STREAM.提供相同的uri setData()将起作用,但不符合规则并导致意外行为(例如Gmail收件人).

从Jelly Bean开始,Intent可以包含ClipData,它应该可以解决问题.因为ACTION_SEND它是从额外的东西自动生成的.

grantUriPermission会工作,但需要revokeUriPermission.要做同样的工作,startActivity(Intent.createChooser(intent, title))你必须选择target(ACTION_PICK_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>
Run Code Online (Sandbox Code Playgroud)

ActivityView.java:

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) {
            …
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

发件人应用:

<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>
Run Code Online (Sandbox Code Playgroud)

ActivitySend.java:

// 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
            )
        );
}
Run Code Online (Sandbox Code Playgroud)