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>