简体   繁体   中英

Android “Permission Denial: can't use the camera”

I'm following a tutorial on utilizing the camera in an Android app. I am getting the error "Permission Denial: can't use the camera" when running debug, both on emulator and physical device. I have tried a variety of permissions in my manifest file. It seems most people who have had this error have had a typo, a missing permission, or the permissions not in the right place in their manifest.

Here's my manifest file:

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.karudo.dbzrealpowerup" >

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.FLASHLIGHT" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera2" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".DBZHome"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".DBZStartPowerUp"
            android:label="@string/title_activity_dbzstart_power_up" >
        </activity>
    </application>

</manifest>

Here's my activity:

package com.example.karudo.dbzrealpowerup;

import android.app.Activity;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.Bundle;
import android.util.Size;
import android.view.Menu;
import android.view.MenuItem;
import android.view.TextureView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class DBZStartPowerUp extends Activity {

    private Size mPreviewSize;
    private String mCameraId;
    private TextureView mTextureView;
    private TextureView.SurfaceTextureListener mSurfaceTextureListener =
            new TextureView.SurfaceTextureListener() {

                @Override
                public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                    setupCamera(width, height);
                    openCamera();
                }

                @Override
                public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

                }

                @Override
                public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                    return false;
                }

                @Override
                public void onSurfaceTextureUpdated(SurfaceTexture surface) {

                }
            };

    private CameraDevice mCameraDevice;
    private CameraDevice.StateCallback mCameraDeviceStateCallback
            = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(CameraDevice camera) {
            mCameraDevice = camera;
            Toast.makeText(getApplicationContext(), "Camera Opened!", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            camera.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            camera.close();
            mCameraDevice = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dbzstartpowerup);
        mTextureView = (TextureView) findViewById(R.id.dbzCameraPreview);
    }

    @Override
    public void onResume() {
        super.onResume();
        if(mTextureView.isAvailable()) {

        } else {
            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_dbzstartpowerup, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private void setupCamera(int width, int height) {
        CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            for(String cameraId : cameraManager.getCameraIdList()) {
                CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
                if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) ==
                        CameraCharacteristics.LENS_FACING_FRONT) {
                    continue;
                }
                StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                mPreviewSize = getPreferredPreviewSize(map.getOutputSizes(SurfaceTexture.class), width, height);
                mCameraId = cameraId;
                return;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private Size getPreferredPreviewSize(Size[] mapSizes, int width, int height) {
        List<Size> collectorSizes = new ArrayList<>();
        for(Size option : mapSizes) {
            if(width > height) {
                if(option.getWidth() > width &&
                        option.getHeight() > height) {
                    collectorSizes.add(option);
                }
            } else {
                if(option.getWidth() > height &&
                        option.getHeight() > width) {
                    collectorSizes.add(option);
                }
            }
        }
        if(collectorSizes.size() > 0) {
            return Collections.min(collectorSizes, new Comparator<Size>() {
                @Override
                public int compare(Size lhs, Size rhs) {
                    return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
                }
            });
        }
        return mapSizes[0];
    }

    private void openCamera() {
        CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
}

And the error in my logcat:

10-04 03:15:02.740     961-8780/? E/CameraService﹕ Permission Denial: can't use the camera pid=20601, uid=10059
10-04 03:15:02.741  20601-20601/com.example.karudo.dbzrealpowerup E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.example.karudo.dbzrealpowerup, PID: 20601
    java.lang.SecurityException: Lacking privileges to access camera service
            at android.hardware.camera2.utils.CameraBinderDecorator.throwOnError(CameraBinderDecorator.java:108)
            at android.hardware.camera2.legacy.CameraDeviceUserShim.connectBinderShim(CameraDeviceUserShim.java:336)
            at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:324)
            at android.hardware.camera2.CameraManager.openCamera(CameraManager.java:454)
            at com.example.karudo.dbzrealpowerup.DBZStartPowerUp.openCamera(DBZStartPowerUp.java:163)
            at com.example.karudo.dbzrealpowerup.DBZStartPowerUp.access$100(DBZStartPowerUp.java:23)
            at com.example.karudo.dbzrealpowerup.DBZStartPowerUp$1.onSurfaceTextureAvailable(DBZStartPowerUp.java:34)
            at android.view.TextureView.getHardwareLayer(TextureView.java:368)
            at android.view.View.updateDisplayListIfDirty(View.java:15151)
            at android.view.View.draw(View.java:15948)
            at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
            at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
            at android.view.View.updateDisplayListIfDirty(View.java:15169)
            at android.view.View.draw(View.java:15948)
            at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
            at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
            at android.view.View.updateDisplayListIfDirty(View.java:15169)
            at android.view.View.draw(View.java:15948)
            at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
            at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
            at android.view.View.updateDisplayListIfDirty(View.java:15169)
            at android.view.View.draw(View.java:15948)
            at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
            at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
            at android.view.View.draw(View.java:16181)
            at com.android.internal.policy.PhoneWindow$DecorView.draw(PhoneWindow.java:2690)
            at android.view.View.updateDisplayListIfDirty(View.java:15174)
            at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:281)
            at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:287)
            at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:322)
            at android.view.ViewRootImpl.draw(ViewRootImpl.java:2615)
            at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2434)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2067)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
            at android.view.Choreographer.doCallbacks(Choreographer.java:670)
            at android.view.Choreographer.doFrame(Choreographer.java:606)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:148)
            at android.app.ActivityThread.main(ActivityThread.java:5417)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

