54

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

然而,在只有当我尝试启动相机意图我看到下面的崩溃(如果我删除从清单摄像头权限,这不会发生),需要一个摄像头意图活性男,

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

,这是我用来让用户点击一个PIC用相机和/或选择一个图像

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

代码我在我的活动中点击一个按钮,致电openImageIntent()。当我在我的应用程序中没有CAMERA权限时,它可以正常工作,但是添加了该功能后,我得到了上面发布的异常。

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

+0

你能提供任何代码吗?您是在运行时请求权限还是期待系统为您执行此操作?原因是,当你从清单中删除权限时,这可能不会发生,实际上它甚至不会尝试启动相机。请参阅以下博客以获取有关M中Android运行时权限的教程:http://www.captechconsulting.com/blogs/runtime-permissions-best-practices-and-how-to-gracefully-handle-permission-removal – TDev

+0

Can你提供了一些更多的信息,例如你调用摄像头的代码,因为这不足以回答你问题 – dex

+0

我已经添加了相关的代码,提供了问题。 –

回答

68

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

“注意:如果你的应用程序的目标米及以上,并声明为使用未授予的摄像头权限,然后atempting使用这个动作将导致SecurityException。“

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

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

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

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

我想知道这是否在我问这个问题之前的文档?(或稍后添加):) –

+1

谢谢!多么奇怪的行为。我在我的manifest.xml –

+1

寻找一个问题所以现在是很好,如果我跳过从我的Manifest.xml文件摄像头权限?它会给诸如棒棒糖,奇巧和果冻豆等其他低版本带来什么问题吗? –

20

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

if (checkSelfPermission(Manifest.permission.CAMERA) 
     != PackageManager.PERMISSION_GRANTED) { 

    requestPermissions(new String[]{Manifest.permission.CAMERA}, 
      MY_REQUEST_CODE); 
} 

MY_REQUEST_CODE是静态常量,你可以定义,这将再次用于requestPermission对话框回调。

您需要在对话框结果的回调:

@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相机(或任何处理您的ACTION意图的其他应用程序)需要一定的权限。

当您的应用程序没有CAMERA权限时,它只是让Google相机使用旧的权限模型进行操作。

然而,随着你的清单中声明摄像头权限,还强制谷歌相机内的摄像头权限(不具有版本的Android M权限模型)使用版本的Android M权限模型(我认为)

这意味着使用上述方法时,您需要在运行时提供您的应用权限,这意味着其子任务(在此情况下为Google Camera)现在也具有该权限。

+2

但在这里https://developer.android.com/preview/features/runtime-permissions.html没有权限与意图部分说,如果我们正在使用意图获取图像,我们不需要声明CAMERA权限? –

+2

@ source.rar确切地说,即使对于Android 4和5,如果没有设置权限,它仍然不起作用。WTF Google? – 0101100101

+0

运作良好。谢谢! –

12

至于你的问题'这是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不介意从用户中抽象使用相机的机制,您可能会策略性地触发相机权限的第一个请求,并引用使用相机作为请求推理的活动的功能。如果您允许您的应用在用户仅仅尝试进行拍摄时首先发出此权限请求图片,用户可能会认为你的应用程序行为异常,因为拍摄照片通常不需要授予许可。

10

如果您使用Google M,请转至设置 - >应用程序s - >你的应用程序 - >并给予适当的权限。

+0

对,即使您正在开发自己的应用程序进行测试,您仍然需要为每个应用程序执行此操作。 – hatted

1

我删除:

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

,只依赖于:

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

清单文件。

+0

然后你做这个错误:“在声明的特征,记住,你还必须请求权限酌情” https://developer.android.com/guide/topics/manifest/uses-feature-element.html –

+0

所以,如果我做错了 - Android应该不会让我访问相机?!然而 - 它完美的工作。此外 - 根据文章https://developer.android.com/training/camera/photobasics.html你不需要权限...只有功能。 (因此需要=“真”标志),但感谢向下表决... –

+0

'使用-feature'不会自动授予您'使用,permission'。这只是意味着你想要相机。你会使用它还是会访问它,由用户授予**完全**其他故事。 –

3

我被困在这个问题上,我已经使用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; 
     } 
    } 
} 

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

+1

我知道S *,你需要大量的代码只是运行一个简单的摄像头意图中正。您使用最新的API支付的价格。 –

0

我的这种方法犯规检查仅相机,但需要在启动过程中我的应用程序的所有权限......我有这在我的Helper.java文件,还要注意,在该对话框我使用这个库: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 

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

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

在我的活动我的文件检查这样的权限,辅助类就是我不停的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 
-1

有点晚了。但我想添加一件事。每当你调用包含相机功能的方法时,都可以在try catch块中使用它。如果不是应用程序会在某些设备上出现故障,例如Moto G4 plus或另外一个设备。 P:确保不复制粘贴覆盖的方法。请确保不复制粘贴覆盖的方法。