应用创建的线程如何被视为与应用的内容提供程序不同的应用?



我有一个应用程序,当由ContentObserver通知更改到ContentProvider时,试图在后台线程上查询提供者。这会导致抛出一个SecurityException:

<>之前8-10 15:54:29.577 3057-3200/com.xxxx.mobile.android。从绑定器存根实现中捕获了一个RuntimeException。java.lang.SecurityException: Permission Denial:读取com.xxx.mobile.android.mdk.model.customer.ContentProvider uri内容://com.xxx.mobile.android.consumer。xxx/vehicle from pid=0, uid=1000要求导出提供程序,或者grantUriPermission()android.content.ContentProvider.enforceReadPermissionInner (ContentProvider.java: 539)android.content.ContentProvider Transport.enforceReadPermission美元(ContentProvider.java: 452)android.content.ContentProvider Transport.query美元(ContentProvider.java: 205)android.content.ContentResolver.query (ContentResolver.java: 478)android.content.ContentResolver.query (ContentResolver.java: 422)之前

一个应用程序创建的线程如何与应用程序的ContentProvider的不同UID结束?

通过在android.content.ContentProvider中放置异常断点,我看到UserHandle.isSameApp(uid, mMyUid)false, UserHandle.isSameUser(uid, mMyUid)true。我还看到提供者的UID是10087。

uid值1000属于Android系统。Android的许多特性都涉及到将请求代理到系统线程进行处理。如果在此期间抛出异常,错误将包括系统的uid,而不是原始请求者。

其他点:

UserHandle.isSameApp(uid, mMyUid) is false

UserHandle.isSameUser(uid, mMyUid) is true

这些最容易通过查看源代码来解释。在支持多用户的Android设备上,每个用户由一系列uid定义。isSameApp为false,因为id的模数不匹配:

 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;
}

同样,这两个id属于同一个用户,因为它们位于同一个范围内:

 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;
        }
}

请注意,这个逻辑是有缺陷的,因为它意味着所有的Android系统id (<10000)将被认为"属于"第一个用户。

还要注意,如果第二个用户安装了超过1000个应用程序(!),那么应用程序就有可能被误认为是系统应用程序(两个uid % PER_USER_RANGE都会返回1000)。不过这并不重要,因为强大的沙箱会防止任何太糟糕的发生。

在系统回调(LeScanCallback)中尝试与我的ContentProvider交互时,我遇到了同样的问题。问题是回调线程由Android系统拥有,而不是由我的应用程序拥有,即使代码在我的应用程序中。

在尝试与我的ContentProvider交互之前,将工作从回调传递给我的一个应用线程,成功地解决了这个问题。

为了减少线程创建和回收的模板(需要频繁回调以减少开销),我在我的委托方法上使用了AndroidAnnotation的@Background注释(但今天将使用Kotlin Coroutines)。

如果Thread是由具有该提供者的应用程序的任何组件启动的,那么您可以在没有SecurityException的情况下访问ContentProvider

我在我的应用程序中使用ContentProvider只是作为一个额外的抽象层,我还没有将内容暴露给其他应用程序。我在后台线程访问ContentProvider(不是AsyncTask,而是一个简单的java.lang.Thread)。我没有得到任何SecurityException。以下是我的应用程序的代码:

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();
}


我似乎没有得到任何SecurityException。理想情况下,我们需要使用AsyncQueryHandler来访问ContentProvider,因为这允许你在后台线程中完成所有的抓取过程,并将使用UI线程在UI中发布结果。但是在看到这篇文章之后,我只是想看看我是否可以使用Thread,并检查我是否仍然可以毫无例外地访问它。

相关内容

  • 没有找到相关文章

最新更新