I'm new to app development and admittedly I'm no good at debugging, but my manifest permissions appear to be correct from what I've seen of other people's files (and the tutorial which is only 4 months old).

Can anyone please tell me what I've done wrong?

Cheers, Lee.

UPDATE: I found via debugging that it crashes as soon as it reaches this method...

private void openCamera() {
    CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }

}

... specifically as soon as it runs the try statement.

UPDATE 2: If I comment out the try/catch statements, the app doesn't crash, but the expected result (which is to print "Camera Opened!") doesn't happen. Any ideas?

UPDATE 3: My apologies, I just realized how dumb my above edit was. It's obvious why it no longer crashes, but at least I now know I have to debug my cameraManager.openCamera parameters. The code is there if anyone can have a look :)

If you're using Android 6 Marshmallow this issue could be caused by the new permission management. In my case I solved the problem by overriding the following activity method:

@Override
public void onRequestPermissionsResult(int requestCode,  String permissions[], int[] grantResults) {
    switch (requestCode) {
        case CAMERA_PERMISSION:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                if(mClss != null) {
                    Intent intent = new Intent(this, ClassUsingCamera);
                    startActivity(intent);
                }
            } else {
                Toast.makeText(this, "Please grant camera permission to use the QR Scanner", Toast.LENGTH_SHORT).show();
            }
            return;
    }
}

I then launched the activity that requires the camera (ClassUsingCamera) with the following code:

if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
     ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION);
} else {
       Intent intent = new Intent(this, ClassUsingCamera);
       startActivity(intent);
        }

On the first app launch you'll get the popup asking you to grant the access to the camera.

Another option is to use the PermissionsDispatcher as explained here https://github.com/hotchemi/PermissionsDispatcher

i got the same error only when i tried the app on devices with (Android 6.0.0) or emulator with (API 23). But with others it works fine.

that's because of android above M makes some changes in granted permissions on run-time application.

to do it, follow this few steps.

first : add this static variable.

private static final int REQUEST_CAMERA_RESULT = 1;

then modify your openCamera method

private void openCamera() {
    CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        Log.v("CAMERA", mCameraId + " " + mCameraDeviceStateCallback);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA)
                    == PackageManager.PERMISSION_GRANTED){
                cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback,mBackgroundHandler);
            }
            else {
                if (shouldShowRequestPermissionRationale(android.Manifest.permission.CAMERA)){
                    Toast.makeText(this,"No Permission to use the Camera services", Toast.LENGTH_SHORT).show();
                }
                requestPermissions(new String[] {android.Manifest.permission.CAMERA},REQUEST_CAMERA_RESULT);
            }
        }
        else {
            cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, mBackgroundHandler);
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

feel free to change mBackgroundHandler to null, i just made it to handle camera work in background thread.

And,then Override this method

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode){
        case  REQUEST_CAMERA_RESULT:
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED){
                Toast.makeText(this, "Cannot run application because camera service permission have not been granted", Toast.LENGTH_SHORT).show();
            }
            break;
        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        break;
    }
}

Few points I can say :

  1. If connection to the camera service is fails (For example if the camera is used by anyother application or device manger has disable the camera or any of the application could not able to release the camera.)
  1. Make sure that your safely close/ release the camera inside your code.

  2. Have you tried checking if the camera is being used by something else or if your policy manager has some setting where the camera is turned off?

try to put this code your manifest.xml file.

 <uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="19" />

<uses-permission
    android:name="android.permission.INTERNET"
    android:maxSdkVersion="19" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_DEBUG_APP"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

First Option you have regarding permissions is to go to the android settings of that specific application and grant it all permissions it requires from there. Second option you have is to check whether that permission is required during runtime and request for permissions the app needs or an even better approach is to ask for a permission when a feature is run that requires that permission.

