Android M Camera Intent +权限错误?


71

我正在尝试为新的Android M权限更改准备好我的应用程序,并发现一些奇怪的行为。我的应用程序使用“相机意图”机制允许用户从相机中获取图片。但是在另一项活动中,需要在“相机”权限下使用相机本身(因为需要此功能的库依赖卡.io)。

但是,如果活动中的M仅在尝试启动Camera Intent时需要Camera Intent,那么我会看到以下崩溃信息(如果我从清单中删除Camera许可,则不会发生这种情况),

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

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

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


你能提供任何代码吗?您是在运行时请求权限还是只是希望系统为您执行此操作?原因是,当您从清单中删除权限时,这可能不会发生,因为它实际上甚至不会尝试启动相机。请访问以下博客,获取有关M中Android运行时权限的教程:captechconsulting.com/blogs/…– user1518292 2015

您能否提供更多信息,例如在哪里调用相机的代码,因为这不足以回答您的问题
dex

我添加了解决该问题的相关代码。
source.rar 2015年

@TDev:如果我理解正确的话,我们不需要CAMERA权限获取图像,意图
源码.rar

@ source.rar现在,权限按类别分组在一起。您需要具有摄像机类别的权限才能访问图片。

Answers:


87

我遇到了同样的问题,并从Google找到了该文档: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;
}

1
我想问这个问题之前是否在文档中?(或稍后添加):)
source.rar 2015年

1
谢谢!多么奇怪的行为。我在manifest.xml中搜索问题
Adrian Krebs

1
因此,如果我从Manifest.xml文件中跳过Camera权限,现在可以了吗?它将对其他较低版本(例如Lollipop,Kitkat和Jellybeans)造成问题吗?
Pravinsingh Waghela '16

没有权限,那你会怎么做?要求权限?
穆尼卜

1
@Singed,因为稍后您可以忽略此修复程序并添加/删除相机权限以进行显示。
阿亚兹·阿里夫

19

如果您使用的是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 Camera没有启用CAMERA权限。毕竟,这实际上看起来像是向后兼容。

假设Google Camera(或处理您的ACTION意向的任何其他应用程序)需要一定的权限。

如果您的应用没有CAMERA权限,则只允许Google Camera使用旧的权限模型来完成。

但是,在清单中声明了CAMERA权限后,它也会在Google Camera(不具有Android M权限模型)中强制执行CAMERA权限以使用Android M权限模型(我认为)。

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


2
但是developer.android.com/preview/features/runtime-permissions.html上的“许可与意图”部分是否说如果我们使用意图获取图像就不需要声明CAMERA许可?
source.rar 2015年

2
@ source.rar的确如此,即使不为Android 4和5设置权限,它仍然无法工作。WTF Google?
0101100101 '16

17

至于您的问题“这是M中的已知问题吗?” 一位Google开发人员回复了有人将此问题报告为错误。

看到这里:https//issuetracker.google.com/issues/37063818#comment8

这是Google家伙的话:“这是为了避免用户从应用程序撤消相机许可而使应用程序仍然能够通过意图拍照而避免沮丧的行为。用户不知道在权限撤销后拍摄的照片是通过不同的机制发生的,并且会质疑权限模型的正确性。这适用于MediaStore.ACTION_IMAGE_CAPTURE,MediaStore.ACTION_VIDEO_CAPTURE和Intent.ACTION_CALL这些文档,这些文档记录了针对M的应用程序的行为更改。”

由于Google并不介意从用户那里抽象使用相机的机制,因此您最好从战略上触发对相机许可的第一个请求,并引用使用相机作为请求理由的“活动”功能。如果在用户只是尝试拍照时允许您的应用程序首先发出此许可请求,则用户可能会认为您的应用程序行为异常,因为拍照通常不需要授予许可。


9

如果您使用的是Google M,请转到“设置”->“应用程序”->“ 您的应用程序” ->并提供适当的权限。


没错,即使您正在开发自己的应用程序进行测试,您仍然需要为每个应用程序进行测试。

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

预期的行为是,如果用户错误地选中了“不再询问”,则您的应用程序被卡住(未显示请求对话框),并且用户可能会感到沮丧。这样一则消息告诉他他需要此许可。


1
我知道有些麻烦,只需运行一个简单的Camera Intent就需要很多代码。您使用最新API所要支付的价格。
Teo Inke

0

我删除了:

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

并且仅依靠:

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

在清单文件中。


1
然后,你做这个错误:“在声明的特征,记住,你还必须请求权限酌情” developer.android.com/guide/topics/manifest/...
马辛ORLOWSKI

因此,如果我做错了-Android是否不应该向我提供使用摄像头的权限?但是-它运作良好。另外-根据developer.android.com/training/camera/photobasics.html的文章您不需要权限...仅功能。(因此为required =“ true”标志)但是感谢您的不赞成票……
Jeann van Rooyen

uses-feature不会自动授予您uses-permission。这只是意味着您需要相机。您将使用它还是将其授予用户权限完全是另一回事。
Marcin Orlowski

介意指向他们说您不需要的地方,uses-permission因为您有uses-feature吗?
Marcin Orlowski

1
好吧-如果我添加了代码,代码就会中断。当我删除它时,以及当我阅读本文时,它都起作用。就这么简单...介意指向他们指出需要的地方吗?
Jeann van Rooyen

0

我的这种方法不只检查Camera,而是在启动过程中检查我的应用程序所需的所有权限...我在Helper.java文件中具有此权限,还请注意,对于对话框,我正在使用此Library: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
            };

在我的活动文件中,我像这样检查权限,在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

0

有点晚了 但我想再增加一件事。每当您调用包含相机功能的方法时,请在try catch块中使用它。否则,应用程序将在Moto G4 plus或one 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:请确保不要复制粘贴重写的方法。


0

您必须启用“使用相机的应用程序”权限。我更喜欢添加此方法添加命令来激活相机:

    public static async Task<bool> HasPermission()
    {
        var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
        if (status == PermissionStatus.Granted) return true;

        if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Camera))
        {
            ShowDialogOk("Error", "Please allow access to the camera.");//that is my custom method for allert
        }

        var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Camera);
        status = results[Permission.Camera];

        return status == PermissionStatus.Granted;
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.