Android M Camera Intent + error de permiso?

Estoy tratando de preparar mi aplicación para los nuevos cambios en los permisos de Android M y encontré un comportamiento extraño. Mi aplicación utiliza el mecanismo de intención de la cámara para permitir que el usuario obtenga una imagen de la cámara. Pero en otra actividad necesita hacer uso de la cámara misma con permiso de cámara (debido a una tarjeta de dependencia de biblioteca.io que requiere esto).

Sin embargo, con M en la actividad que solo necesita la intención de la cámara cuando bash lanzar la intención de la cámara, veo el siguiente locking (esto no ocurre si elimino el permiso de la cámara del Manifiesto),

> 09-25 21:57:55.260 774-8053/? I/ActivityManager: START u0 > {act=android.media.action.IMAGE_CAPTURE flg=0x3000003 > pkg=com.google.android.GoogleCamera > cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity > (has clip) (has extras)} from uid 10098 on display 0 09-25 > 21:57:55.261 774-8053/? W/ActivityManager: Permission Denial: starting > Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3000003 > pkg=com.google.android.GoogleCamera > cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity > (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked > permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/? > E/ResolverActivity: Unable to launch as uid 10098 package > com.example.me.mycamerselectapp, while running in android:ui 09-25 > 21:57:55.263 32657-32657/? E/ResolverActivity: > java.lang.SecurityException: Permission Denial: starting Intent { > act=android.media.action.IMAGE_CAPTURE flg=0x3000003 > pkg=com.google.android.GoogleCamera > cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity > (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked > permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/? > E/ResolverActivity: at > android.os.Parcel.readException(Parcel.java:1599) 09-25 21:57:55.263 > 32657-32657/? E/ResolverActivity: at > android.os.Parcel.readException(Parcel.java:1552) 09-25 21:57:55.263 > 32657-32657/? E/ResolverActivity: at > android.app.ActivityManagerProxy.startActivityAsCaller(ActivityManagerNative.java:2730) > 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at > android.app.Instrumentation.execStartActivityAsCaller(Instrumentation.java:1725) > 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at > android.app.Activity.startActivityAsCaller(Activity.java:4047) 09-25 > 21:57:55.263 32657-32657/? E/ResolverActivity: at > com.android.internal.app.ResolverActivity$DisplayResolveInfo.startAsCaller(ResolverActivity.java:983) > 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at > com.android.internal.app.ResolverActivity.safelyStartActivity(ResolverActivity.java:772) > 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at > com.android.internal.app.ResolverActivity.onTargetSelected(ResolverActivity.java:754) > 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at > com.android.internal.app.ChooserActivity.onTargetSelected(ChooserActivity.java:305) > 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at > com.android.internal.app.ResolverActivity.startSelected(ResolverActivity.java:603) > 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at > com.android.internal.app.ChooserActivity.startSelected(ChooserActivity.java:310) > 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at > com.android.internal.app.ChooserActivity$ChooserRowAdapter$2.onClick(ChooserActivity.java:990) > 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at > android.view.View.performClick(View.java:5198) 09-25 21:57:55.263 > 32657-32657/? E/ResolverActivity: at > android.view.View$PerformClick.run(View.java:21147) 09-25 21:57:55.263 > 32657-32657/? E/ResolverActivity: at > android.os.Handler.handleCallback(Handler.java:739) 09-25 21:57:55.263 > 32657-32657/? E/ResolverActivity: at > android.os.Handler.dispatchMessage(Handler.java:95) 09-25 21:57:55.263 > 32657-32657/? E/ResolverActivity: at > android.os.Looper.loop(Looper.java:148) 09-25 21:57:55.263 > 32657-32657/? E/ResolverActivity: at > android.app.ActivityThread.main(ActivityThread.java:5417) 09-25 > 21:57:55.263 32657-32657/? E/ResolverActivity: at > java.lang.reflect.Method.invoke(Native Method) 09-25 21:57:55.263 > 32657-32657/? E/ResolverActivity: at > com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) > 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at > com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 09-25 > 21:57:55.286 1159-1159/? I/Keyboard.Facilitator: onFinishInput() 09-25 > 21:57:55.297 32657-32676/? E/Surface: getSlotFromBufferLocked: unknown > buffer: 0xaec352e0 09-25 21:57:55.344 325-349/? V/RenderScript: > 0xb3693000 Launching thread(s), CPUs 4 09-25 21:57:57.290 325-349/? > E/Surface: getSlotFromBufferLocked: unknown buffer: 0xb3f88240 

¿Es este un problema conocido con Android M? Y más importante aún, ¿cómo puedo solucionar esto?

en el manifiesto tengo lo siguiente,

  

y este es el código que uso para permitir que el usuario haga clic en una imagen con la cámara y / o seleccione una imagen

 public static Intent openImageIntent(Context context, Uri cameraOutputFile) { // Camera. final List cameraIntents = new ArrayList(); final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); final PackageManager packageManager = context.getPackageManager(); final List listCam = packageManager.queryIntentActivities(captureIntent, 0); for(ResolveInfo res : listCam) { final String packageName = res.activityInfo.packageName; final Intent intent = new Intent(captureIntent); intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name)); intent.setPackage(packageName); intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraOutputFile); cameraIntents.add(intent); } // Filesystem. final Intent galleryIntent = new Intent(); galleryIntent.setType("image/*"); galleryIntent.setAction(Intent.ACTION_GET_CONTENT); // Chooser of filesystem options. final Intent chooserIntent = Intent.createChooser(galleryIntent, "Take or select pic"); // Add the camera options. chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{})); return chooserIntent; } 

Llamo a openImageIntent() en un clic de botón en mi actividad. Cuando no tengo el permiso CAMERA en mi aplicación funciona bien, pero con eso agregado obtengo la excepción publicada anteriormente.

  @Override public void onClick(View v) { Intent picCaptureIntenet = openImageIntent(MainActivity.this, getTempImageFileUri(MainActivity.this)); try { startActivityForResult(picCaptureIntenet, 100); } catch(Exception e) { Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); } } 

Tuve el mismo problema y encontré este documento de Google: https://developer.android.com/reference/android/provider/MediaStore.html#ACTION_IMAGE_CAPTURE

“Nota: si su aplicación apunta a M y más arriba y declara que usa el permiso CAMERA que no se concede, entonces intentar utilizar esta acción dará como resultado una SecurityException”.

Esto es muy extraño. No tiene sentido en absoluto. La aplicación declara el permiso de la cámara con intención con la acción IMAGE_CAPTURE acaba de ejecutar en SecurityException. Pero si su aplicación no declara el permiso de la cámara con intención de acción, IMAGE_CAPTURE puede iniciar la aplicación de la cámara sin problemas.

La solución alternativa sería verificar si la aplicación tiene permiso de cámara incluido en el manifiesto, si es así, solicite permiso de cámara antes de iniciar el bash.

Esta es la forma de verificar si el permiso está incluido en el manifiesto, no importa si el permiso es otorgado o no.

 public boolean hasPermissionInManifest(Context context, String permissionName) { final String packageName = context.getPackageName(); try { final PackageInfo packageInfo = context.getPackageManager() .getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); final String[] declaredPermisisons = packageInfo.requestedPermissions; if (declaredPermisisons != null && declaredPermisisons.length > 0) { for (String p : declaredPermisisons) { if (p.equals(permissionName)) { return true; } } } } catch (NameNotFoundException e) { } return false; } 

Si está utilizando el modelo de permiso de Android M, primero debe verificar si la aplicación tiene este permiso durante el tiempo de ejecución y debe solicitar al usuario este permiso durante el tiempo de ejecución. El permiso que defina en su manifiesto no se otorgará automáticamente en el momento de la instalación.

 if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.CAMERA}, MY_REQUEST_CODE); } 

