25

I've created a custom content provider, which will be accessed by a few more applications. I've included the permission TAG in my provider AndroidManifest.xml file, and in the second application, I included the uses-permissions tag, but no success. Logcat shows me:

java.lang.SecurityException: Permission Denial: opening provider com.company.contentprovider.AplicacaoContentProvider requires READ_DATABASE or WRITE_DATABASE.

I've search on similar questions, but it seems like everything is correct. Any ideas ? Thanks !!!

Here is my provider AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.company.contentprovider"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="17" />
<permission android:name="READ_DATABASE" android:label="@string/app_read"       android:protectionLevel="normal"></permission>
<permission android:name="WRITE_DATABASE" android:label="@string/app_write" android:protectionLevel="normal"></permission>

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name=".CompanyProvider"
        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:name="AplicacaoContentProvider"
        android:authorities="com.company.contentprovider"
        android:exported="true"
        android:readPermission="@string/app_read"
        android:writePermission="@string/app_write"
       />
</application>

And this is my second application AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testeprovider"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="16" />
<uses-permission android:name="android.permissions.READ_DATABASE"/>
<uses-permission android:name="android.permissioms.WRITE_DATABASE"/>


<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name="com.example.testeprovider.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

</application>

duggu
  • 37,851
  • 12
  • 116
  • 113
Android Hunter
  • 361
  • 1
  • 3
  • 8

4 Answers4

25

The answer above was a litle confusing for me. But I got it now. I want to post my solution as well. Maybe for someone it's more better to understand.

First App A is the App that has the SQLite-Database and the "Custom Content Provider". App B uses with a ContentResolver the databse from App A.

This is the AndroidManifest.xml-File from App A:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.test"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:minSdkVersion="10" />


<permission android:name="de.test.READ_DATABASE" android:protectionLevel="normal" />
<permission android:name="de.test.WRITE_DATABASE" android:protectionLevel="normal" />

<application
    android:debuggable="true"
    ... >
    ...
    ...
    <provider
        android:name="de.test.TestContentProvider"
        android:authorities="de.test.ContentProvider"
        android:exported="true"
        android:readPermission="de.test.READ_DATABASE"
        android:writePermission="de.test.WRITE_DATABASE" />
    ...
    ...
</application>

Ok and this is the AndroidManifest.xml-File from App B. Important is the part with "uses-permission":

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.test.testercontentprovider"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="15"
    android:targetSdkVersion="17" />

<uses-permission android:name="de.test.READ_DATABASE" />
<uses-permission android:name="de.test.WRITE_DATABASE" />

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name="de.test.testercontentprovider.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

And the Code of the ContentProvider for App A looks like this:

public class TestContentProvider extends ContentProvider {

public static final String AUTHORITY = "de.test.TestContentProvider";

public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
        + "/" + "nameoftable");


@Override
public boolean onCreate() {
    ...
    return true;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {
    // TODO Auto-generated method stub
            return null;
}

@Override
public int update(Uri uri, ContentValues values, String selection,
        String[] selectionArgs) {
    // TODO Auto-generated method stub
    return 0;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    // TODO Auto-generated method stub
    return 0;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
    // TODO Auto-generated method stub
    return null;
}

@Override
public String getType(Uri uri) {
    // TODO Auto-generated method stub
    return null;
}
}

And the Code for the ContentResolver from App B:

public class MainActivity extends Activity {

private static final String TAG = MainActivity.class.getSimpleName();
public static final String AUTHORITY = "de.test.TestContentProvider";
public static final String TABLE_NAME = "nameoftable";

    ...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ContentResolver cr = getContentResolver();

    // show entries of db
    listEntries(cr);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

private void listEntries(ContentResolver cr) {
    Uri uri = Uri.parse("content://" + AUTHORITY + "/" + TABLE_NAME);
    Cursor c = cr.query(uri, null, null, null, null);

    if (c == null) {
        Log.d(TAG, "Cursor c == null.");
        return;
    }
    while (c.moveToNext()) {
        String column1 = c.getString(0);
        String column2 = c.getString(1);
        String column3 = c.getString(2);

        Log.d(TAG, "column1=" + column1 + " column2=" + column2 + " column3=" + column3);
    }
    c.close();
}
}

I hope this can help someone to understand it better.

KingAlex1985
  • 659
  • 1
  • 8
  • 9
19

but it seems like everything is correct

Not exactly.

<permission android:name="READ_DATABASE" android:label="@string/app_read"       android:protectionLevel="normal"></permission>
<permission android:name="WRITE_DATABASE" android:label="@string/app_write" android:protectionLevel="normal"></permission>

