'Calling of camera from different thread

I am writing a code that should capture images and send it to a python server. One part of this code requires calling the camera. My solution for this was to simulate a button press for the button connected to the camera. When i trigger the camera using the button on the interface however if i simulate the button press from within the code it fails.

Any help to fix this is appreciated

Camera usage is modified from here

Code (Comments for everywhere i face an issue):

private int captured = 0;
private final int REQ_CODE = 100;
private static final String TAG = MainActivity.class.getSimpleName();
private String res;
private TextureView textureView;
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0, 90);
    ORIENTATIONS.append(Surface.ROTATION_90, 0);
    ORIENTATIONS.append(Surface.ROTATION_180, 270);
    ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
private String cameraId;
protected CameraDevice cameraDevice;
protected CameraCaptureSession cameraCaptureSessions;
protected CaptureRequest.Builder captureRequestBuilder;
private Size imageDimension;
private ImageReader imageReader;
private File file;
private static final int REQUEST_CAMERA_PERMISSION = 200;
private Handler mBackgroundHandler;
private HandlerThread mBackgroundThread;
ImageButton initiateButton =  null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    textureView = (TextureView) findViewById(R.id.textureView);
    assert textureView != null;
    currentMode = "A";
    initiateButton = findViewById(R.id.initiateButton);
    initiateButton.setOnClickListener( new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    takePicture();
                }
            });
        }
    });

}

public void captureImage(){
    try {
        initiateButton.callOnClick(); //Simulate a press of the camera image button
    }catch (Exception E){
        System.out.println(E);
    }

}

public void runMain(){//Called after image is saved 
    if (captured == 0){
        try {
            TimeUnit.MILLISECONDS.sleep(1000);
            captureImage();//Start point of issue with camera
        }catch (Exception e){
            e.printStackTrace();
        }
        return ;
    }
    captured = 0;
    String cat = currentMode.toUpperCase();
    System.out.println(cat);
    Thread t = null;
    if (cat.equals("A")) {
        /*
        Some Code to run on a different thread
         */
    } else if (cat.equals("B")) {
        /*
        Some Code to run on a different thread
         */
    } else{
        return ;
    }
    //t.start(); -> start the thread
    /*while (t.isAlive()){
        Wait for thread to finish processing
    }*/
    if (currentMode.equalsIgnoreCase("A")){
        runMain(); //recursive (value changes from thread A as exit condition)
    }else{
        currentMode = "";
    }

}

TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int 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 final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice camera) {
        Log.e(TAG, "onOpened");
        cameraDevice = camera;
        createCameraPreview();
    }
    @Override
    public void onDisconnected(CameraDevice camera) {
        cameraDevice.close();
    }
    @Override
    public void onError(CameraDevice camera, int error) {
        cameraDevice.close();
        cameraDevice = null;
    }
};
final CameraCaptureSession.CaptureCallback captureCallbackListener = new CameraCaptureSession.CaptureCallback() {
    @Override
    public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
        super.onCaptureCompleted(session, request, result);
        Toast.makeText(MainActivity.this, "Saved:" + file, Toast.LENGTH_LONG).show();
        createCameraPreview();
    }
};
protected void startBackgroundThread() {
    mBackgroundThread = new HandlerThread("Camera Background");
    mBackgroundThread.start();
    mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
protected void stopBackgroundThread() {
    mBackgroundThread.quitSafely();
    try {
        mBackgroundThread.join();
        mBackgroundThread = null;
        mBackgroundHandler = null;
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
protected void takePicture() {
    if(null == cameraDevice) {
        Log.e(TAG, "cameraDevice is null");
        return;
    }
    CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        int width = 640;
        int height = 480;
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraDevice.getId());
        Size[] jpegSizes = null;
        if (characteristics != null) {
            jpegSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageFormat.JPEG);
        }

        if (jpegSizes != null && 0 < jpegSizes.length) {
            width = jpegSizes[0].getWidth();
            height = jpegSizes[0].getHeight();
        }
        ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
        List<Surface> outputSurfaces = new ArrayList<Surface>(2);
        outputSurfaces.add(reader.getSurface());
        outputSurfaces.add(new Surface(textureView.getSurfaceTexture()));
        final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        captureBuilder.addTarget(reader.getSurface());
        captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
        final File file = new File(Environment.getExternalStorageDirectory()+"/virtual_eye_image.jpg");
        ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = null;
                try {
                    image = reader.acquireLatestImage();
                    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                    byte[] bytes = new byte[buffer.capacity()];
                    buffer.get(bytes);
                    save(bytes);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (image != null) {
                        image.close();
                    }
                }
            }
            private void save(byte[] bytes) throws IOException {
                OutputStream output = null;
                try {
                    output = new FileOutputStream(file);
                    output.write(bytes);
                } finally {
                    if (null != output) {
                        output.close();
                    }
                }
            }
        };
        reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);
        final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
                Toast.makeText(MainActivity.this, "Saved:" + file, Toast.LENGTH_SHORT).show();
                captured = 1;
                runMain();
            }
        };
        cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(CameraCaptureSession session) {
                try {
                    session.capture(captureBuilder.build(), captureListener, mBackgroundHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onConfigureFailed(CameraCaptureSession session) {
            }
        }, mBackgroundHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}
protected void createCameraPreview() {
    try {
        SurfaceTexture texture = textureView.getSurfaceTexture();
        assert texture != null;
        texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
        Surface surface = new Surface(texture);
        captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        captureRequestBuilder.addTarget(surface);
        cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback(){
            @Override
            public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                if (null == cameraDevice) {
                    return;
                }
                cameraCaptureSessions = cameraCaptureSession;
                updatePreview();
            }
            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                Toast.makeText(MainActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();
            }
        }, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}
private void openCamera() {
    CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    Log.e(TAG, "is camera open");
    try {
        cameraId = manager.getCameraIdList()[0];
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
        StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        assert map != null;
        imageDimension = map.getOutputSizes(SurfaceTexture.class)[0];
        // Add permission for camera and let user grant the permission
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_PERMISSION);
            return;
        }
        manager.openCamera(cameraId, stateCallback, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
    Log.e(TAG, "openCamera X");
}
protected void updatePreview() {
    if(null == cameraDevice) {
        Log.e(TAG, "updatePreview error, return");
    }
    captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
    try {
        cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_CAMERA_PERMISSION) {
        if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
            Toast.makeText(MainActivity.this, "Sorry!!!, you can't use this app without granting permission", Toast.LENGTH_LONG).show();
            finish();
        }
    }
}
@Override
protected void onResume() {
    super.onResume();
    Log.e(TAG, "onResume");
    startBackgroundThread();
    if (textureView.isAvailable()) {
        openCamera();
    } else {
        textureView.setSurfaceTextureListener(textureListener);
    }
}
@Override
protected void onPause() {
    Log.e(TAG, "onPause");
    stopBackgroundThread();
    super.onPause();
}