MY_REQUEST_CODE es una constante estática que puede definir, que se utilizará nuevamente para la callback del diálogo requestPermission.

Necesitará una callback para el resultado del diálogo:

 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == MY_REQUEST_CODE) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Now user should be able to use camera } else { // Your app will not have this permission. Turn off all functions // that require this permission or it will force close like your // original question } } } 

editar

Al leer del seguimiento de la stack, parece que Google Camera no tiene habilitado el permiso CAMERA. Esto en realidad podría parecer una cosa de compatibilidad con versiones anteriores después de todo.

Supongamos que Google Camera (o cualquier otra aplicación que maneje su intención de ACTION) requiere un cierto permiso.

Cuando su aplicación no tiene el permiso de CAMERA, solo permite que Google Camera lo haga con el modelo de permisos anterior.

Sin embargo, con el permiso CAMERA declarado en su manifiesto, también se aplica el permiso CAMERA dentro de la cámara de Google (que no tiene el modelo de permisos de Android M) para usar el modelo de permisos de Android M (creo).

Así que eso significa usar el método anterior, deberá proporcionar el permiso de su aplicación durante el tiempo de ejecución, lo que significa que su tarea hija (en este caso, la Cámara de Google) ahora también tendrá ese permiso.

En cuanto a su pregunta ‘¿Es este un problema conocido en M?’ Un desarrollador de Google respondió que alguien informaba este problema como un error.

Consulte aquí: https://code.google.com/p/android/issues/detail?id=188073&q=label%3APriority-Medium&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&start=100

Aquí está la palabra del chico de Google: “Este es el comportamiento previsto para evitar la frustración del usuario, donde revocaron el permiso de la cámara de una aplicación y la aplicación todavía es capaz de tomar fotos a través de la intención. Los usuarios no son conscientes de que la foto tomada después de la revocación del permiso ocurre a través de un mecanismo diferente y cuestionaría la exactitud del modelo de permiso. Esto se aplica a MediaStore.ACTION_IMAGE_CAPTURE, MediaStore.ACTION_VIDEO_CAPTURE e Intent.ACTION_CALL los documentos para los que se documenta el cambio de comportamiento para las aplicaciones que se dirigen a M. ”

Dado que a Google no le importa abstraer la mecánica de usar la cámara de su usuario, también podría activar estratégicamente la primera solicitud de permiso de cámara y hacer referencia a la funcionalidad de la Actividad que usa la Cámara como su razonamiento para la solicitud. Si permite que su aplicación primero realice esta solicitud de permiso cuando el usuario simplemente intenta tomar una fotografía, el usuario puede pensar que su aplicación se comporta de manera extraña, ya que tomar una foto generalmente no requiere la concesión de un permiso.

Si está usando Google M, vaya a Configuración -> Aplicaciones -> su aplicación -> y otorgue los permisos correspondientes.

Me quedé atascado en este problema y ya estaba usando la respuesta de JTY. El problema es que en algún momento se marcó el diálogo de permiso de solicitud en “Nunca preguntar de nuevo”. Estoy desarrollando en SDK 24.

Mi código completo para manejar permisos (la cámara en mi caso) fue el siguiente:

 public void checksCameraPermission(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Log.d("MyApp", "SDK >= 23"); if (this.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { Log.d("MyApp", "Request permission"); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, MY_REQUEST_CODE); if (! shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { showMessageOKCancel("You need to allow camera usage", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCompat.requestPermissions(FotoPerfil.this, new String[] {Manifest.permission.CAMERA}, MY_REQUEST_CODE); } }); } } else { Log.d("MyApp", "Permission granted: taking pic"); takePicture(); } } else { Log.d("MyApp", "Android < 6.0"); } } 

