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

Posted

技术标签:

【中文标题】如何使用 FLAG_GRANT_READ_URI_PERMISSION 授予对自定义内容提供者的临时访问权限【英文标题】:How to grant temporary access to custom content provider using FLAG_GRANT_READ_URI_PERMISSION 【发布时间】:2012-11-06 07:17:23 【问题描述】:

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

当内容提供者没有权限保护时,我可以这样做。具体来说,我在 App A 上构建了自定义内容提供程序,并向 App B 发送了一个包含 URI 的意图。 这是 App 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);
        

后跟 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>

然后 App B(class Provid) 获取 URI 并在 content provider 中查询相应的数据(使用以下代码)。

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

这是 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>

但是,当我从 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 ProcessRecord426c6ea8 17032:com.android.web/u0a95 (pid=17032, uid=10095) requires android.permission.permRead or android.permission.permWrite   

App B 似乎没有使用临时权限访问。也就是说,如何利用 App B 的 FLAG_GRANT_READ_URI_PERMISSION?

我也尝试过直接将 Uri 添加到意图(使用 setData()),而不是 Uri.toString()(使用 putExtra())。

sendIntent.setData(uri2);

Uri userNameuri = getIntent().getData(); 

但App B中得到的“userNameuri”为空。

我完全糊涂了……

更新

我根据之前的帖子尝试了“grantUriPermission("com.android.getCPaccess", uri2, Intent.FLAG_GRANT_READ_URI_PERMISSION)"What is the correct permission handling when sending sensitive app data as email attachment?

它确实有效。它可以在不使用 FLAG_GRANT_READ_URI_PERMISSION 的情况下工作。但许可不是“临时的”。它必须通过 revokeUriPermission() 手动结束。

所以,我想知道是否有一种方法可以授予 FLAG_GRANT_READ_URI_PERMISSION 中介绍的临时权限,或者根本就是一个错误?

【问题讨论】:

您的应用中是否有 android.permission.permRead 用于实现提供程序和选择器活动? 嗨,詹斯。我也试过了。当两个应用程序在清单文件中都有 时,它可以正常工作。但这是一种永久许可。我想用 FLAG_GRANT_READ_URI_PERMISSION 计算出临时访问权限(如 google android developer 所述)。因此,App B 将没有 android.permission.permRead,而是从 App A 接收带有 FLAG_GRANT_READ_URI_PERMISSION 标志“true”的 Intent。但这失败了。 您应该只需要一个uses-permission,在声明调用#addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 的选择器活动的应用程序中声明。您可能应该在问题中包含更多清单和代码。 当然。我编辑了这个问题。是的,App A 不需要使用许可。但是App B也不应该有,因为如果有,就不需要addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)。 否 - 定义提供程序的应用程序 A,应该还定义您用于读/写的权限。它必须还为读取(和写入,如果您将来也希望授予写入权限)声明使用权限 - 您不能授予您自己不拥有的权限。 【参考方案1】:

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

我在玩ACTION_SEND 时发现了类似的问题,它在EXTRA_STREAM 中获取一个uri。在 setData() 中提供相同的 uri 会起作用,但不符合规则并导致意外行为(例如 Gmail 收件人)。

Jelly Bean Intent 可以包含 ClipData,这应该可以解决问题。对于ACTION_SEND,它是由附加组件自动生成的。

grantUriPermission 可以工作,但需要revokeUriPermission。要完成与startActivity(Intent.createChooser(intent, title)) 相同的工作,您必须选择目标 (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>

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

发件人应用:

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

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

【讨论】:

以上是关于如何使用 FLAG_GRANT_READ_URI_PERMISSION 授予对自定义内容提供者的临时访问权限的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 CSS 使多个内联块元素居中?

如何在 django 中使用模型进行查询

如何使用 __subclasscheck__ 魔术方法?

python如何使用__new__()函数的参数列表中的属性创建类?

如何在 8051 中使用 __func__

如何正确使用“__beginthreadex”?