Run Log:

E/MainActivity: onResume
E/MainActivity: is camera open
D/IMonitor: Load library imonitor_jni
D/: [ZeroHung]zrhung_send_event: wp=257, ret=-1
E/ZrHungImpl:  sendAppFreezeEvent failed!
I/PermissionManager: camera remind result:true
I/CameraManager: open camera: 0, package name: com.example.virtualeye
I/BackgroundPermManager: pkgName: com.example.virtualeye,pid: 10545 ,uidOf3RdApk: 10205 ,permType: 0 ,permCfg: 1
I/HwCameraUtil: notifySurfaceFlingerCameraStatus : isFront = false , isOpend = true
E/MainActivity: openCamera X
E/MainActivity: openCamera X
W/MessageQueue: Handler (android.os.Handler) {28a8a52} sending message to a Handler on a dead thread
    java.lang.IllegalStateException: Handler (android.os.Handler) {28a8a52} sending message to a Handler on a dead thread
        at android.os.MessageQueue.enqueueMessage(MessageQueue.java:654)
        at android.os.Handler.enqueueMessage(Handler.java:769)
        at android.os.Handler.sendMessageAtTime(Handler.java:718)
        at android.os.Handler.sendMessageDelayed(Handler.java:688)
        at android.os.Handler.post(Handler.java:405)
        at android.hardware.camera2.impl.CameraDeviceImpl$CameraHandlerExecutor.execute(CameraDeviceImpl.java:2353)
        at android.hardware.camera2.impl.CallbackProxies$SessionStateCallbackProxy.onClosed(CallbackProxies.java:104)
        at android.hardware.camera2.impl.CameraCaptureSessionImpl$SequenceDrainListener.onDrained(CameraCaptureSessionImpl.java:886)
        at android.hardware.camera2.utils.TaskDrainer.lambda$postDrained$0(TaskDrainer.java:207)
        at android.hardware.camera2.utils.-$$Lambda$TaskDrainer$Jb53sDskEXp_qIjiikQeCRx0wJs.run(Unknown Source:2)
        at android.os.Handler.handleCallback(Handler.java:907)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:216)
        at android.app.ActivityThread.main(ActivityThread.java:7625)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)
W/System.err: android.hardware.camera2.CameraAccessException: CAMERA_DISCONNECTED (2): checkPidStatus:1773: The camera device has been disconnected
W/System.err:     at android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:753)
        at android.hardware.camera2.impl.ICameraDeviceUserWrapper.submitRequestList(ICameraDeviceUserWrapper.java:86)
W/System.err:     at android.hardware.camera2.impl.CameraDeviceImpl.submitCaptureRequest(CameraDeviceImpl.java:1051)
        at android.hardware.camera2.impl.CameraDeviceImpl.capture(CameraDeviceImpl.java:918)
        at android.hardware.camera2.impl.CameraCaptureSessionImpl.capture(CameraCaptureSessionImpl.java:173)
        at com.example.virtualeye.MainActivity$11.onConfigured(MainActivity.java:394)
        at android.hardware.camera2.impl.CallbackProxies$SessionStateCallbackProxy.lambda$onConfigured$0(CallbackProxies.java:53)
        at android.hardware.camera2.impl.-$$Lambda$CallbackProxies$SessionStateCallbackProxy$soW0qC12Osypoky6AfL3P2-TeDw.run(Unknown Source:4)
        at android.os.Handler.handleCallback(Handler.java:907)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:216)
        at android.app.ActivityThread.main(ActivityThread.java:7625)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)
    Caused by: android.os.ServiceSpecificException: checkPidStatus:1773: The camera device has been disconnected (code 4)
W/System.err:     at android.os.Parcel.createException(Parcel.java:1967)
        at android.os.Parcel.readException(Parcel.java:1921)
        at android.os.Parcel.readException(Parcel.java:1871)
        at android.hardware.camera2.ICameraDeviceUser$Stub$Proxy.submitRequestList(ICameraDeviceUser.java:361)
        at android.hardware.camera2.impl.ICameraDeviceUserWrapper.submitRequestList(ICameraDeviceUserWrapper.java:84)
        ... 13 more
I/CameraDevice-JV-0: close camera: 0, package name: com.example.virtualeye
I/BackgroundPermManager: pkgName: com.example.virtualeye,pid: 10545 ,uidOf3RdApk: 10205 ,permType: 0 ,permCfg: 0
I/HwCameraUtil: notifySurfaceFlingerCameraStatus : isFront = false , isOpend = false
E/MainActivity: onOpened
Process 10545 terminated.


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source