Well go through some code that ask for app permissions when an activity opens. But you can modify that code to ask for permission when a feature such as taking camera is required.

And another thing is that some permissions such as Internet are granted automatically by the system therefore you don't need to ask for it. However You will need to ask for some explicitly.

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

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

The above shows the permissions declared right above the opening application tag in the AndroidManifest.

    public boolean hasFineLocationPermission(){
        if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED){
            return true;
        }
        else{
            return false;
        }
    }

    public boolean hasExternalStoragePermission(){
        if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
            return true;
        }
        else{
            return false;
        }
    }

public boolean hasLocationForegroundService(){
    if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.FOREGROUND_SERVICE) == PackageManager.PERMISSION_GRANTED){
        return true;
    }
    else{
        return false;
    }
}



public boolean hasReadPhoneStatePermission(){
    if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED){
        return true;
    }
    else{
        return false;
    }
}

public boolean hasCameraPermission(){
    if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
        return true;
    }
    else{
        return false;
    }
}

public boolean hasAccessNetworkState(){
    if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_NETWORK_STATE) == PackageManager.PERMISSION_GRANTED){
        return true;
    }
    else{
        return false;
    }
}

We have some functions above. What all the functions do is basically check if a certain permission is granted. If it is return true therefore there is no need to ask for that permission. If not return false therefore well ask for that permission in a bit. For example:

if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){ return true; }

This checks If we have access to the camera and if we do it returns True and if not it will return false as shown in the function hasCameraPermission()

Now we can check that all permissions are granted using the function below:

public void getApplicationPermissions(){

    List<String> listPermissionsNeeded = new ArrayList<>();
    if (!hasFineLocationPermission()){

        listPermissionsNeeded.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);

    }

    if (!hasExternalStoragePermission()){

        listPermissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);

    }

    if (!hasLocationForegroundService()){
        listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);

    }
    if (!hasReadPhoneStatePermission()){
        listPermissionsNeeded.add(Manifest.permission.READ_PHONE_STATE);
    }

    if (!hasCameraPermission()){
        listPermissionsNeeded.add(Manifest.permission.CAMERA);
    }

    if (!hasAccessNetworkState()){
        listPermissionsNeeded.add(Manifest.permission.ACCESS_NETWORK_STATE);
    }

    Log.i("listPermissionsNeeded", String.valueOf(listPermissionsNeeded));

    if (!listPermissionsNeeded.isEmpty()){
        ActivityCompat.requestPermissions(this,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),0);
    }
    else{
        Toast.makeText(this,"All Permissions Required Are Granted",Toast.LENGTH_SHORT).show();
    }

}

The code above first declares a list List<String> listPermissionsNeeded = new ArrayList<>(); of type String

Then checks if:

if (!hasCameraPermission()){
            listPermissionsNeeded.add(Manifest.permission.CAMERA);
        } 

Meaning: If Permission to camera has not been granted add that permission to the list by adding Manifest.permission.CAMERA

If our permissionsList is Empty we'll just show the user a toast. However If it does have some permissions we Call the function ActivityCompat.requestPermissions() passing in context,our list and an identifier which in this case is 0:

if (!listPermissionsNeeded.isEmpty()){
    ActivityCompat.requestPermissions(this,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),0);
}
else{
    Toast.makeText(this,"All Permissions Required Are Granted",Toast.LENGTH_SHORT).show();
} 

When that requestPermissions is called another android method is called which is onRequestPermissionsResult() . We implement it as below:

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if(requestCode=0){
         for (int i=0;i<grantResults.length;i++){
            if (grantResults[i] == -1){
                getApplicationPermissions();

            }
        }

        }

    }

The the request Code will be 0 and the grantResults will be the list we passed in the function: ActivityCompat.requestPermissions(this,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),0);

When a permission is rejected by the user you will get back a value of -1 in the grantResult. All I do in that function is loop through my grantResults and search for any permission that has been rejected and if there is a permission that has been rejected, I ask for it again and again and again. That is terrible for user experience. But you get the point.

Finally We need to check whether the version of Android being used is Android Marshmallow Api Level 23. From what I got, below android 6.0 You can get all permissions as long as you declare it in the Manifest, But starting From 6.0 and upwards You need to ask for the permissions. This way you can avoid a crash in api levels below that. Am not 100% on this, I found conflicting info on this, but settled for that, somebody can correct me there.

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            getApplicationPermissions();

        }

Hope I've not confused you. If you have any questions u can ask or I can send the github link housing that so that u can see how all the code joins up, ie if you need it.

All Code was written in Java.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM