Подтвердить что ты не робот

Как поток, созданный приложением, можно рассматривать как другое приложение из приложения 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.

4b9b3361

Ответ 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 и проверить, могу ли я получить к нему доступ без исключения. Он отлично работает.