Contact FutureLearn for Support
Skip main navigation
We use cookies to give you a better experience, if that’s ok you can close this message and carry on browsing. For more info read our cookies policy.
We use cookies to give you a better experience. Carry on browsing if you're happy with this, or read our cookies policy for more information.

Securing Content Providers

The data stored in a Content Provider is accessed via content URIs.

A content URI is a URI of the form content://authority/path/id, where authority refers to the Content Provider itself, and path/id to data stored within the Content Provider.

To access data stored within a Content Provider an app must use the query(), insert(), update(), and delete() methods provided by a ContentResolver object. These methods provide synchronous RPC like access to the Content Provider.

Access to the Content Provider through these methods can be controlled using permissions, however as we shall see, Content Providers allow much finer access control than Activities and Services.

The different mechanisms for applying permissions that we cover below have different scopes, from those that apply to the whole of the Content Provider, through to those that only apply to individual data items.

If two permissions apply to the same content URI, then the one with the smaller scope, i.e. the more fine-grained permission, is the one that takes precedence.

Basic access control

The most basic level of access control is performed in much the same way as for Activities and Services by using the android:permission attribute of the <provider> element in the app’s AndroidManifest.xml file.

This defines a permission that controls both read and write access to the whole of the Content provider.

However, following the Principle of Least Privilege, we can apply separate read and write permissions.

Sometimes we would like other apps to be able to read the contents of a Content Provider but not change them, and sometimes we want to allow an app to add a new entry to our database, but not to be able to read what is already in there. For example a logging system where apps can record what they are doing, but cannot read what other apps are doing.

This means that having write permission does not imply having read permission. This is a common misconception.

Read permission is controlled by the android:readPermission attribute of the <provider> element, and write permission by the android:writePermission attribute.

The access methods of a Content Provider are controlled as follows:

  • query() requires read permission, and

  • insert(), update(), and delete() require write permission.

Controlling access to subsets of the data

To further apply the Principle of Least Privilege we can give separate permissions to subsets of a Content Provider’s data. This will override any global permissions set on the whole of the Content Provider.

We can do this by adding <path-permission> elements to the <provider> element.

There are three different attributes that can be set to control which subset of the data the permissions apply to:

android:path="/subpath"

applies the permissions to entries within /subpath, but not to subdirectories of /subpath,

android:pathPrefix="/subpath"

applies the permissions to entries within subdirectories of /subpath, and

android:pathPattern

allows the use of wildcards to match the content URIs for which the permissions apply.

For example if we wanted to separately control access to the data stored under the content URIs content://authority/subpath1 and content://authority/subpath2 we would add the following:

<provider ><path-permission android:pathPrefix="/subpath1"
                   android:readPermission="com.example.myamazingapp.SUBPATH1_READ_PERMISSION"
                   android:writePermission="com.example.myamazingapp.SUBPATH1_WRITE_PERMISSION" />
  <path-permission android:pathPrefix="/subpath2"
                   android:readPermission="com.example.myamazingapp.SUBPATH2_READ_PERMISSION"
                   android:writePermission="com.example.myamazingapp.SUBPATH2_WRITE_PERMISSION" />
</provider>

By applying different permissions to the two content URIs we can give an app access to content://authority/subpath1 but deny it access to content://authority/subpath2.

Temporary access to specific data items

We can take the Principle of Least Privilege one step further and consider the case where we might want to give an app temporary access to some of the data within a Content Provider.

One obvious example of this is the case of an email client that may store emails and attachments in a database accessed via a Content Provider.

Now suppose one of the attachments is a media file that the email client does not know how to handle, but for which there is another app on the device that can handle such files. We would like to give that app temporary access to the media file within the Content Provider in order to play the file, but we do not want to allow the app permanent access to the file, nor do we want to give it complete access to all the user’s emails. To handle this situation Android provides URI permissions.

URI permissions allow any component that has permission to access a content URI within a Content Provider to temporarily grant another component that permission, even if that component does not itself have permission to access the Content Provider.

Enabling URI permissions

By default the granting of temporary URI permissions is disabled. It can be turned on in two different ways.

The first way enables any component that has permission to access the Content Provider the ability to grant temporary access to any content URI within the Content Provider to another component. To do this we set the attribute android:grantUriPermission="true" in the <provider> element.

Again following the Principle of Least Privilege we might not want to do this, so the second way is to add <grant-uri-permission> elements to the <provider> element. Like with <path-permission> elements (above), you can set one of three different attributes: android:path, android:pathPrefix, and android:pathPattern. For example,

<provider ><grant-uri-permission android:path="/subpath" />
</provider>

allows a component to grant temporary permissions for entries within /subpath, but not for subdirectories of /subpath.

Granting temporary permissions

So far we have discussed how we can give a component permission to grant temporary permissions to other components, however we have not yet said anything about how to actually grant temporary permissions. This is done programatically, and there are two ways to do this.

The first, and preferred way, is through the setting of flags on Intents. The way this works is as follows:

  1. A component wants to ask another component, like an Activity (say an image viewer) or a Service, to perform an action on some data in a Content Provider for which it has access.

  2. As usual, the component will create an Intent and call startActivity() or startService() with that Intent.

  3. To tell the receiving component what data it should perform the action on the sending component calls the setData() method with the required content URI.

  4. In order to give the component that receives the Intent permission to access the content URI where the data is stored, the sending component uses the setFlags() method to set either the FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION flag on the Intent before sending it.

  5. When startActivity() (or startService() etc.) is called, the system will check whether the calling component has permission to grant the temporary access to the content URI, if it does, the Intent will be delivered to the receiving component.

The second way to grant temporary permission to access a content URI to another app is by calling the grantUriPermission() method, but this is not recommended.

Finally, the revokeUriPermission() method can be used to revoke temporary permissions, and must be called if a content URI for which a temporary permission is granted, is removed from the Content Provider.

Share this article:

This article is from the free online course:

Secure Android App Development

University of Southampton