Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

permission denial for content provider when called from within the same app

I have only one app. It has a content provider that is not exported (I don't want other apps to have access to the content) I have successfully been using the content provider until now.

I have created a content observer to update a TextView. The onChange method gets called when the content changes and it tries to re-query that content. That's when it gets the security exception that looks like this:

java.lang.SecurityException: Permission Denial: reading org.sil.lcroffline.data.DataProvider uri content://org.sil.lcroffline/users/by_account_name/5555544444 from pid=0, uid=1000 requires the provider be exported, or grantUriPermission()

Here's the code that generates it:

@Override
public void onChange(boolean selfChange, Uri uri) {
    Cursor c = null;
    try {
        c = mContentResolver.query(UserEntry.buildUserPhoneUri(mAccount.name), null, null, null, null);
        // do stuff with the data in the cursor
    } finally {
        if (c != null) c.close();
    }
}

The URI looks like it's formed properly, and I don't think there's a problem with matching it in the content provider.

The code above works fine when called programatically from within the app, it's only when it's triggered by a change in the observed data that the Exception occurs.

How could I get a permission denial from within the same app, and how do I fix this?

Behold the manifest:

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

    <!-- To communicate with LCR -->
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- Required because we're manually creating a new account. -->
    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>

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

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

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".authentication.LoginActivity"
            android:label="@string/app_name"
            android:exported="true">
        </activity>
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main"
            android:theme="@style/AppTheme.NoActionBar"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".ReportActivity"
            android:parentActivityName=".MainActivity"></activity>
        <service android:name=".authentication.AuthenticatorService">
            <intent-filter>
                <action android:name="android.accounts.AccountAuthenticator" />
            </intent-filter>
            <meta-data android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/authenticator" />
        </service>
        <provider
            android:authorities="@string/data_authority"
            android:name=".data.DataProvider"
            android:exported="false" />
        <service
            android:name=".data.SyncService"
            android:exported="true"
            android:process=":sync">
            <intent-filter>
                <action android:name="android.content.SyncAdapter"/>
            </intent-filter>
            <meta-data android:name="android.content.SyncAdapter"
                android:resource="@xml/syncadapter" />
        </service>
    </application>

</manifest>
like image 928
Toby 1 Kenobi Avatar asked Mar 11 '23 08:03

Toby 1 Kenobi


1 Answers

It turns out this is caused by me not using a Handler in the ContentObserver

When you don't pass a handler to the content observer the onChange method gets called directly instead of a message to call it being posted through the handler. This is all well and good, except that the process that calls the onChange method in that situation is the System OS, which doesn't have the necessary permissions to query my content provider.

To fixed this I changed the code where my content observer is created from this:

mUserObserver = new ContentObserver(null) {
    // override onChange methods here
}

To this:

mUserObserver = new ContentObserver(new Handler()) {
    // override onChange methods here
}

I was clued in by this question

like image 121
Toby 1 Kenobi Avatar answered Apr 27 '23 03:04

Toby 1 Kenobi