Android M Intención de cámara + error de permiso?

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

Sin embargo, con M en la actividad que sólo necesita una intención de cámara cuando intento iniciar la cámara, veo el siguiente bloqueo (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 

¿Se trata de un problema conocido con Android M? Y lo más importante, ¿cómo puedo evitar esto?

En el manifiesto tengo lo siguiente,

 <uses-permission android:name="android.permission.CAMERA" /> 

Y este es el código que utilizo para dejar que el usuario haga clic en una foto con la cámara y / o seleccione una imagen

 public static Intent openImageIntent(Context context, Uri cameraOutputFile) { // Camera. final List<Intent> cameraIntents = new ArrayList<Intent>(); final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); final PackageManager packageManager = context.getPackageManager(); final List<ResolveInfo> 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 de la CÁMARA en mi aplicación funciona bien, pero con eso añadido 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 encontrar este documento de google: https://developer.android.com/reference/android/provider/MediaStore.html#ACTION_IMAGE_CAPTURE

"Nota: si la aplicación tiene como objetivo M y superior y declara que utiliza el permiso CAMERA que no se concede, entonces intentar utilizar esta acción resultará en SecurityException."

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

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

Aquí está la manera de comprobar si el permiso se incluye en el manifiesto, no importa el permiso se concede 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 comprobar 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 concederá 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á de nuevo para la devolución de llamada del diálogo requestPermission.

Necesitará una devolución de llamada 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 el rastreo de la pila, parece que Google Camera no tiene habilitado el permiso CAMERA. Esto podría parecer una cosa de compatibilidad hacia atrás después de todo.

Supongamos que Google Camera (o cualquier otra aplicación que maneje su intención de ACCIÓN) requiera un cierto permiso.

Cuando tu aplicación no tiene el permiso CAMERA, solo permite que Google Camera haga algo con el modelo de permisos antiguo.

Sin embargo, con el permiso CAMERA declarado en su manifiesto, también está aplicando el permiso CAMERA en 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, necesitarás proporcionar el permiso de tu aplicación durante el tiempo de ejecución, lo que significa que su tarea secundaria (en este caso Cámara de Google) también tendrá ese permiso.

En cuanto a su pregunta "¿Se trata de un problema conocido en M?" Un dev de Google respondió a alguien que reportó 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: "Se trata de un comportamiento destinado a evitar la frustración del usuario, donde revocó el permiso de la cámara de una aplicación y la aplicación sigue siendo 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 se produce a través de diferentes mecanismos y cuestionaría la corrección del modelo de permiso. Esto se aplica a MediaStore.ACTION_IMAGE_CAPTURE, MediaStore.ACTION_VIDEO_CAPTURE e Intent.ACTION_CALL a los documentos para los que se documenta el cambio de comportamiento para las aplicaciones orientadas a M. "

Puesto que 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 utiliza la cámara como su razonamiento para la solicitud. Si permite que su aplicación primero haga esta solicitud de permiso cuando el usuario simplemente intenta tomar una fotografía, el usuario puede pensar que su aplicación se está comportando extrañamente, ya que la toma de una foto normalmente no requiere otorgar permiso.

Si estás usando Google M, accede a Ajustes -> Aplicaciones -> tu aplicación -> y da los permisos apropiados.

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

Mi código completo para manejar los permisos (la cámara en mi caso) era 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 deseado es que en caso de que el usuario por error compruebe "Nunca vuelva a preguntar" su aplicación se queda atascada (el cuadro de diálogo de solicitud no se muestra) y el usuario puede sentirse frustrado. De esta manera un mensaje le dice que necesita este permiso.

Quite:

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

Y sólo se basó en:

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

En el archivo de manifiesto.

Este método de la mina no comprueba solamente la cámara pero todos los permisos requeridos por mi app durante el arranque … Tengo esto en mi archivo de Helper.java, también observo que para el diálogo estoy utilizando 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<String> ungrantedPerms = new ArrayList<String>(); //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 

Por lo que va a agregar o quitar su permiso Listas 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 comprobar el permiso de esta manera, la clase de ayuda 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 .. Una vez más tendremos que escuchar la solicitud de permiso después de que haya terminado, para hacer eso, agregue el código 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 añadir una cosa más. Cada vez que llame a métodos que contenga la funcionalidad de la cámara, úselo en el bloque try catch. Si no la aplicación se bloqueará en algunos dispositivos como Moto G4 plus o una ventaja.

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

PS: asegúrese de no copiar pegar el método anulado.

  • ¿Cómo manejar las solicitudes de permisos fuera de Actividad y Fragmento?
  • Listando los permisos de la aplicación de Android vía adb
  • Cómo ocultar la barra inferior del sistema en la tableta android
  • Permisos para la búsqueda por voz
  • Error básico en AndroidManifest.xml para recibir permiso de SMS
  • Android 6.0.1 no puede habilitar wifi hotspot mediante programación
  • Requerir permiso sólo para versiones android anteriores: maxSdkVersion no funciona?
  • No se puede contestar llamadas entrantes en android marshmallow 6.0
  • La forma menos invasiva de identificar de forma exclusiva al usuario de Android
  • El permiso de vibración sólo se requiere en algunos dispositivos
  • ¿Qué es <permission-tree> en android manifest? ¿Cómo hacer uso de esto?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.