Android – proveedor de archivos – denegación de permiso

Tengo dos aplicaciones: app1 y app2.

App2 tiene:

   

paths.xml:

    

App2 recibe una solicitud en su Actividad de la App1 para obtener URI para una imagen. La actividad de App2 hace lo siguiente una vez que se decide el 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(); 

Al recibir el resultado de App2, App1 hace lo siguiente:

 Uri imageUri = data.getData(); if(imageUri != null) { ImageView iv = (ImageView) layoutView.findViewById(R.id.imageReceived); iv.setImageURI(imageUri); } 

En App1, al regresar de App2, recibo la siguiente excepción:

java.lang.SecurityException: denegación de permiso: proveedor de apertura android.support.v4.content.FileProvider de ProcessRecord {52a99eb0 3493: com.android.App1.app/u0a57} (pid = 3493, uid = 10057) que no se exporta desde uid 10058

Qué estoy haciendo mal ?

Resulta que la única forma de resolver esto es otorgando permisos a todos los paquetes que puedan necesitarlo, como este:

 List 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); } 

Primero, trataría de grantUriPermission() de grantUriPermission() y simplemente poner el FLAG_GRANT_READ_URI_PERMISSION en el Intent mismo a través de addFlags() o setFlag() .

Si por alguna razón eso no funciona, puede intentar mover su lógica getCallingUid() a onCreate() lugar de donde la tenga, y ver si puede encontrar la “persona que llama” allí.

Solo agregue setData(contentUri); y según el requisito, agregue addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); o addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

Esto resuelve la java.lang.SecurityException: denegación de permiso

Verificado

Esto se hace de acuerdo con https://developer.android.com/reference/android/support/v4/content/FileProvider.html#Permissions

Android <= Lollipop (API 22)

Hay un excelente artículo de Lorenzo Quiroli que resuelve este problema para versiones anteriores de Android.

Descubrió que debe configurar manualmente el ClipData del Intento y establecer los permisos para él, así:

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

Probé esto en API 17 y funcionó muy bien. No se pudo encontrar una solución en ningún lado que funcionó.

Cómo capturas imágenes usando una cámara en Nougat

y kit kat y malvavisco sigue estos pasos. En primer lugar, proveedor de tags publicitarias bajo etiqueta de aplicación en MainfestFile.

     

crea un nombre de archivo con (provider_paths.xml) debajo de la carpeta res enter image description here

   

He resuelto este problema, viene en kit kitkat versión

 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; } 

Gracias @CommonsWare por su consejo.

El problema fue con el paquete de llamadas. Por alguna razón, Binder.callingUid() y getPackageManager().getNameForUid(uid) me está dando el nombre del paquete de App2 lugar de App1 .

Intenté llamarlo en OnCreate de onCreate así como también en onResume , pero no me onResume .

Usé lo siguiente para resolverlo:

 getApplicationContext().grantUriPermission(getCallingPackage(), contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 

Resulta que la actividad tiene API dedicada para esto. Mira aquí .

Solucioné el problema de esa manera:

  Intent sIntent = new Intent("com.appname.ACTION_RETURN_FILE").setData(uri); List 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); 

Debe configurar el nombre del paquete específico, luego podrá acceder a él.

 context.grantUriPermission("com.android.App1.app", fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); 

java.lang.SecurityException: denegación de permiso: …

Consulte https://stackoverflow.com/a/44455957/966789 (Android agregó un nuevo modelo de permiso para Android 6.0 Marshmallow)

Para solicitar permisos en tiempo de ejecución:

 String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; requestPermissions(permissions, WRITE_REQUEST_CODE);