First, you really really really really really really should put a namespace on those permission names. Make them com.company.contentprovider.READ_DATABASE and com.company.contentprovider.WRITE_DATABASE.

<provider android:name="AplicacaoContentProvider"
    android:authorities="com.company.contentprovider"
    android:exported="true"
    android:readPermission="@string/app_read"
    android:writePermission="@string/app_write"
   />

Second, your android:readPermission and android:writePermission values need to use the android:name value from <permission>, not android:label. android:label is a display name only. So, the above snippet should be:

<provider android:name="AplicacaoContentProvider"
    android:authorities="com.company.contentprovider"
    android:exported="true"
    android:readPermission="com.company.contentprovider.READ_DATABASE"
    android:writePermission="com.company.contentprovider.WRITE_DATABASE"
   />

(though, bonus points for explicitly putting android:exported="true", which is a good idea)

<uses-permission android:name="android.permissions.READ_DATABASE"/>
<uses-permission android:name="android.permissioms.WRITE_DATABASE"/>

Third, your other manifest does not use your old android:name, nor my suggested revised android:name, nor android:label, but something else entirely, where you elected to say that these are in the android.permission namespace, and they are not. This should be:

<uses-permission android:name="com.company.contentprovider.READ_DATABASE"/>
<uses-permission android:name="com.company.contentprovider.WRITE_DATABASE"/>

(though it is possible that com.company.contentprovider.WRITE_DATABASE will be sufficient -- I don't know if android:writePermission will automatically imply android:readPermission or not)

Make those changes, and I think you will have better luck.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thank you ! As soon I get to work tomorrow, I'll make these modifications and let you know about the results ! – Android Hunter Jan 17 '13 at 00:25
  • It worked, just the way you said, but there's another doubt. I have, let´s say, 20 more apps that will access my content provider. I want each one of my apps to check if the content provider exists. If it does, then just uses the permission to record data. Otherwise, register the provider and then record the data. so,I thought something like including both permission and uses-permission tags in the AndroidManifest.xml file of each app. Is it gonna work ? youtube downloader – Android Hunter Jan 17 '13 at 11:28
  • @MauricioAlencar: "I want each one of my apps to check if the content provider exists. If it does, then just uses the permission to record data. Otherwise, register the provider and then record the data." -- that is an extremely bad plan. Please make all 20 applications completely independent of each other. As it stands, your plan says that if the user happens to uninstall 1 of the 20 -- and that 1 happened to be the 1 that decided to be the `ContentProvider` -- they lose their data for all 20 apps. – CommonsWare Jan 17 '13 at 13:35
  • 1
    @MauricioAlencar: "I thought something like including both permission and uses-permission tags in the AndroidManifest.xml file of each app. Is it gonna work ?" -- that part is fine. In fact, it is generally a good practice in "plugin" sorts of situations. – CommonsWare Jan 17 '13 at 13:37
  • I just found out it worked as I thought, though something strange is happening. Only the app which is run last is listed in the Android Applications list. Android does not list my other apps previously installed (I mean the ones accessing the content provider) youtube downloader – Android Hunter Jan 17 '13 at 13:45
  • 1
    I have a weird issue. If I install receiver app before the content provider app, I get java.lang.SecurityException: Permission Denial: opening provider. If I do it other way around it works well. – Nishant Shah Nov 27 '13 at 06:29
  • 1
    android:exported="true" is the solution. – Abhishek Dhote Jan 15 '16 at 10:28
  • @AbhishekDhote This fixed it for me. Really odd because the docs say `exported=true` is default for content providers, but sure enough the permission denial error went away once I set this explicitly in my `` declaration in the host app's manifest. – Cody Feb 18 '19 at 21:18
  • 1
    @Cody: `true` has not been the default for `android:exported` for a while. For apps with `android:minSdkVersion` and `android:targetSdkVersion` set to 16 or lower, the provider is exported by default. For all other apps, the provider is not exported by default. IOW, it's a mess. – CommonsWare Feb 19 '19 at 00:07
4

I was facing the same problem, then I remember I forgot to

add export tag for provider in the Manifest

   <provider
        android:name=".UsersProvider"
        android:authorities="com.xyz.demoapplication"
        android:exported="true">
   </provider>

To access in another app you need to export true this allows other application to access Content Provider from this host application

Atif Mukhtiar
  • 1,186
  • 9
  • 11
0

I was getting the same issue. Make sure your authorities declaration insider AndroidManifest and URL(in URI.parse(URL)) are the same. It may be the issue.

Aanal Shah
  • 1,946
  • 2
  • 20
  • 30