I have an app that, when notified by a ContentObserver
of a change to a ContentProvider
, attempts to query the provider on a background thread. This causes an SecurityException
to be thrown:
8-10 15:54:29.577 3057-3200/com.xxxx.mobile.android.xxx W/Binder﹕ Caught a RuntimeException from the binder stub implementation. java.lang.SecurityException: Permission Denial: reading com.xxx.mobile.android.mdk.model.customer.ContentProvider uri content://com.xxx.mobile.android.consumer.xxx/vehicle from pid=0, uid=1000 requires the provider be exported, or grantUriPermission() at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:539) at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:452) at android.content.ContentProvider$Transport.query(ContentProvider.java:205) at android.content.ContentResolver.query(ContentResolver.java:478) at android.content.ContentResolver.query(ContentResolver.java:422)
How would a thread created by an app end up with a different UID from the app's ContentProvider?
By placing an exception breakpoint in android.content.ContentProvider
I see that UserHandle.isSameApp(uid, mMyUid)
is false
and UserHandle.isSameUser(uid, mMyUid)
is true
. I also see that the providers UID is 10087.
I got the same problem while trying to interact with my ContentProvider in a system callback (LeScanCallback
). The problem is that the callback thread is owned by the Android system, and not by my app, even if the code is in my app.
Passing the work from the callback to one of my app threads before trying to interact with my ContentProvider solved the problem successfully.
To reduce boilerplate for thread creation and recycling (needed for frequent callbacks to reduce overhead), I used AndroidAnnotation's @Background
annotation on my delegate method (but would use Kotlin Coroutines today).