Search code examples
androidandroid-contentprovideruidandroid-securityexception

How would a thread created by an app be considered a different app from the app's ContentProvider?


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.


Solution

  • 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).