web-dev-qa-db-fra.com

Android - fournisseur de fichier - refus d'autorisation

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 ?

56
Jake

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);
}
64
limlim

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".

34
CommonsWare

Android <= Lollipop (API 22)

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.

31
Joshua Pinter

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

22
RamiReddy

Comment vous pouvez capturer une image en utilisant la caméra sur Nougat en utilisant File Provider.

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 enter image description here

<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;
}
12
Najaf Ali

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);
3
Rainmaker

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 .

2
Jake

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);
1
Jd Prajapati