entonces

 private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) { new AlertDialog.Builder(this) .setMessage(message) .setPositiveButton("OK", okListener) .setNegativeButton("Cancel", null) .create() .show(); } 

y entonces

 @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_REQUEST_CODE: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { criarFoto(); } else { Toast.makeText(this, "You did not allow camera usage :(", Toast.LENGTH_SHORT).show(); noFotoTaken(); } return; } } } 

El comportamiento previsto es que, en caso de que el usuario haya comprobado por error "Nunca volver a preguntar", su aplicación se atasque (no se muestre el cuadro de diálogo de solicitud) y el usuario se sienta frustrado. De esta forma, un mensaje le dice que necesita este permiso.

Quite:

 uses-permission android:name="android.permission.CAMERA" 

y solo confió en:

 uses-feature android:name="android.hardware.camera" android:required="true" 

en el archivo de manifiesto.

Este método mío no solo verifica la Cámara, sino también todos los permisos requeridos por mi aplicación durante el inicio … Lo tengo en mi archivo Helper.java. También tenga en cuenta que para el diálogo estoy usando esta Biblioteca: https: // github. com / afollestad / material-dialogs

  ///check camera permission public static boolean hasPermissions(final Activity activity){ //add your permissions here String[] AppPermissions = { Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; //ungranted permissions ArrayList ungrantedPerms = new ArrayList(); //loop //lets set a boolean of hasUngrantedPerm to false Boolean needsPermRequest = false; //permissionGranted int permGranted = PackageManager.PERMISSION_GRANTED; //permission required content String permRequestStr = activity.getString(R.string.the_following_perm_required); //loop for(String permission : AppPermissions){ //check if perm is granted int checkPerm = ContextCompat.checkSelfPermission(activity,permission); //if the permission is not granted if(ContextCompat.checkSelfPermission(activity,permission) != permGranted){ needsPermRequest = true; //add the permission to the ungranted permission list ungrantedPerms.add(permission); //permssion name String[] splitPerm = permission.split(Pattern.quote(".")); String permName = splitPerm[splitPerm.length-1].concat("\n"); permRequestStr = permRequestStr.concat(permName); }//end if }//end loop //if all permission is granted end exec //then continue code exec if(!needsPermRequest) { return true; }//end if //convert array list to array string final String[] ungrantedPermsArray = ungrantedPerms.toArray(new String[ungrantedPerms.size()]); //show alert Dialog requesting permission new MaterialDialog.Builder(activity) .title(R.string.permission_required) .content(permRequestStr) .positiveText(R.string.enable) .negativeText(R.string.cancel) .onPositive(new MaterialDialog.SingleButtonCallback(){ @Override public void onClick(@NonNull MaterialDialog dialog,@NonNull DialogAction which){ //request the permission now ActivityCompat.requestPermissions(activity,ungrantedPermsArray,0); } }) .show(); //return false so that code exec in that script will not be allowed //to continue return false; }//end checkPermissions 

