J'ai deux applications: app1 et app2.
App2 a:
<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 reçoit une demande d'App1 dans son activité pour obtenir l'URI d'une image. L’activité App2 effectue les opérations suivantes une fois l’URI définie:
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();
Lors de la réception du résultat de App2, App1 effectue les opérations suivantes:
Uri imageUri = data.getData();
if(imageUri != null) {
ImageView iv = (ImageView) layoutView.findViewById(R.id.imageReceived);
iv.setImageURI(imageUri);
}
Dans App1, à mon retour d'App2, j'obtiens l'exception suivante:
Java.lang.SecurityException: refus d'autorisation: ouverture du fournisseur Android.support.v4.content.FileProvider de ProcessRecord {52a99eb0 3493: com.Android.App1.app/u0a57} (pid = 3493, uid = 10057) non exporté. uid 10058
Qu'est-ce que je fais mal ?
Il s'avère que la seule façon de résoudre ce problème consiste à accorder des autorisations à tous les paquets qui pourraient en avoir besoin, comme ceci:
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);
}
Tout d’abord, j’essayerais de changer de grantUriPermission()
et de placer simplement le FLAG_GRANT_READ_URI_PERMISSION
Sur le Intent
lui-même via addFlags()
ou setFlag()
.
Si pour une raison quelconque, cela ne fonctionne pas, vous pouvez essayer de déplacer votre logique getCallingUid()
dans onCreate()
au lieu de l'endroit où vous l'avez et voir si vous pouvez trouver le véritable "appelant".
Il y a un excellent article de Lorenzo Quiroli qui résout ce problème pour les anciennes Android versions.
Il a découvert que vous devez définir manuellement le ClipData de l’intention et définir les autorisations correspondantes, comme suit:
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 );
}
J'ai testé cela sur API 17 et cela a très bien fonctionné. Impossible de trouver une solution qui fonctionne.
Ajoutez simplement setData(contentUri);
et, en fonction des besoins, ajoutez addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
ou addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Cela résout l'exception Java.lang.SecurityException: refus d'autorisation.
Vérifié.
Ceci est effectué conformément à https://developer.Android.com/reference/Android/support/v4/content/FileProvider.html#Permissions
Pour en savoir plus sur le fournisseur de fichiers, suivez ce lien Fournisseur de fichiers
, et kit kat and Marshmallow suivent ces étapes. Tout d'abord, le fournisseur de balises d'annonce sous la balise d'application dans 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>
créez un nom de fichier avec (provider_paths.xml) sous le dossier res
<paths xmlns:Android="http://schemas.Android.com/apk/res/Android">
<external-path name="external_files" path="."/>
J'ai résolu ce problème, il vient sur la version kitKat Kit
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;
}
J'ai résolu le problème de cette façon:
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);
Merci, @CommonsWare pour ce conseil.
Mon problème était avec le forfait d'appel. Pour une raison quelconque, Binder.callingUid()
et getPackageManager().getNameForUid(uid)
me donnaient le nom du package App2
au lieu de App1
.
J'ai essayé de l'appeler dans onCreate
de App2 ainsi que dans onResume
, mais pas de joie.
J'ai utilisé ce qui suit pour le résoudre:
getApplicationContext().grantUriPermission(getCallingPackage(),
contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Il s'avère que l'activité a une API dédiée à cela. Voir ici .
Vous devez définir l'autorisation du nom du paquet spécifique, après quoi vous pourrez y accéder.
context.grantUriPermission("com.Android.App1.app", fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);