diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraException.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraException.java new file mode 100644 index 0000000000000000000000000000000000000000..0bff14ed7c0e6637b1bf14d68a85e23a21a3d016 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraException.java @@ -0,0 +1,14 @@ +package org.briarproject.briar.android.keyagreement; + +import java.io.IOException; + +class CameraException extends IOException { + + CameraException(String message) { + super(message); + } + + CameraException(Throwable cause) { + super(cause); + } +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraView.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraView.java index 19559a6969a984cfc4f8e85eba6043b7d0775de8..4ea6ad246081ed742a6089eb1375098193c3d4e4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/CameraView.java @@ -84,16 +84,14 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback, } @UiThread - public void start() { + public void start() throws CameraException { + LOG.info("Opening camera"); try { - LOG.info("Opening camera"); camera = Camera.open(); - if (camera == null) - throw new RuntimeException("No back-facing camera."); } catch (RuntimeException e) { - LOG.log(WARNING, "Error opening camera", e); - return; + throw new CameraException(e); } + if (camera == null) throw new CameraException("No back-facing camera"); setDisplayOrientation(0); // Use barcode scene mode if it's available Parameters params = camera.getParameters(); @@ -117,64 +115,81 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback, } @UiThread - public void stop() { + public void stop() throws CameraException { if (camera == null) return; stopPreview(); + LOG.info("Releasing camera"); try { - LOG.info("Releasing camera"); camera.release(); } catch (RuntimeException e) { - LOG.log(WARNING, "Error releasing camera", e); + throw new CameraException(e); } camera = null; } @UiThread - private void startPreview(SurfaceHolder holder) { + private void startPreview(SurfaceHolder holder) throws CameraException { LOG.info("Starting preview"); + if (camera == null) throw new CameraException("Camera is null"); try { - if (camera == null) throw new IOException("Camera is null."); camera.setPreviewDisplay(holder); camera.startPreview(); previewStarted = true; startConsumer(); } catch (IOException | RuntimeException e) { - LOG.log(WARNING, "Error starting camera preview", e); + throw new CameraException(e); } } @UiThread - private void stopPreview() { + private void stopPreview() throws CameraException { LOG.info("Stopping preview"); + if (camera == null) throw new CameraException("Camera is null"); try { - if (camera == null) throw new RuntimeException("Camera is null."); stopConsumer(); camera.stopPreview(); } catch (RuntimeException e) { - LOG.log(WARNING, "Error stopping camera preview", e); + throw new CameraException(e); } previewStarted = false; } @UiThread - private void startConsumer() { - if (camera == null) throw new RuntimeException("Camera is null"); - if (autoFocus) camera.autoFocus(this); + private void startConsumer() throws CameraException { + if (camera == null) throw new CameraException("Camera is null"); + if (autoFocus) { + try { + camera.autoFocus(this); + } catch (RuntimeException e) { + throw new CameraException(e); + } + } previewConsumer.start(camera); } @UiThread - private void stopConsumer() { - if (camera == null) throw new RuntimeException("Camera is null"); - if (autoFocus) camera.cancelAutoFocus(); + private void stopConsumer() throws CameraException { + if (camera == null) throw new CameraException("Camera is null"); + if (autoFocus) { + try { + camera.cancelAutoFocus(); + } catch (RuntimeException e) { + throw new CameraException(e); + } + } previewConsumer.stop(); } @UiThread - private void setDisplayOrientation(int rotationDegrees) { + private void setDisplayOrientation(int rotationDegrees) + throws CameraException { int orientation; CameraInfo info = new CameraInfo(); - Camera.getCameraInfo(0, info); + try { + Camera.getCameraInfo(0, info); + } catch (RuntimeException e) { + throw new CameraException(e); + } if (info.facing == CAMERA_FACING_FRONT) { orientation = (info.orientation + rotationDegrees) % 360; orientation = (360 - orientation) % 360; @@ -183,54 +198,70 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback, } if (LOG.isLoggable(INFO)) LOG.info("Display orientation " + orientation + " degrees"); + if (camera == null) throw new CameraException("Camera is null"); try { - if (camera == null) throw new RuntimeException("Camera is null"); camera.setDisplayOrientation(orientation); } catch (RuntimeException e) { - LOG.log(WARNING, "Error setting display orientation", e); + throw new CameraException(e); } displayOrientation = orientation; } @UiThread - private Parameters setSceneMode(Camera camera, Parameters params) { + private Parameters setSceneMode(Camera camera, Parameters params) + throws CameraException { List<String> sceneModes = params.getSupportedSceneModes(); if (sceneModes == null) return params; if (LOG.isLoggable(INFO)) LOG.info("Scene modes: " + sceneModes); if (sceneModes.contains(SCENE_MODE_BARCODE)) { params.setSceneMode(SCENE_MODE_BARCODE); - camera.setParameters(params); - return camera.getParameters(); + try { + camera.setParameters(params); + return camera.getParameters(); + } catch (RuntimeException e) { + throw new CameraException(e); + } } return params; } @UiThread - private Parameters disableFlash(Camera camera, Parameters params) { + private Parameters disableFlash(Camera camera, Parameters params) + throws CameraException { params.setFlashMode(FLASH_MODE_OFF); - camera.setParameters(params); - return camera.getParameters(); + try { + camera.setParameters(params); + return camera.getParameters(); + } catch (RuntimeException e) { + throw new CameraException(e); + } } @UiThread - private Parameters disableSceneMode(Camera camera, Parameters params) { + private Parameters disableSceneMode(Camera camera, Parameters params) + throws CameraException { params.setSceneMode(SCENE_MODE_AUTO); - camera.setParameters(params); - return camera.getParameters(); + try { + camera.setParameters(params); + return camera.getParameters(); + } catch (RuntimeException e) { + throw new CameraException(e); + } } @UiThread - private Parameters setBestParameters(Camera camera, Parameters params) { + private Parameters setBestParameters(Camera camera, Parameters params) + throws CameraException { setVideoStabilisation(params); setFocusMode(params); params.setFlashMode(FLASH_MODE_OFF); setPreviewSize(params); try { camera.setParameters(params); + return camera.getParameters(); } catch (RuntimeException e) { - LOG.log(WARNING, "Error setting best camera parameters", e); + throw new CameraException(e); } - return camera.getParameters(); } @UiThread @@ -299,9 +330,15 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback, } @UiThread - private void logCameraParameters() { - if (camera != null && LOG.isLoggable(INFO)) { - Parameters params = camera.getParameters(); + private void logCameraParameters() throws CameraException { + if (camera == null) throw new AssertionError(); + if (LOG.isLoggable(INFO)) { + Parameters params; + try { + params = camera.getParameters(); + } catch (RuntimeException e) { + throw new CameraException(e); + } if (Build.VERSION.SDK_INT >= 15) { LOG.info("Video stabilisation enabled: " + params.getVideoStabilization()); @@ -319,13 +356,18 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback, post(new Runnable() { @Override public void run() { - surfaceCreatedUi(holder); + try { + surfaceCreatedUi(holder); + } catch (CameraException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } } }); } @UiThread - private void surfaceCreatedUi(SurfaceHolder holder) { + private void surfaceCreatedUi(SurfaceHolder holder) throws CameraException { LOG.info("Surface created"); if (surface != null && surface != holder.getSurface()) { LOG.info("Releasing old surface"); @@ -342,13 +384,19 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback, post(new Runnable() { @Override public void run() { - surfaceChangedUi(holder, w, h); + try { + surfaceChangedUi(holder, w, h); + } catch (CameraException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } } }); } @UiThread - private void surfaceChangedUi(SurfaceHolder holder, int w, int h) { + private void surfaceChangedUi(SurfaceHolder holder, int w, int h) + throws CameraException { if (LOG.isLoggable(INFO)) LOG.info("Surface changed: " + w + "x" + h); if (surface != null && surface != holder.getSurface()) { LOG.info("Releasing old surface"); @@ -365,7 +413,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback, camera.setParameters(params); logCameraParameters(); } catch (RuntimeException e) { - LOG.log(WARNING, "Error setting preview size", e); + throw new CameraException(e); } startPreview(holder); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java index ddb452e35a425633921d308be6f99eae3c2277be..7a57a601b55f5efee400587dd605ea5485e9a1fe 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java @@ -56,6 +56,7 @@ import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.widget.Toast.LENGTH_LONG; import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -143,6 +144,12 @@ public class ShowQrCodeFragment extends BaseEventFragment public void onStart() { super.onStart(); + try { + cameraView.start(); + } catch (CameraException e) { + logCameraExceptionAndFinish(e); + } + // Listen for changes to the Bluetooth state IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_STATE_CHANGED); @@ -162,7 +169,6 @@ public class ShowQrCodeFragment extends BaseEventFragment } else { startListening(); } - cameraView.start(); } @Override @@ -170,7 +176,19 @@ public class ShowQrCodeFragment extends BaseEventFragment super.onStop(); stopListening(); if (receiver != null) getActivity().unregisterReceiver(receiver); - cameraView.stop(); + try { + cameraView.stop(); + } catch (CameraException e) { + logCameraExceptionAndFinish(e); + } + } + + @UiThread + private void logCameraExceptionAndFinish(CameraException e) { + if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + Toast.makeText(getActivity(), R.string.camera_error, + LENGTH_LONG).show(); + finish(); } @UiThread diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 923f2b40a357d64ba007c6658da6c5e08f270104..92536e0a3072abb5c924105838c0981d21b6b93e 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -121,6 +121,7 @@ <string name="contact_already_exists">Contact %s already exists</string> <string name="contact_exchange_failed">Contact exchange failed</string> <string name="qr_code_invalid">The QR code is invalid</string> + <string name="camera_error">Camera error</string> <string name="connecting_to_device">Connecting to device\u2026</string> <string name="authenticating_with_device">Authenticating with device\u2026</string> <string name="connection_aborted_local">Connection aborted by us! This could mean that someone is trying to interfere with your connection</string>