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

家住魔仙堡 提交于 2019-12-10 12:32:23

问题


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.


回答1:


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




回答2:


The uid value of 1000 belongs to the Android system. Many features of Android involve proxying requests to the system thread for processing. If an exception is thrown during this, the error will include the uid of the system, rather than the original requestor.

For the other points:

UserHandle.isSameApp(uid, mMyUid) is false

UserHandle.isSameUser(uid, mMyUid) is true

These are easiest to explain by looking at the source. On an Android device with multi-user support, each user is defined by a range of UIDs. isSameApp is false because the modulus of the ids do not match:

 public static final boolean isSameApp(int uid1, int uid2) {
        return getAppId(uid1) == getAppId(uid2);
}

 public static final int getAppId(int uid) {
        return uid % PER_USER_RANGE;
}

Similarly, the two ids belong to the same user because they live in the same range:

 public static final boolean isSameUser(int uid1, int uid2) {
        return getUserId(uid1) == getUserId(uid2);
 }

public static final int getUserId(int uid) {
        if (MU_ENABLED) {
            return uid / PER_USER_RANGE;
        } else {
            return 0;
        }
}

Note that this logic is flawed because it means that all Android system uids (< 10000) will be assumed to "belong" to the first user.

Also note that if a second user installs more than 1000 apps(!), there's the possibility that an App will be mistaken for a system app (both uid % PER_USER_RANGE will return 1000). It won't really matter though, because the strong sandboxing would prevent anything too bad from happening.




回答3:


If a Thread is started by any component of the application that has the provider, then you can access the ContentProvider without any SecurityException.

I am using a ContentProvider in my application just as an extra abstraction layer and I haven't exposed the content to other applications. I am accessing the ContentProvider in background thread (Not AsyncTask, but a simple java.lang.Thread). I am not getting any SecurityException. The following is the code from my application.

AndroidManifest.xml

 <provider
    android:authorities="com.sample.provider"
    android:name="com.sample.MyProvider"
    android:exported="false" />

MainActivity

public void performContinue(Bundle extras){
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            String AUTHORITY = "com.sample.provider";
            Uri BASE_URI = Uri.parse("content://" + AUTHORITY);
            Uri currentUri = BASE_URI.buildUpon().appendPath("SAMPLE_COUNT").build();
            final Cursor query = InputActivity.this.getContentResolver().query(currentUri, null, null, null, null);
            if (query != null) {
                final int count = query.getCount();
                Log.d("DEBUG","CONTENT = " + count);
            }else{
                Log.d("DEBUG","CONTENT = CURSOR NULL");
            }
        }
    });
    thread.setName("THREAD_1");
    thread.start();
}


I don't seem to get any SecurityException. Ideally we need to be using AsyncQueryHandler for accessing the ContentProvider, because that allows you do all the fetching process in the background thread and will use the UI Thread to post the results in the UI. But after seeing this post, I just wanted to see if I could just use Thread and check if I could still access it without any Exception. It works fine.



来源:https://stackoverflow.com/questions/31929908/how-would-a-thread-created-by-an-app-be-considered-a-different-app-from-the-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!