Skip to content

Granting Content Provider URI Permissions

August 7, 2012

Hey everyone,

The goal of this short example is to show you how you can further protect your application’s data by enforcing permissions upon your custom content provider. For those who have never seen a custom content provider implementation, I invite you to check out my post Writing Your own Content Provider before reading on.

The purpose of giving your content provider the ability to grant access to its data should be pretty clear. Suppose you want to protect the integrity of your content provider’s data. Then, for some users/applications you may want to give them both read and write permissions, while for others you may only want to give them read permission. A simple example of this is attachments in a mail application [taken from Google]:

Access to the mail should be protected by permissions, since this is sensitive user data. However, if a URI to an image attachment is given to an image viewer, that image viewer will not have permission to open the attachment since it has no reason to hold a permission to access all e-mail.

These permissions are for a specific content URI, and will last until the Activity that receives them is finished. In other words, when an application grants read and/or write permissions to another Activity, these permissions are temporary. Before moving on to some code, let me note that this implementation is different than declaring the android:readPermission and android:writePermission attributes as these tags specify specific applications and give those applications permanent read/write access. In this way, the method below which uses the android:grantUriPermissions tag a.k.a the sub-tag grant-uri-permission is much more dynamic and flexible.

So let’s see how all this is done. Adding permission requirements to your content provider is actually quite simple. Let’s extend my Notes ContentProvider from the above example and give it the ability to grant URI permissions. Say you want to give the user the ability to share his/her notes through various mail/social-media applications, but you want to protect the content of the note. Then, you’ll want to add the following to your AndroidManifest.xml file:

<provider android:name="jason.wei.apps.notes.providers.NotesContentProvider"
          android:authorities="jason.wei.apps.notes.providers.NotesContentProvider"
          android:grantUriPermission="true" <!-- for granting URI-based permissions throughout the entire provider -->
          <grant-uri-permission android:pathPattern="/notes/" /> <!-- for granting URI-based permissions to specific sub-branches of the provider -->
</provider>

What I mean by the distinction “permissions throughout the entire provider” versus “permissions to specific sub-branches of the provider” is that in the former, any URI with pattern:

“jason.wei.apps.notes.providers.NotesContentProvider/…”

Will have the ability to grant permissions, while in the second only URIs with pattern:

“jason.wei.apps.notes.providers.NotesContentProvider/notes/{id}”

can grant URI permissions (for those struggling with the concept of URIs, just think of this as giving your entire hard drive some desired property, versus only giving one directory in your hard drive that same property). Finally, as for how you actually go about granting such permissions through Intents, the below shows it done in a generic Activity:

public class NoteUriGrantActivity extends Activity {
	
	public static final String NOTE_ACTION_VIEW = "jason.wei.custom.intent.action.NOTE_VIEW";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		// ... code ...

		// IMPLICIT INTENT EXAMPLE
		Uri uri = Uri.parse("content://jason.wei.apps.notes.providers.NotesContentProvider/notes/1");
		Intent intent = new Intent();
		intent.setAction(NOTE_ACTION_VIEW); // SET CUSTOM INTENT ACTION
		intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // GRANT TEMPORARY READ PERMISSION
		intent.setData(uri);
		startActivity(intent); // SEND INTENT TO BE RESOLVED

		// EXPLICIT INTENT EXAMPLE
		grantUriPermission("jason.wei.apps.NotesReader", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
	}
}

And with that, in order to resolve our custom Intent, we simply need to write a separate Activity (note this Activity need not be defined within the same application as the ContentProvider) with correctly specified intent filter:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="jason.wei.apps.NotesReader"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".NoteReaderActivity" android:label="@string/app_name" >
            <intent-filter>
                <action android:name="jason.wei.custom.intent.action.NOTE_VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>

And voila! Now your NoteReaderActivity simply needs to call the getIntent() method to retrieve the content of the note! Architecturally, this process looks like:

Uri Permissions Architecture

Granting Uri Permissions Architecture

And once the NoteReaderActivity obtains the content of the note, it has the ability to layer on top any additional functionality (i.e. including pictures, videos, other media content) before ultimately sharing it with your friends.

Here, at the end of the day, everyone is happy. The NotePad application can allow other applications on the device to consume its content without fear of data corruption, while the NoteReader application can focus on non-content related issues such as UI, media attachments, group sharing, etc.

Hope this helps and happy coding!

- jwei

About these ads
2 Comments leave one →
  1. Jin permalink
    November 20, 2012 10:55 pm

    Hi, Jason
    Did you add any of these uri permission requirements in ?
    android:readPermission=”android.permission.permRead”
    android:writePermission=”android.permission.permWrite”

    If you did not put in these requirements, any app can access to the content provider even without being granted permission.
    I did that and found FLAG_GRANT_READ_URI_PERMISSION is actually not working.

    So, do you know how to implement FLAG_GRANT_READ_URI_PERMISSION when content provider is protected by user-defined permissions?

    I have my detailed question here http://stackoverflow.com/questions/13435654/how-to-grant-temporary-access-to-custom-content-provider-using-flag-grant-read-u

    Best,
    Jin

  2. February 21, 2013 10:49 pm

    Having read this I thought it was very enlightening.
    I appreciate you finding the time and effort to put this article
    together. I once again find myself spending a significant amount of time both reading and leaving comments.
    But so what, it was still worth it!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 880 other followers

%d bloggers like this: