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>
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With