androidandroid-roomandroid-contentproviderandroid-contentresolver

Failed to find provider info for com.example.serialprovider.provider.SampleProvider


I've been trying to get data from another app's custom ContentProvider class but I keep getting this error: Failed to find provider info for com.example.serialprovider.provider.SampleProvider..

I searched a lot for similar issues online but still didn't know what's wrong, I checked the manifest multiple times, and I even took a copy of the authorities attribute to use it in the receiver app but still, the receiver app can't find the provider.

Here's the declaration in the manifest:

<provider
    android:name=".provider.SampleProvider"
    android:authorities="com.example.serialprovider.provider.SampleProvider"
    android:enabled="true"
    android:exported="true" />

and here's the implementation of onCreate and query methods in the Provider class (I'm using RoomDatabase):

public class SampleProvider extends ContentProvider {
    
    public SampleProvider() {
    }

    private static final String AUTHORITY = "com.example.serialprovider.provider.SampleProvider";
    private static final String TABLE_NAME = "devicepin";

    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        sURIMatcher.addURI(AUTHORITY, TABLE_NAME, 1);
    }

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

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        if (sURIMatcher.match(uri) == 1) {
            final Context context = getContext();
            AppDao dao = DatabaseClient.getInstance(context).getAppDatabase().appDao();
            final Cursor cursor = dao.get();
            cursor.setNotificationUri(getContext().getContentResolver(), uri);
            cursor.close();
            return cursor;
        } else {
            throw new IllegalArgumentException("Unknown URI: " + uri);
        }
    }
}

and here's how I try to get the cursor in the other app "receiver":

private void getPin(){
    new Thread(() -> {
        ContentResolver resolver = getContentResolver();
        try{
            Cursor cursor = resolver.query(Uri.parse("content://com.example.serialprovider.provider.SampleProvider/devciepin"), null, null, null, null);
            cursor.close();
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }).start();
}

cursor is always null, when I surround it with try and catch blocks, the "failed to find provider info" is what I get as an exception.


Solution

  • The code turned out to be alright, but there are some new restrictions that were introduced in Android 11 (API 30) on accessing the ContentProvider from another app. You can check out the behavior changes in the documentation.

    1- Add this permission to the provider app's manifest as a sibling for the tag:

    <permission android:name="com.example.myprovider.READ_PIN" />
    

    2- Do this inside the provider app's manifest as a sibling for the tags:

    <provider
          android:name=".provider.SampleContentProvider"
          android:authorities="com.example.app_package_name.provider"
          android:exported="true"
          android:readPermission="com.example.app_package_name.READ_PIN" />
    

    N.B: you can skip #1 and remove the last line in #2 if you want your app's content to be exposed to all the other apps regardless of whether they grant the permission or not, but it's not a safe practice and you'll get a warning.

    3- add these as siblings to the tag in the reader app's manifest:

    <uses-permission android:name="com.example.myprovider.READ_PIN" />
    
    <queries> 
       <package android:name="com.example.your_provider_name" /> 
    </queries>