Android M相机意图+权限错误?

我试图让我的应用程序准备好新的Android M权限更改,并发现一些奇怪的行为。 我的应用程序使用相机意图机制,允许用户从相机获取图片。 但在另一个活动需要使用相机的权限相机本身(因为一个库依赖card.io需要这个)。

但是,当我尝试启动相机意向时,只需要相机意图的M中的活动就会出现以下故障(如果从Manifest中删除相机权限,则不会发生这种情况)

> 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 

这是Android M的一个已知问题吗? 更重要的是我该如何解决这个问题?

在清单中我有以下几点,

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

这是我用来让用户用相机和/或select一个图像点击图片的代码

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

我在我的活动中点击一个button,调用openImageIntent() 。 当我没有在我的应用程序中的CAMERA权限,它工作正常,但与此增加我得到上面发布的exception。

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

我有同样的问题,并从谷歌find这个文档: https : //developer.android.com/reference/android/provider/MediaStore.html#ACTION_IMAGE_CAPTURE

“注意:如果你的应用程序的目标是M或更高,并声明使用未授予的CAMERA权限,那么尝试使用此操作将导致SecurityException。

这真的很奇怪。 根本没有意义。 该应用程序声明相机权限使用意图行动IMAGE_CAPTURE只是运行SecurityException。 但是,如果你的应用程序没有使用意图和动作声明相机权限IMAGE_CAPTURE可以启动相机应用程序没有问题。

解决方法是检查应用程序是否具有清单中包含的相机权限,如果是,则在启动intent之前请求相机权限。

这里是检查权限是否包含在清单中的方法,无论权限是否被授予。

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

如果您使用的是Android M权限模型,则首先需要检查应用程序在运行时是否具有此权限,并且在运行时必须提示用户input此权限。 您在清单中定义的权限不会在安装时自动授予。

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

MY_REQUEST_CODE是可以定义的静态常量,将再次用于requestPermission对话框callback。

您将需要callback对话框结果:

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

编辑

从堆栈轨迹中读取,看起来Google Camera没有启用CAMERA权限。 毕竟这实际上看起来像是一个向后兼容的东西。

让我们假设Google相机(或任何其他处理您的ACTION意图的应用程序)需要一定的权限。

当您的应用程序没有CAMERA权限时,只需让Google相机使用旧的权限模型就可以做到这一点。

但是,通过清单中声明的​​CAMERA权限,它也会在Google相机(没有Android M权限模型)中强制执行CAMERA权限以使用Android M权限模式(我认为)。

所以这意味着使用上面的方法,您将需要在运行时提供您的应用程序权限,这意味着它的子任务(在这种情况下,谷歌相机)现在也将具有该权限。

至于你的问题“这是一个已知的问题吗? 谷歌开发人员回应报告这个问题的人作为一个错误。

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

这里是谷歌家伙的话:“这是为了避免用户的挫折,他们撤销了应用程序的相机许可,应用程序仍然可以通过意图拍照。 用户不知道权限撤销后拍摄的照片是通过不同的机制发生的,会质疑权限模型的正确性。 这适用于MediaStore.ACTION_IMAGE_CAPTURE,MediaStore.ACTION_VIDEO_CAPTURE和Intent.ACTION_CALL文档,针对M的应用的行为更改。

由于Google不介意抽象用户使用相机的机制,因此您可能会策略性地触发第一个相机权限请求,并引用使用相机作为请求推理的“活动”的function。 如果您允许您的应用在用户尝试拍摄照片时首先发出此权限请求,则用户可能会认为您的应用行为exception,因为拍摄照片通常不需要授予许可。

如果您使用的是Google M,请转到设置 – >应用程序 – > 您的应用程序 – >,并给予适当的权限。

我被困在这个问题上,我已经使用JTY的答案。 问题是,在某些时候请求权限对话框被选中“永不再问”。 我正在开发SDK 24。

我完整的代码来处理权限(在我的情况下,相机)是:

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

然后

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

接着

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

预期的行为是,如果用户误选“永远不要再问”你的应用程序卡住(请求对话框没有显示),用户可能会感到沮丧。 这样一条消息告诉他他需要这个许可。

我删除了:

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

只依靠:

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

在清单文件中。

我的这种方法不检查只有相机,但在启动过程中,我的应用程序所需的所有权限…我在我的Helper.java文件中,也请注意对于我使用此库的对话框: https:// github。 COM / afollestad /材料的对话框

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

所以你会添加或删除您的权限列表在这里:

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

在我的活动文件中,我检查这个权限,Helper类是我保存hasPermissions方法的地方

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

意思是我们不需要继续执行,如果没有授予权限..我们将需要听取权限请求完成后,要做到这一点,将代码添加到您的活动文件(可选)

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

有点晚了 但我想添加一件事。 每当调用包含相机function的方法时,都可以在try catch块中使用它。 如果不是应用程序会在一些设备,如Moto G4 plus或一个加上崩溃。

 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:确保不要复制粘贴覆盖的方法。