entonces agregará o eliminará sus listas de permisos aquí:

 //add your permissions here String[] AppPermissions = { Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; 

En mi archivo de actividad, verifico el permiso de esta manera, la clase Helper es donde guardé el método hasPermissions

  if(Helper.hasPermissions(this) == false){ return; }//end if 

Significa que no necesitamos continuar la ejecución si no se concede permiso. De nuevo, tendremos que escuchar la solicitud de permiso una vez que haya finalizado; para hacerlo, agregue el código que se muestra a continuación a su archivo de actividad (opcional)

 //Listen to Permission request completion //put in your activity file @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { int permGranted = PackageManager.PERMISSION_GRANTED; Boolean permissionRequired = false; for(int perm : grantResults){ if(perm != permGranted){ permissionRequired = true; } } //if permission is still required if(permissionRequired){ //recheck and enforce permission again Helper.hasPermissions(this); }//end if }//end method 

es un poco tarde pero quiero agregar una cosa más. cada vez que llame a métodos que contengan la funcionalidad de la cámara, utilícela en el bloque try catch. si no la aplicación se bloqueará en algunos dispositivos como Moto G4 plus o uno más.

 private static final int CAMERA_REQUEST_CODE = 10; //TODO add camera opening functionality here. try { captureImage(); Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); startActivityForResult(intent,CAMERA_REQUEST_CODE); } catch (Exception e){ e.printStackTrace(); } private void captureImage(){ if( ContextCompat.checkSelfPermission(getContext(), android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{android.Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE); } else { // Open your camera here. } } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == CAMERA_REQUEST_CODE) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Now user should be able to use camera } else { // Your app will not have this permission. Turn off all functions // that require this permission or it will force close like your // original question } } } 

PD: asegúrese de no copiar pegar el método reemplazado.