diff --git a/briar-core/src/net/sf/briar/plugins/modem/Crc32.java b/briar-core/src/net/sf/briar/plugins/modem/Crc32.java
index 9cd9d2d81c5d5f68c4c1f4a9d27c60a37fc7dc9d..8279d3c9715622acf8cc939e95d4b9cc49aa2051 100644
--- a/briar-core/src/net/sf/briar/plugins/modem/Crc32.java
+++ b/briar-core/src/net/sf/briar/plugins/modem/Crc32.java
@@ -15,10 +15,9 @@ class Crc32 {
 		}
 	}
 
-	static long update(long c, byte[] b, int off, int len) {
-		for(int i = off; i < off + len; i++) {
+	private static long update(long c, byte[] b, int off, int len) {
+		for(int i = off; i < off + len; i++)
 			c = TABLE[(int) ((c ^ b[i]) & 0xff)] ^ (c >> 8);
-		}
 		return c;
 	}
 
diff --git a/briar-core/src/net/sf/briar/plugins/modem/Frame.java b/briar-core/src/net/sf/briar/plugins/modem/Frame.java
index 3feb0e47e6581c7201b3170bba212912461e944c..fc80bd6b88ab0388a57eb1db572af1898752d56b 100644
--- a/briar-core/src/net/sf/briar/plugins/modem/Frame.java
+++ b/briar-core/src/net/sf/briar/plugins/modem/Frame.java
@@ -8,7 +8,7 @@ abstract class Frame {
 
 	protected final byte[] buf;
 
-	Frame(byte[] buf) {
+	protected Frame(byte[] buf) {
 		this.buf = buf;
 	}
 
@@ -49,8 +49,8 @@ abstract class Frame {
 	public boolean equals(Object o) {
 		if(o instanceof Frame) {
 			Frame f = (Frame) o;
-			if(buf[0] != f.buf[0]) return false;
-			return getSequenceNumber() == f.getSequenceNumber();
+			return buf[0] == f.buf[0] &&
+					getSequenceNumber() == f.getSequenceNumber();
 		}
 		return false;
 	}
diff --git a/briar-core/src/net/sf/briar/plugins/modem/Modem.java b/briar-core/src/net/sf/briar/plugins/modem/Modem.java
index ffb372f9831c1a4c9b7e6d912a921ff757d6735a..004f7f18be35afd85c8821d6380833698a6c0ca7 100644
--- a/briar-core/src/net/sf/briar/plugins/modem/Modem.java
+++ b/briar-core/src/net/sf/briar/plugins/modem/Modem.java
@@ -28,10 +28,10 @@ interface Modem {
 	boolean dial(String number) throws IOException;
 
 	/** Returns a stream for reading from the currently connected call. */
-	InputStream getInputStream();
+	InputStream getInputStream() throws IOException;
 
 	/** Returns a stream for writing to the currently connected call. */
-	OutputStream getOutputStream();
+	OutputStream getOutputStream() throws IOException;
 
 	/** Hangs up the modem, ending the currently connected call. */
 	void hangUp() throws IOException;
diff --git a/briar-core/src/net/sf/briar/plugins/modem/ModemImpl.java b/briar-core/src/net/sf/briar/plugins/modem/ModemImpl.java
index 0fc79fdd2a2cf70372712e431d624bd1b4761b95..ea26b698a3a46fdd6eceadecf3b45c582e62d04e 100644
--- a/briar-core/src/net/sf/briar/plugins/modem/ModemImpl.java
+++ b/briar-core/src/net/sf/briar/plugins/modem/ModemImpl.java
@@ -9,7 +9,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.concurrent.Executor;
-import java.util.concurrent.Semaphore;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Logger;
 
@@ -32,20 +31,20 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 	private final Executor executor;
 	private final Callback callback;
 	private final SerialPort port;
-	private final AtomicBoolean initialised, connected;
-	private final Semaphore offHook;
+	private final AtomicBoolean initialised; // Locking: self
+	private final AtomicBoolean connected; // Locking: self
 	private final byte[] line;
 
 	private int lineLen = 0;
 
-	private volatile ReliabilityLayer reliabilityLayer = null;
+	private ReliabilityLayer reliabilityLayer = null; // Locking: this
+	private boolean offHook = false; // Locking: this;
 
 	ModemImpl(Executor executor, Callback callback, String portName) {
 		this.executor = executor;
 		this.callback = callback;
 		port = new SerialPort(portName);
 		initialised = new AtomicBoolean(false);
-		offHook = new Semaphore(1);
 		connected = new AtomicBoolean(false);
 		line = new byte[MAX_LINE_LENGTH];
 	}
@@ -79,19 +78,19 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 		try {
 			synchronized(initialised) {
 				if(!initialised.get()) initialised.wait(OK_TIMEOUT);
+				if(initialised.get()) return;
 			}
 		} catch(InterruptedException e) {
 			tryToClose(port);
 			Thread.currentThread().interrupt();
 			throw new IOException("Interrupted while initialising modem");
 		}
-		if(!initialised.get())
-			throw new IOException("Modem did not respond");
+		tryToClose(port);
+		throw new IOException("Modem did not respond");
 	}
 
 	public void stop() throws IOException {
-		if(offHook.tryAcquire()) offHook.release();
-		else hangUp();
+		hangUp();
 		try {
 			port.closePort();
 		} catch(SerialPortException e) {
@@ -100,13 +99,16 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 	}
 
 	public boolean dial(String number) throws IOException {
-		if(!offHook.tryAcquire()) {
-			if(LOG.isLoggable(INFO))
-				LOG.info("Not dialling - call in progress");
-			return false;
+		synchronized(this) {
+			if(offHook) {
+				if(LOG.isLoggable(INFO))
+					LOG.info("Not dialling - call in progress");
+				return false;
+			}
+			reliabilityLayer = new ReliabilityLayer(this);
+			reliabilityLayer.start();
+			offHook = true;
 		}
-		reliabilityLayer = new ReliabilityLayer(this);
-		reliabilityLayer.start();
 		if(LOG.isLoggable(INFO)) LOG.info("Dialling");
 		try {
 			port.writeBytes(("ATDT" + number + "\r\n").getBytes("US-ASCII"));
@@ -117,36 +119,46 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 		try {
 			synchronized(connected) {
 				if(!connected.get()) connected.wait(CONNECT_TIMEOUT);
+				if(connected.get()) return true;
 			}
 		} catch(InterruptedException e) {
 			tryToClose(port);
 			Thread.currentThread().interrupt();
 			throw new IOException("Interrupted while connecting outgoing call");
 		}
-		if(connected.get()) return true;
 		hangUp();
 		return false;
 	}
 
-	public InputStream getInputStream() {
-		return reliabilityLayer.getInputStream();
+	public synchronized InputStream getInputStream() throws IOException {
+		if(offHook) return reliabilityLayer.getInputStream();
+		throw new IOException("Not connected");
 	}
 
-	public OutputStream getOutputStream() {
-		return reliabilityLayer.getOutputStream();
+	public synchronized OutputStream getOutputStream() throws IOException {
+		if(offHook) return reliabilityLayer.getOutputStream();
+		throw new IOException("Not connected");
 	}
 
 	public void hangUp() throws IOException {
+		synchronized(this) {
+			if(!offHook) {
+				if(LOG.isLoggable(INFO))
+					LOG.info("Not hanging up - already on the hook");
+				return;
+			}
+			reliabilityLayer.stop();
+			reliabilityLayer = null;
+			offHook = false;
+		}
 		if(LOG.isLoggable(INFO)) LOG.info("Hanging up");
+		connected.set(false);
 		try {
 			port.setDTR(false);
 		} catch(SerialPortException e) {
 			tryToClose(port);
 			throw new IOException(e.toString());
 		}
-		reliabilityLayer.stop();
-		connected.set(false);
-		offHook.release();
 	}
 
 	public void handleWrite(byte[] b) throws IOException {
@@ -193,8 +205,8 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 				if(LOG.isLoggable(INFO)) LOG.info("Modem status: " + s);
 				if(s.startsWith("CONNECT")) {
 					synchronized(connected) {
-						if(!connected.getAndSet(true))
-							connected.notifyAll();
+						connected.set(true);
+						connected.notifyAll();
 					}
 					// There might be data in the buffer as well as text
 					int off = i + 1;
@@ -211,8 +223,8 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 					}
 				} else if(s.equals("OK")) {
 					synchronized(initialised) {
-						if(!initialised.getAndSet(true))
-							initialised.notifyAll();
+						initialised.set(true);
+						initialised.notifyAll();
 					}
 				} else if(s.equals("RING")) {
 					executor.execute(new Runnable() {
@@ -233,13 +245,16 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 	}
 
 	private void answer() throws IOException {
-		if(!offHook.tryAcquire()) {
-			if(LOG.isLoggable(INFO))
-				LOG.info("Not answering - call in progress");
-			return;
+		synchronized(this) {
+			if(offHook) {
+				if(LOG.isLoggable(INFO))
+					LOG.info("Not answering - call in progress");
+				return;
+			}
+			reliabilityLayer = new ReliabilityLayer(this);
+			reliabilityLayer.start();
+			offHook = true;
 		}
-		reliabilityLayer = new ReliabilityLayer(this);
-		reliabilityLayer.start();
 		if(LOG.isLoggable(INFO)) LOG.info("Answering");
 		try {
 			port.writeBytes("ATA\r\n".getBytes("US-ASCII"));
@@ -247,16 +262,18 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 			tryToClose(port);
 			throw new IOException(e.toString());
 		}
+		boolean success = false;
 		try {
 			synchronized(connected) {
 				if(!connected.get()) connected.wait(CONNECT_TIMEOUT);
+				success = connected.get();
 			}
 		} catch(InterruptedException e) {
 			tryToClose(port);
 			Thread.currentThread().interrupt();
 			throw new IOException("Interrupted while connecting incoming call");
 		}
-		if(connected.get()) callback.incomingCallConnected();
+		if(success) callback.incomingCallConnected();
 		else hangUp();
 	}
 
diff --git a/briar-core/src/net/sf/briar/plugins/modem/ModemPlugin.java b/briar-core/src/net/sf/briar/plugins/modem/ModemPlugin.java
index eaa8536bddfa7bf01106436b7cb919a5d31ac9ba..e77444d02753952ef801c929d495ccf2f94fc3f8 100644
--- a/briar-core/src/net/sf/briar/plugins/modem/ModemPlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/modem/ModemPlugin.java
@@ -208,11 +208,11 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 
 		private final CountDownLatch finished = new CountDownLatch(1);
 
-		public InputStream getInputStream() {
+		public InputStream getInputStream() throws IOException {
 			return modem.getInputStream();
 		}
 
-		public OutputStream getOutputStream() {
+		public OutputStream getOutputStream() throws IOException {
 			return modem.getOutputStream();
 		}