【问题标题】:Android M Camera Intent + permission bug?Android M Camera Intent + 权限错误?
【发布时间】:2015-12-23 16:34:06
【问题描述】:

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

但是,当我尝试启动相机意图时,活动中的 M 只需要相机意图,我看到以下崩溃(如果我从清单中删除相机权限,则不会发生这种情况),

> 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/…
  • 您能否提供更多信息,例如调用相机的代码,因为这不足以回答您的问题
  • 我已经添加了给出问题的相关代码。
  • @TDev:如果我理解正确,我们不需要 CAMERA 权限来获取具有 INTENT 的图像
  • @source.rar 权限现在按类别分组。您需要相机类别权限才能访问图片。

标签: android android-camera android-permissions android-camera-intent android-6.0-marshmallow


【解决方案1】:

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

"注意:如果您的应用程序以 M 及以上为目标并声明使用未授予的 CAMERA 权限,则尝试使用此操作将导致 SecurityException。"

这真的很奇怪。 完全没有意义。 该应用程序使用带有操作 IMAGE_CAPTURE 的 Intent 声明相机权限,只是遇到了 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;
}

【讨论】:

  • 在我问这个问题之前,我想知道这是否在文档中? (或者是后来添加的):)
  • 谢谢!多么奇怪的行为。我在 manifest.xml 中搜索问题
  • 那么现在可以跳过 Manifest.xml 文件中的相机权限了吗?它会对 Lollipop、Kitkat 和 Jellybeans 等其他低版本造成任何问题吗?
  • 什么是没有权限,那你会怎么做?请求权限?
  • @Singed,因为稍后您可以忘记此修复并添加/删除相机权限到清单。
【解决方案2】:

如果您使用的是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 权限。毕竟,这实际上可能看起来像是向后兼容的事情。

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

当您的应用没有 CAMERA 权限时,它只是让 Google 相机使用旧的权限模型来做这件事。

但是,在清单中声明了 CAMERA 权限后,它还强制 Google 相机(没有 Android M 权限模型)中的 CAMERA 权限使用 Android M 权限模型(我认为。)

也就是说,使用上述方法,您需要在运行时提供您的应用权限,这意味着它的子任务(在本例中为 Google 相机)现在也将拥有该权限。

【讨论】:

【解决方案3】:

就您的问题“这是 M 中的一个已知问题吗?”谷歌开发者回应了有人将此问题报告为错误。

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

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

由于 Google 不介意从您的用户那里抽象出使用摄像头的机制,因此您不妨策略性地触发第一个摄像头权限请求,并参考使用摄像头的 Activity 的功能作为您请求的理由。如果您允许您的应用在用户只是尝试拍照时首先发出此权限请求,那么用户可能会认为您的应用行为异常,因为拍照通常不需要授予权限。

【讨论】:

    【解决方案4】:

    如果您使用的是 Google M,请转至 设置 -> 应用 -> 您的应用 -> 并授予适当的权限。

    【讨论】:

    • 对,即使您正在开发自己的应用程序进行测试,您仍然需要为每个应用程序都这样做。
    【解决方案5】:

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

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

    【讨论】:

    • 我知道你需要很多代码来运行一个简单的相机 Intent 是很糟糕的。您为使用最新 API 所付出的代价。
    【解决方案6】:

    我删除了:

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

    并且只依赖于:

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

    在清单文件中。

    【讨论】:

    • 那你做错了:“在声明一个特性的时候,记住你还必须酌情请求权限”developer.android.com/guide/topics/manifest/…
    • 那么,如果我做错了 - Android 不应该为我提供摄像头的访问权限?!然而 - 它完美地工作。另外-根据文章developer.android.com/training/camera/photobasics.html,您不需要许可……只有功能。 (因此 required="true" 标志)但是感谢您的反对...
    • uses-feature 不会自动授予您uses-permission。这只是意味着你想要相机。您会使用它还是会由用户授予对它的访问权限完全是另一回事。
    • 介意指向他们说你不需要uses-permission的地方,因为你有uses-feature
    • 好吧 - 如果我添加它,代码就会中断。当我删除它时它起作用了 - 当我关注这篇文章时。就这么简单...介意指向他们声明需要它的地方吗?!
    【解决方案7】:

    我的这种方法不只检查相机,而是检查我的应用程序在启动期间所需的所有权限......我在我的 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
                };
    

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

    【讨论】:

      【解决方案8】:

      有点晚了。但我想再补充一件事。每当您调用包含相机功能的方法时,请在 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
              }
          }
      }
      

      P.S:确保不要复制粘贴覆盖的方法。

      【讨论】:

        【解决方案9】:

        您必须启用应用程序权限才能使用相机。我更喜欢添加这个方法添加激活相机的命令:

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

        【讨论】:

          猜你喜欢
          • 2015-11-17
          • 1970-01-01
          • 2015-10-04
          • 2017-08-09
          • 1970-01-01
          • 2016-03-25
          • 2014-08-16
          • 1970-01-01
          • 2015-12-16
          相关资源
          最近更新 更多