Search code examples
androidsqliteandroid-contentprovider

Can't access a customProvider from another app. It says it's missing permissions it already has


I'm trying to make an app that access a SQLite table from another app with a custom content provider. I'm getting the following error:

java.lang.SecurityException: Permission Denial: reading com.example.carlos.gymlog.MiContentProvider uri content://mi.uri.porquesi/sesiones from pid=2375, uid=10064 requires android.permission.permRead, or grantUriPermission()

However, my "sender" app (the one with the database) already has the required permissions:

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/MiTema" >
        <activity
            android:name=".Principal"
            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="MiContentProvider"
            android:authorities="mi.uri.porquesi"
            android:readPermission="android.permission.permRead"
            android:exported="true"
            android:grantUriPermissions="true"> </provider>

    </application>

</manifest>

This is the code that causes the error in the "receiver" app (the one that tries to access the other):

ArrayList<Sesion> sacarDatos(Context c){

        Uri cUri = Uri.parse("content://mi.uri.porquesi/sesiones");
        ContentProviderClient miCP = c.getContentResolver().acquireContentProviderClient(cUri);

        ArrayList<Sesion> sesiones = null;
        try {
            Cursor cur  = miCP.query(cUri, null, null, null, null);
            sesiones = new ArrayList<Sesion>();
            cur.moveToFirst();
            Sesion sesion = null;
            do {
                sesion = new Sesion(cur.getInt(0),cur.getInt(1),cur.getInt(2),cur.getInt(3));
                sesiones.add(sesion);
            } while (cur.moveToNext());

        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return sesiones;
    }

And lastly, this is my custom content provider:

public class MiContentProvider extends android.content.ContentProvider {

    private static final String uri = "content://mi.uri.porquesi";

    public static final Uri CONTENT_URI = Uri.parse(uri);


    @Override
    public boolean onCreate() {

        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

        BaseDatosHelper miBD = new BaseDatosHelper(getContext(), "SESIONES", null, 1);
        SQLiteDatabase db = miBD.getWritableDatabase();
        SQLiteQueryBuilder qb  = new SQLiteQueryBuilder();
        qb.setTables("SESIONES");

        Cursor cur = qb.query(db,projection,selection,selectionArgs,"","",sortOrder);
        cur.setNotificationUri(getContext().getContentResolver(),uri);
        return cur;
    }

(the rest of the methods like insert() aren't implemented, they just return null or whatever it is, I just want to select data not insert it).

EDIT:

My app A has

 <provider android:name="MiContentProvider"
        android:authorities="mi.uri.porquesi"
        android:exported="true"
        android:permission="mi.permision"
        android:grantUriPermissions="true"> </provider>
    <permission
        android:name="mi.permision"
        />

( just a random temporal name)

In app B, I've included

<uses-permission android:name="mi.permision"/>

However, now I get:

java.lang.SecurityException: Permission Denial: opening provider com.example.carlos.gymlog.MiContentProvider from ProcessRecord{335efe6e 3116:com.example.carlos.sample/u0a67} (pid=3116, uid=10067) requires mi.permision or mi.permision

So it recognizes that I need my custom permission, but it doesn't recognize that my app B is using it. Why??


Solution

  • Let's say that we have App A and App B. App A has the ContentProvider; App B wants to use the ContentProvider from App A. And, you want to defend the ContentProvider, with a custom permission.

    App A needs a <permission> element in its manifest, with your custom permission name in the android:name attribute. DO NOT use an android.permission prefix on that custom permission name, as you are not a developer on the Android Open Source Project. Use something namespaced for your app, such as com.kace91.permission.AND_REMEMBER_THAT_CUSTOM_PERMISSIONS_HAVE_SECURITY_ISSUES.

    App A then needs an attribute, on the <provider> manifest element, that has a value matching the name of the android:name attribute of the <permission> element. Either use android:permission to defend all operations, or have two custom permissions and use android:readPermission and android:writePermission. DO NOT simply use android:readPermission, as that means that any app can modify the ContentProvider without permission, and it is rather unlikely that this is what the user wants.

    App A also needs an android:exported="true" attribute on the <provider> element, to say that third party apps can initiate communications with that ContentProvider.

    App B can then have a <uses-permission> manifest element, with an android:name attribute value matching the android:name attribute of the <permission> element from App A. If you used two permissions, for read and write, App B would need two <uses-permission> elements.

    Then, if App A is installed before App B, and the user agrees to grant App B permission to hold the custom permission, App B will be able to perform the designated operations on App A's ContentProvider.

    And, as noted in my comment, there are security issues with custom permissions.