Как поток, созданный приложением, можно рассматривать как другое приложение из приложения ContentProvider?
У меня есть приложение, которое после уведомления ContentObserver
об изменении на ContentProvider
пытается запросить поставщика в фоновом потоке. Это вызывает выброс SecurityException
:
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)
Как бы поток, созданный приложением в конечном итоге с другим UID из приложения ContentProvider?
Поместив контрольную точку исключения в android.content.ContentProvider
, я вижу, что UserHandle.isSameApp(uid, mMyUid)
- false
и UserHandle.isSameUser(uid, mMyUid)
- true
. Я также вижу, что UID провайдеров составляет 10087.
Ответы
Ответ 1
У меня такая же проблема, когда я пытаюсь взаимодействовать с моим ContentProvider в системном обратном вызове (LeScanCallback
). Проблема в том, что поток обратного вызова принадлежит системе Android, а не моему приложению, даже если код находится в моем приложении.
Передача работы из обратного вызова в один из моих приложений, прежде чем пытаться взаимодействовать с моим ContentProvider, решила проблему успешно.
Чтобы уменьшить шаблон для создания и повторного использования потоков (необходимо для частых обратных вызовов для уменьшения накладных расходов), я использовал Андроид аннуляции @Background
моего делегата метод.
Ответ 2
Значение uid 1000 принадлежит системе Android. Многие функции Android включают в себя запросы проксирования в системный поток для обработки. Если во время этого возникает исключение, ошибка будет включать в себя uid системы, а не исходный запросчик.
Для других точек:
UserHandle.isSameApp(uid, mMyUid) is false
UserHandle.isSameUser(uid, mMyUid) is true
Это проще всего объяснить, посмотрев источник . На Android-устройстве с многопользовательской поддержкой каждый пользователь определяется рядом UID. isSameApp
неверно, потому что модуль идентификаторов не совпадает:
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;
}
Аналогично, два идентификатора принадлежат одному и тому же пользователю, поскольку они живут в одном диапазоне:
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-системы uids (< 10000) будут считаться "принадлежащими" первому пользователю.
Также обратите внимание, что если второй пользователь устанавливает более 1000 приложений (!), там вероятность того, что приложение будет ошибочно принята за системное приложение (оба uid % PER_USER_RANGE
вернут 1000). На самом деле это не имеет значения, потому что сильная песочница будет предотвращать что-то слишком плохое.
Ответ 3
Если a Thread
запускается любым компонентом приложения, у которого есть поставщик, вы можете получить доступ к ContentProvider
без каких-либо SecurityException
.
Я использую ContentProvider
в своем приложении как дополнительный уровень абстракции, и я не показывал контент другим приложениям. Я обращаюсь к ContentProvider
в фоновом потоке (Not 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
, поскольку это позволяет вам выполнить весь процесс выборки в фоновом потоке и использовать поток пользовательского интерфейса для публикации результатов в пользовательском интерфейсе. Но, увидев этот пост, я просто хотел посмотреть, могу ли я просто использовать Thread
и проверить, могу ли я получить к нему доступ без исключения. Он отлично работает.