Android - поставщик файлов - отказ в разрешении
У меня есть два приложения: app1 и app2.
App2 имеет:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.android.provider.ImageSharing"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths" />
</provider>
paths.xml:
<paths>
<files-path name="my_images" path="images/"/>
</paths>
App2 получает запрос в своей деятельности из App1, чтобы получить URI для изображения. Активность App2 делает следующее, как только URI определяется:
Intent intent = new Intent();
intent.setDataAndType(contentUri, getContentResolver().getType(contentUri));
int uid = Binder.getCallingUid();
String callingPackage = getPackageManager().getNameForUid(uid);
getApplicationContext().grantUriPermission(callingPackage, contentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
setResult(Activity.RESULT_OK, intent);
finish();
При получении результата из App2, App1 выполняет следующие действия:
Uri imageUri = data.getData();
if(imageUri != null) {
ImageView iv = (ImageView) layoutView.findViewById(R.id.imageReceived);
iv.setImageURI(imageUri);
}
В App1, возвращаясь из App2, я получаю следующее исключение:
java.lang.SecurityException: отказ в разрешении: открытие провайдер android.support.v4.content.FileProvider от ProcessRecord {52a99eb0 3493: com.android.App1.app/u0a57} (pid = 3493, uid = 10057), который не экспортируется из uid 10058
Что я делаю неправильно?
Ответы
Ответ 1
Оказывается, единственный способ решить это - предоставить разрешения всем пакетам, которые могут ему понадобиться, например:
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
Ответ 2
Во-первых, я попытался бы отключиться от grantUriPermission()
и просто поставить FLAG_GRANT_READ_URI_PERMISSION
на Intent
через addFlags()
или setFlag()
.
Если по какой-то причине это не сработает, вы можете попробовать переместить логику getCallingUid()
в onCreate()
вместо того, где она есть, и посмотреть, можете ли вы найти там фактического "вызывающего".
Ответ 3
Android <= Lollipop (API 22)
Есть отличная статья Лоренцо Квироли, которая решает эту проблему для более старых версий Android.
Он обнаружил, что вам нужно вручную установить ClipData для Intent и установить разрешения для него, например, так:
if ( Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP ) {
takePictureIntent.setClipData( ClipData.newRawUri( "", photoURI ) );
takePictureIntent.addFlags( Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION );
}
Я проверил это на API 17, и он отлично работал. Не удалось найти решение, где бы ни работало.
Ответ 4
Просто добавьте
setData(contentUri);
а также
на основе требования добавить
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
или
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Это разрешает java.lang.SecurityException: отказ от разрешения
Проверенный.
Это делается согласно
https://developer.android.com/reference/android/support/v4/content/FileProvider.html#Permissions
Ответ 5
Как вы можете захватить изображение с помощью камеры на Nougat, используя File Provider.
Чтобы прочитать о провайдере файла по этой ссылке Provider файла
и кит кат и зефир следуйте этим шагам. Прежде всего, провайдер рекламных тегов под тегом приложения в MainfestFile.
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
создайте имя файла с помощью (provider_paths.xml) в папке res ![enter image description here]()
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
Я решил эту проблему, это идет на версии Kit KitKat
private void takePicture() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
Uri photoURI = null;
try {
File photoFile = createImageFileWith();
path = photoFile.getAbsolutePath();
photoURI = FileProvider.getUriForFile(MainActivity.this,
getString(R.string.file_provider_authority),
photoFile);
} catch (IOException ex) {
Log.e("TakePicture", ex.getMessage());
}
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
takePictureIntent.setClipData(ClipData.newRawUri("", photoURI));
takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
startActivityForResult(takePictureIntent, PHOTO_REQUEST_CODE);
}
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM), "Camera");
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
return image;
}
Ответ 6
Спасибо, @CommonsWare за этот совет.
Моя проблема была с пакетом вызова. По какой-то причине Binder.callingUid()
и getPackageManager().getNameForUid(uid)
давали мне имя пакета App2
вместо App1
.
Я пытался вызвать его в App2 onCreate
а также onResume
, но без радости.
Я использовал следующее, чтобы решить это:
getApplicationContext().grantUriPermission(getCallingPackage(),
contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Оказывается, у деятельности есть специальный API для этого. Смотрите здесь
Ответ 7
Я решил проблему таким образом:
Intent sIntent = new Intent("com.appname.ACTION_RETURN_FILE").setData(uri);
List<ResolveInfo> resInfoList = activity.getPackageManager().queryIntentActivities(sIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
activity.grantUriPermission(FILE_PROVIDER_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
sIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.setResult(RESULT_OK, sIntent);
Ответ 8
Вам нужно установить разрешение определенного имени пакета, после чего вы сможете получить к нему доступ.
context.grantUriPermission("com.android.App1.app", fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
Ответ 9
java.lang.SecurityException: отказ от разрешения:...
Смотрите fooobar.com/info/202492/... (Android добавила новую модель разрешения для Android 6.0 Marshmallow)
Запросить разрешения во время выполнения:
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions, WRITE_REQUEST_CODE);