В рамках нашей цели по постоянному улучшению нашего приложения мы решили пересмотреть разрешения и постараться уменьшить количество элементов, которые мы просим наших пользователей одобрить.

Android не помог делу благодаря своей старой модели разрешений. То, что было введено в M aka Marshmallow, является значительным улучшением для пользователей и разработчиков. Он предлагает разработчикам возможность объяснить пользователям, почему и что мы просим от них в отношении доступа к их устройствам и / или данным.

Но хватит обсуждения Android и модели разрешений. Давайте посмотрим на проблему. Мы добавили FileProvider в наше приложение, чтобы контролировать доступ к файлам в наше приложение и из него.

Проблема заключалась в том, что при завершении действия по фотосъемке приложение «Камера / Галерея» вылетало из строя и вызывалась функция onActivityResult.

java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{423c0ad0 13416:com.google.android.gallery3d/u0a10020} (pid=13416, uid=10020) that is not exported from uid 10079

Пример определения Support v4 FileProvider выглядит следующим образом:

<manifest>
    ...
    <application>
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.mydomain.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            ...
        </provider>
        ...
    </application>
</manifest>

Наивным подходом было бы просто обновить определение FileProvider и установить для атрибута XML export значение true. Это приведет к другому SecurityException.

В конце концов, после долгих размышлений и поисков души я решил попробовать следующий метод, который предоставляется в объекте Context grantUriPermission и revokeUriPermission. .

Решение: предоставьте возможным приложениям разрешение на URI, чтобы завершить действие. В следующем фрагменте кода показано использование PackageManager для поиска действий, которые будут отвечать на ACTION_IMAGE_CAPTURE

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
List<ResolveInfo> resolvedIntentActivities = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolvedIntentInfo : resolvedIntentActivities) {
    String packageName = resolvedIntentInfo.activityInfo.packageName;

    context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

Точно так же необходимо отозвать доступ после того, как вы закончите полагаться на URI.

context.revokeUriPermissionfileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

Я надеюсь, что следующее поможет другим, кто чешет голову и задается вопросом, что же, черт возьми, происходит.

Я приветствую отзывы о формате и моем сообщении о проблеме, я всегда стремлюсь усовершенствовать искусство обмена знаниями.