Why can't I access camera in Android Instant App?

1.8k views Asked by At

I am trying to develop a camera instant app, but it always posts a crash when I open the camera by manager.openCamera(cameraId, mStateCallback, null);:

09-10 21:00:55.333 9472-9472/com.pixelslab.stickerpe I/CameraManager: Using legacy camera HAL.
09-10 21:00:55.340 1402-1914/? I/CameraService: CameraService::connect call (PID -1 "com.pixelslab.stickerpe", camera ID 0) for HAL version default and Camera API version 1
09-10 21:00:55.340 1402-1914/? W/ServiceManager: Permission failure: android.permission.CAMERA from uid=10088 pid=9472
09-10 21:00:55.340 1402-1914/? E/CameraService: Permission Denial: can't use the camera pid=9472, uid=10088
09-10 21:00:55.345 9472-9472/com.pixelslab.stickerpe D/AndroidRuntime: Shutting down VM
09-10 21:00:55.348 9472-9472/com.pixelslab.stickerpe E/AndroidRuntime: FATAL EXCEPTION: 
     main Process: com.pixelslab.stickerpe, PID: 9472
     java.lang.SecurityException: Lacking privileges to access camera service
     at android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:643)
     at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:340)
     at android.hardware.camera2.CameraManager.openCameraForUid(CameraManager.java:466)
     at android.hardware.camera2.CameraManager.openCamera(CameraManager.java:430)
     at com.gomo.minivideo.camera2.Camera2VideoFragment.openCamera(Camera2VideoFragment.java:378)
     at com.gomo.minivideo.camera2.Camera2VideoFragment.access$000(Camera2VideoFragment.java:65)
     at com.gomo.minivideo.camera2.Camera2VideoFragment$1.onSurfaceTextureAvailable(Camera2VideoFragment.java:120)
     at android.view.TextureView.getHardwareLayer(TextureView.java:390)
     at android.view.TextureView.draw(TextureView.java:339)
     at android.view.View.updateDisplayListIfDirty(View.java:18069)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.draw(View.java:19122)
     at com.android.internal.policy.DecorView.draw(DecorView.java:785)
     at android.view.View.updateDisplayListIfDirty(View.java:18069)
     at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:643)
     at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:649)
     at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:757)
     at android.view.ViewRootImpl.draw(ViewRootImpl.java:2980)
     at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2794)
     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2347)
     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1386)
     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6733)
     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
     at android.view.Choreographer.doCallbacks(Choreographer.java:723)
     at android.view.Choreographer.doFrame(Choreographer.java:658)
     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
     at android.os.Handler.handleCallback(Handler.java:789)
     at android.os.Handler.dispatchMessage(Handler.java:98)
     at android.os.Looper.loop(Looper.java:164)
     at android.app.ActivityThread.main(ActivityThread.java:6541)
09-10 21:00:55.349 9472-9472/com.pixelslab.stickerpe E/AndroidRuntime:
     at java.lang.reflect.Method.invoke(Native Method)
     at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
       Caused by: android.os.ServiceSpecificException: Lacking privileges to access camera service (code 1)
     at android.hardware.camera2.legacy.LegacyExceptionUtils.throwOnServiceError(LegacyExceptionUtils.java:132)
     at android.hardware.camera2.legacy.CameraDeviceUserShim.connectBinderShim(CameraDeviceUserShim.java:374)
     at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:317)
     ... 61 more

However, I am sure that I have camera-permission granted, because the same code run successfully in installed version.

Can anyone solve this? Thank you!

Here is the code went crash:

// CameraFragment.java
private TextureView.SurfaceTextureListener mSurfaceTextureListener
        = new TextureView.SurfaceTextureListener() {

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

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
                                            int width, int height) {
        configureTransform(width, height);
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
    }

};

private void openCamera(int width, int height) {
    final Activity activity = getActivity();
    if (null == activity || activity.isFinishing()) {
        return;
    }
    CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
    try {
        Log.d(TAG, "tryAcquire");
        if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
            throw new RuntimeException("Time out waiting to lock camera opening.");
        }
        String cameraId = manager.getCameraIdList()[0];

        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
        StreamConfigurationMap map = characteristics
                .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
        if (map == null) {
            throw new RuntimeException("Cannot get available preview/video sizes");
        }
        mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
        mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                width, height, mVideoSize);

        int orientation = getResources().getConfiguration().orientation;
        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
        } else {
            mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
        }
        configureTransform(width, height);
        mMediaRecorder = new MediaRecorder();
        manager.openCamera(cameraId, mStateCallback, null); // here will crash!!!
    } catch (CameraAccessException e) {
        Toast.makeText(activity, "Cannot access the camera.", Toast.LENGTH_SHORT).show();
        activity.finish();
    } catch (NullPointerException e) {
        ErrorDialog.newInstance(getString(R.string.camera_error))
                .show(getChildFragmentManager(), FRAGMENT_DIALOG);
    } catch (InterruptedException e) {
        throw new RuntimeException("Interrupted while trying to lock camera opening.");
    }
}

And here is how I get the camera permission:

// MainActivity.java
private void requestCameraPermission() {
    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.CAMERA)) {
        Snackbar.make(mRootView, R.string.permission_camera_rationale,
                Snackbar.LENGTH_INDEFINITE)
                .setAction(R.string.ok, new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        ActivityCompat.requestPermissions(MainActivity.this,
                                new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA);
                    }
                })
                .show();
    } else {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA,
                Manifest.permission.INTERNET, Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CAMERA) {
        if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getSupportFragmentManager().beginTransaction().replace(R.id.content_layout, Camera2VideoFragment.newInstance()).commitAllowingStateLoss();
            //getSupportFragmentManager().beginTransaction().replace(R.id.content_layout, new CameraFragment()).commitAllowingStateLoss();
        }
    }
}

And then here is my full manifest file:

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

    <supports-screens
        android:anyDensity="true"
        android:largeScreens="true"
        android:normalScreens="true"
        android:smallScreens="true"
        android:xlargeScreens="true" />

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

    <application
        android:name="com.gomo.minivideo.CameraApp"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/CameraTheme">
        <activity
            android:name="com.gomo.minivideo.MainActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:launchMode="singleTop"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data
                android:name="default-url"
                android:value="https://hugo775128583.github.io/main" />

            <intent-filter android:order="1" android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data
                    android:scheme="https"
                    android:host="hugo775128583.github.io"
                    android:pathPrefix="/main" />
            </intent-filter>
            <intent-filter android:order="1" android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data
                    android:scheme="http"
                    android:host="hugo775128583.github.io"
                    android:pathPrefix="/main" />
            </intent-filter>
        </activity>

        <service android:name="com.jb.zcamera.camera.ProcessVideoService" />

        <activity android:name="com.gomo.minivideo.camera.ShareActivity" />
        <activity android:name="com.gomo.minivideo.camera.VideoViewActivity" />
    </application>

</manifest>
3

There are 3 answers

0
Prags On

With reference to Google issue tracker,

This issue has been fixed and rolled out on 8.1 devices already. We are working with our partners to roll out the fix into the update for 8.0 devices. We expect those updates to be rolled out in the coming months.

If any issue persists, please report at Google issue tracker they will re-open to examine.

0
Volo On

That looks like a bug reproducible on the Android O. It could be reproduced as well with the sample Camera2Basic project adapted into an Instant App:
https://github.com/Idolon-V/InstantApp-Camera

I've filed a bug on the Google tracker: https://issuetracker.google.com/issues/66942980

0
Shiva Kanumala On

add this line in your manifest file

<uses-feature android:name="android.hardware.camera"></uses-feature>