mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 06:09:55 +01:00
Merge branch '696-npe-key-agreement-task' into 'master'
Fix NPE when stopping KeyAgreementTask, improve thread safety This branch fixes #696 and improves the thread safety of the camera code, mostly by adding @UiThread annotations and occasionally by moving stuff onto the UI thread that might have happened on other threads before. Closes #696 See merge request !345
This commit is contained in:
@@ -89,10 +89,8 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
|
|
||||||
private BluetoothStateReceiver receiver;
|
private BluetoothStateReceiver receiver;
|
||||||
private QrCodeDecoder decoder;
|
private QrCodeDecoder decoder;
|
||||||
private boolean gotRemotePayload;
|
private boolean gotRemotePayload, waitingForBluetooth;
|
||||||
|
private KeyAgreementTask task;
|
||||||
private volatile KeyAgreementTask task;
|
|
||||||
private volatile boolean waitingForBluetooth;
|
|
||||||
|
|
||||||
public static ShowQrCodeFragment newInstance() {
|
public static ShowQrCodeFragment newInstance() {
|
||||||
|
|
||||||
@@ -190,26 +188,33 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
if (receiver != null) getActivity().unregisterReceiver(receiver);
|
if (receiver != null) getActivity().unregisterReceiver(receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void startListening() {
|
private void startListening() {
|
||||||
task = keyAgreementTaskFactory.getTask();
|
final KeyAgreementTask oldTask = task;
|
||||||
|
final KeyAgreementTask newTask = keyAgreementTaskFactory.getTask();
|
||||||
|
task = newTask;
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
task.listen();
|
if (oldTask != null) oldTask.stopListening();
|
||||||
|
newTask.listen();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void stopListening() {
|
private void stopListening() {
|
||||||
|
final KeyAgreementTask oldTask = task;
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
task.stopListening();
|
if (oldTask != null) oldTask.stopListening();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
@UiThread
|
||||||
private void openCamera() {
|
private void openCamera() {
|
||||||
LOG.info("Opening camera");
|
LOG.info("Opening camera");
|
||||||
Camera camera;
|
Camera camera;
|
||||||
@@ -229,6 +234,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
cameraView.start(camera, decoder, 0);
|
cameraView.start(camera, decoder, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void releaseCamera() {
|
private void releaseCamera() {
|
||||||
LOG.info("Releasing camera");
|
LOG.info("Releasing camera");
|
||||||
try {
|
try {
|
||||||
@@ -240,6 +246,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void reset() {
|
private void reset() {
|
||||||
statusView.setVisibility(INVISIBLE);
|
statusView.setVisibility(INVISIBLE);
|
||||||
cameraView.setVisibility(VISIBLE);
|
cameraView.setVisibility(VISIBLE);
|
||||||
@@ -248,6 +255,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
startListening();
|
startListening();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void qrCodeScanned(String content) {
|
private void qrCodeScanned(String content) {
|
||||||
try {
|
try {
|
||||||
Payload remotePayload = payloadParser.parse(
|
Payload remotePayload = payloadParser.parse(
|
||||||
@@ -320,7 +328,6 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setQrCode(final Payload localPayload) {
|
private void setQrCode(final Payload localPayload) {
|
||||||
|
|
||||||
listener.runOnUiThread(new Runnable() {
|
listener.runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -397,6 +404,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class BluetoothStateReceiver extends BroadcastReceiver {
|
private class BluetoothStateReceiver extends BroadcastReceiver {
|
||||||
|
@UiThread
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context ctx, Intent intent) {
|
public void onReceive(Context ctx, Intent intent) {
|
||||||
int state = intent.getIntExtra(EXTRA_STATE, 0);
|
int state = intent.getIntExtra(EXTRA_STATE, 0);
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package org.briarproject.android.util;
|
package org.briarproject.android.util;
|
||||||
|
|
||||||
import android.hardware.Camera;
|
import android.hardware.Camera;
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public interface PreviewConsumer {
|
public interface PreviewConsumer {
|
||||||
|
|
||||||
|
@UiThread
|
||||||
void start(Camera camera);
|
void start(Camera camera);
|
||||||
|
|
||||||
|
@UiThread
|
||||||
void stop();
|
void stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.hardware.Camera;
|
|||||||
import android.hardware.Camera.PreviewCallback;
|
import android.hardware.Camera.PreviewCallback;
|
||||||
import android.hardware.Camera.Size;
|
import android.hardware.Camera.Size;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import com.google.zxing.BinaryBitmap;
|
import com.google.zxing.BinaryBitmap;
|
||||||
import com.google.zxing.LuminanceSource;
|
import com.google.zxing.LuminanceSource;
|
||||||
@@ -33,19 +34,24 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void start(Camera camera) {
|
public void start(Camera camera) {
|
||||||
stopped = false;
|
stopped = false;
|
||||||
askForPreviewFrame(camera);
|
askForPreviewFrame(camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
stopped = true;
|
stopped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void askForPreviewFrame(Camera camera) {
|
private void askForPreviewFrame(Camera camera) {
|
||||||
if (!stopped) camera.setOneShotPreviewCallback(this);
|
if (!stopped) camera.setOneShotPreviewCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
@Override
|
||||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||||
if (!stopped) {
|
if (!stopped) {
|
||||||
Size size = camera.getParameters().getPreviewSize();
|
Size size = camera.getParameters().getPreviewSize();
|
||||||
@@ -55,9 +61,9 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
|
|
||||||
private class DecoderTask extends AsyncTask<Void, Void, Void> {
|
private class DecoderTask extends AsyncTask<Void, Void, Void> {
|
||||||
|
|
||||||
final Camera camera;
|
private final Camera camera;
|
||||||
final byte[] data;
|
private final byte[] data;
|
||||||
final int width, height;
|
private final int width, height;
|
||||||
|
|
||||||
DecoderTask(Camera camera, byte[] data, int width, int height) {
|
DecoderTask(Camera camera, byte[] data, int width, int height) {
|
||||||
this.camera = camera;
|
this.camera = camera;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.hardware.Camera.CameraInfo;
|
|||||||
import android.hardware.Camera.Parameters;
|
import android.hardware.Camera.Parameters;
|
||||||
import android.hardware.Camera.Size;
|
import android.hardware.Camera.Size;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
@@ -72,6 +73,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
if (surface != null) surface.release();
|
if (surface != null) surface.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
public void start(Camera camera, PreviewConsumer previewConsumer,
|
public void start(Camera camera, PreviewConsumer previewConsumer,
|
||||||
int rotationDegrees) {
|
int rotationDegrees) {
|
||||||
this.camera = camera;
|
this.camera = camera;
|
||||||
@@ -97,17 +99,18 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
if (surface != null) startPreview(getHolder());
|
if (surface != null) startPreview(getHolder());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
public void stop() {
|
public void stop() {
|
||||||
stopPreview();
|
stopPreview();
|
||||||
try {
|
try {
|
||||||
if (camera != null)
|
if (camera != null) camera.release();
|
||||||
camera.release();
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
LOG.log(WARNING, "Error releasing camera", e);
|
LOG.log(WARNING, "Error releasing camera", e);
|
||||||
}
|
}
|
||||||
camera = null;
|
camera = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void startPreview(SurfaceHolder holder) {
|
private void startPreview(SurfaceHolder holder) {
|
||||||
try {
|
try {
|
||||||
camera.setPreviewDisplay(holder);
|
camera.setPreviewDisplay(holder);
|
||||||
@@ -118,28 +121,29 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void stopPreview() {
|
private void stopPreview() {
|
||||||
try {
|
try {
|
||||||
stopConsumer();
|
stopConsumer();
|
||||||
if (camera != null)
|
if (camera != null) camera.stopPreview();
|
||||||
camera.stopPreview();
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
LOG.log(WARNING, "Error stopping camera preview", e);
|
LOG.log(WARNING, "Error stopping camera preview", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
public void startConsumer() {
|
public void startConsumer() {
|
||||||
if (autoFocus) camera.autoFocus(this);
|
if (autoFocus) camera.autoFocus(this);
|
||||||
previewConsumer.start(camera);
|
previewConsumer.start(camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
public void stopConsumer() {
|
public void stopConsumer() {
|
||||||
if (previewConsumer != null) {
|
if (previewConsumer != null) previewConsumer.stop();
|
||||||
previewConsumer.stop();
|
|
||||||
}
|
|
||||||
if (autoFocus) camera.cancelAutoFocus();
|
if (autoFocus) camera.cancelAutoFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void setDisplayOrientation(int rotationDegrees) {
|
private void setDisplayOrientation(int rotationDegrees) {
|
||||||
int orientation;
|
int orientation;
|
||||||
CameraInfo info = new CameraInfo();
|
CameraInfo info = new CameraInfo();
|
||||||
@@ -160,6 +164,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
displayOrientation = orientation;
|
displayOrientation = orientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private Parameters setSceneMode(Camera camera, Parameters params) {
|
private Parameters setSceneMode(Camera camera, Parameters params) {
|
||||||
List<String> sceneModes = params.getSupportedSceneModes();
|
List<String> sceneModes = params.getSupportedSceneModes();
|
||||||
if (sceneModes == null) return params;
|
if (sceneModes == null) return params;
|
||||||
@@ -172,18 +177,21 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private Parameters disableFlash(Camera camera, Parameters params) {
|
private Parameters disableFlash(Camera camera, Parameters params) {
|
||||||
params.setFlashMode(FLASH_MODE_OFF);
|
params.setFlashMode(FLASH_MODE_OFF);
|
||||||
camera.setParameters(params);
|
camera.setParameters(params);
|
||||||
return camera.getParameters();
|
return camera.getParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private Parameters disableSceneMode(Camera camera, Parameters params) {
|
private Parameters disableSceneMode(Camera camera, Parameters params) {
|
||||||
params.setSceneMode(SCENE_MODE_AUTO);
|
params.setSceneMode(SCENE_MODE_AUTO);
|
||||||
camera.setParameters(params);
|
camera.setParameters(params);
|
||||||
return camera.getParameters();
|
return camera.getParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private Parameters setBestParameters(Camera camera, Parameters params) {
|
private Parameters setBestParameters(Camera camera, Parameters params) {
|
||||||
setVideoStabilisation(params);
|
setVideoStabilisation(params);
|
||||||
setFocusMode(params);
|
setFocusMode(params);
|
||||||
@@ -193,6 +201,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
return camera.getParameters();
|
return camera.getParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void setVideoStabilisation(Parameters params) {
|
private void setVideoStabilisation(Parameters params) {
|
||||||
if (Build.VERSION.SDK_INT >= 15 &&
|
if (Build.VERSION.SDK_INT >= 15 &&
|
||||||
params.isVideoStabilizationSupported()) {
|
params.isVideoStabilizationSupported()) {
|
||||||
@@ -200,6 +209,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void setFocusMode(Parameters params) {
|
private void setFocusMode(Parameters params) {
|
||||||
List<String> focusModes = params.getSupportedFocusModes();
|
List<String> focusModes = params.getSupportedFocusModes();
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Focus modes: " + focusModes);
|
if (LOG.isLoggable(INFO)) LOG.info("Focus modes: " + focusModes);
|
||||||
@@ -218,6 +228,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void setPreviewSize(Parameters params) {
|
private void setPreviewSize(Parameters params) {
|
||||||
if (surfaceWidth == 0 || surfaceHeight == 0) return;
|
if (surfaceWidth == 0 || surfaceHeight == 0) return;
|
||||||
float idealRatio = (float) surfaceWidth / surfaceHeight;
|
float idealRatio = (float) surfaceWidth / surfaceHeight;
|
||||||
@@ -249,11 +260,13 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void enableAutoFocus(String focusMode) {
|
private void enableAutoFocus(String focusMode) {
|
||||||
autoFocus = FOCUS_MODE_AUTO.equals(focusMode) ||
|
autoFocus = FOCUS_MODE_AUTO.equals(focusMode) ||
|
||||||
FOCUS_MODE_MACRO.equals(focusMode);
|
FOCUS_MODE_MACRO.equals(focusMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void logCameraParameters() {
|
private void logCameraParameters() {
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
Parameters params = camera.getParameters();
|
Parameters params = camera.getParameters();
|
||||||
@@ -270,7 +283,17 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceCreated(SurfaceHolder holder) {
|
public void surfaceCreated(final SurfaceHolder holder) {
|
||||||
|
post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
surfaceCreatedUi(holder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void surfaceCreatedUi(SurfaceHolder holder) {
|
||||||
LOG.info("Surface created");
|
LOG.info("Surface created");
|
||||||
if (surface != null) throw new IllegalStateException();
|
if (surface != null) throw new IllegalStateException();
|
||||||
surface = holder.getSurface();
|
surface = holder.getSurface();
|
||||||
@@ -278,7 +301,18 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
|
public void surfaceChanged(final SurfaceHolder holder, int format,
|
||||||
|
final int w, final int h) {
|
||||||
|
post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
surfaceChangedUi(holder, w, h);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void surfaceChangedUi(SurfaceHolder holder, int w, int h) {
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Surface changed: " + w + "x" + h);
|
if (LOG.isLoggable(INFO)) LOG.info("Surface changed: " + w + "x" + h);
|
||||||
// Release the previous surface if necessary
|
// Release the previous surface if necessary
|
||||||
if (surface != null && surface != holder.getSurface())
|
if (surface != null && surface != holder.getSurface())
|
||||||
@@ -300,7 +334,17 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
public void surfaceDestroyed(final SurfaceHolder holder) {
|
||||||
|
post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
surfaceDestroyedUi(holder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void surfaceDestroyedUi(SurfaceHolder holder) {
|
||||||
LOG.info("Surface destroyed");
|
LOG.info("Surface destroyed");
|
||||||
if (holder.getSurface() != surface) throw new IllegalStateException();
|
if (holder.getSurface() != surface) throw new IllegalStateException();
|
||||||
if (surface != null) surface.release();
|
if (surface != null) surface.release();
|
||||||
@@ -317,6 +361,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
}, AUTO_FOCUS_RETRY_DELAY);
|
}, AUTO_FOCUS_RETRY_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void retryAutoFocus() {
|
private void retryAutoFocus() {
|
||||||
try {
|
try {
|
||||||
if (camera != null) camera.autoFocus(this);
|
if (camera != null) camera.autoFocus(this);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class KeyAgreementTaskImpl extends Thread implements
|
|||||||
private Payload localPayload;
|
private Payload localPayload;
|
||||||
private Payload remotePayload;
|
private Payload remotePayload;
|
||||||
|
|
||||||
public KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
|
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
|
||||||
EventBus eventBus, PayloadEncoder payloadEncoder,
|
EventBus eventBus, PayloadEncoder payloadEncoder,
|
||||||
PluginManager pluginManager, Executor ioExecutor) {
|
PluginManager pluginManager, Executor ioExecutor) {
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
|
|||||||
Reference in New Issue
Block a user