13

我有一个应用程序,当 a 通知 aContentObserver的更改时ContentProvider,它会尝试在后台线程上查询提供程序。这会导致 anSecurityException被抛出:

8-10 15:54:29.577 3057-3200/com.xxxx.mobile.android.xxx W/Binder:从活页夹存根实现中捕获了一个 RuntimeException。
  java.lang.SecurityException: Permission Denial: 读取 com.xxx.mobile.android.mdk.model.customer.ContentProvider uri content://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)isfalseUserHandle.isSameUser(uid, mMyUid)is true。我还看到提供者 UID 是 10087。

4

3 回答 3

5

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

对于其他点:

UserHandle.isSameApp(uid, mMyUid) is false

UserHandle.isSameUser(uid, mMyUid) is true

这些最容易通过查看源代码来解释。在支持多用户的 Android 设备上,每个用户都由一系列 UID 定义。isSameApp为假,因为 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 系统 uid (< 10000) 都将被假定为“属于”第一个用户。

另请注意,如果第二个用户安装了超过 1000 个应用程序(!),则应用程序可能会被误认为是系统应用程序(两者都uid % PER_USER_RANGE将返回 1000)。不过这并不重要,因为强大的沙盒可以防止任何糟糕的事情发生。

于 2015-08-10T23:13:07.583 回答
3

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

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

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

于 2015-11-27T14:22:08.113 回答
2

如果 aThread由具有提供程序的应用程序的任何组件启动,那么您可以访问ContentProvider没有任何SecurityException.

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

AndroidManifest.xml

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

主要活动

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并检查我是否仍然可以在没有任何异常的情况下访问它。它工作正常。

于 2015-10-30T16:36:57.583 回答