diff --git a/src/net/sf/briar/api/plugins/Plugin.java b/src/net/sf/briar/api/plugins/Plugin.java
index 4debef1e2984059d4fd8d3d3f054e16112b9c962..c6284ca92dd33575311fa07a251b7e65ce9d9a5d 100644
--- a/src/net/sf/briar/api/plugins/Plugin.java
+++ b/src/net/sf/briar/api/plugins/Plugin.java
@@ -14,8 +14,8 @@ public interface Plugin {
 	/** Returns a label for looking up the plugin's translated name. */
 	String getName();
 
-	/** Starts the plugin. */
-	void start() throws IOException;
+	/** Starts the plugin and returns true if it started successfully. */
+	boolean start() throws IOException;
 
 	/** Stops the plugin. */
 	void stop() throws IOException;
diff --git a/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java b/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
index a46a9b8cad395f68d82e9383ac11714d4f964e1f..5fac5810c34649c686f65ec0a6800188cf17c1c8 100644
--- a/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
+++ b/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
@@ -53,8 +53,8 @@ class BluetoothPlugin implements DuplexPlugin {
 	private final Object discoveryLock = new Object();
 	private final ScheduledExecutorService scheduler;
 
-	private boolean running = false; // Locking: this
-	private StreamConnectionNotifier socket = null; // Locking: this
+	private volatile boolean running = false;
+	private volatile StreamConnectionNotifier socket = null;
 
 	// Non-null if running has ever been true
 	private volatile LocalDevice localDevice = null;
@@ -76,7 +76,7 @@ class BluetoothPlugin implements DuplexPlugin {
 		return "BLUETOOTH_PLUGIN_NAME";
 	}
 
-	public void start() throws IOException {
+	public boolean start() throws IOException {
 		// Initialise the Bluetooth stack
 		try {
 			localDevice = LocalDevice.getLocalDevice();
@@ -84,24 +84,21 @@ class BluetoothPlugin implements DuplexPlugin {
 			// On Linux the user may need to install libbluetooth-dev
 			if(OsUtils.isLinux())
 				callback.showMessage("BLUETOOTH_INSTALL_LIBS");
-			throw new IOException(e.toString());
+			return false;
 		}
 		if(LOG.isLoggable(INFO))
 			LOG.info("Local address " + localDevice.getBluetoothAddress());
-		synchronized(this) {
-			running = true;
-		} 
+		running = true;
 		pluginExecutor.execute(new Runnable() {
 			public void run() {
 				bind();
 			}
 		});
+		return true;
 	}
 
 	private void bind() {
-		synchronized(this) {
-			if(!running) return;
-		}
+		if(!running) return;
 		// Advertise the Bluetooth address to contacts
 		TransportProperties p = new TransportProperties();
 		p.put("address", localDevice.getBluetoothAddress());
@@ -114,13 +111,11 @@ class BluetoothPlugin implements DuplexPlugin {
 			if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
 			return;
 		}
-		synchronized(this) {
-			if(!running) {
-				tryToClose(scn);
-				return;
-			}
-			socket = scn;
+		if(!running) {
+			tryToClose(scn);
+			return;
 		}
+		socket = scn;
 		acceptContactConnections(scn);
 	}
 
@@ -155,20 +150,13 @@ class BluetoothPlugin implements DuplexPlugin {
 			BluetoothTransportConnection conn =
 					new BluetoothTransportConnection(s);
 			callback.incomingConnectionCreated(conn);
-			synchronized(this) {
-				if(!running) return;
-			}
+			if(!running) return;
 		}
 	}
 
 	public void stop() {
-		synchronized(this) {
-			running = false;
-			if(socket != null) {
-				tryToClose(socket);
-				socket = null;
-			}
-		}
+		running = false;
+		if(socket != null) tryToClose(socket);
 		scheduler.shutdownNow();
 	}
 
@@ -181,9 +169,7 @@ class BluetoothPlugin implements DuplexPlugin {
 	}
 
 	public void poll(final Collection<ContactId> connected) {
-		synchronized(this) {
-			if(!running) return;
-		}
+		if(!running) return;
 		// Try to connect to known devices in parallel
 		Map<ContactId, TransportProperties> remote =
 				callback.getRemoteProperties();
@@ -195,9 +181,7 @@ class BluetoothPlugin implements DuplexPlugin {
 			if(address != null && uuid != null) {
 				pluginExecutor.execute(new Runnable() {
 					public void run() {
-						synchronized(BluetoothPlugin.this) {
-							if(!running) return;
-						}
+						if(!running) return;
 						String url = makeUrl(address, uuid);
 						DuplexTransportConnection conn = connect(url);
 						if(conn != null)
@@ -219,9 +203,7 @@ class BluetoothPlugin implements DuplexPlugin {
 	}
 
 	public DuplexTransportConnection createConnection(ContactId c) {
-		synchronized(this) {
-			if(!running) return null;
-		}
+		if(!running) return null;
 		TransportProperties p = callback.getRemoteProperties().get(c);
 		if(p == null) return null;
 		String address = p.get("address");
@@ -237,9 +219,7 @@ class BluetoothPlugin implements DuplexPlugin {
 
 	public DuplexTransportConnection sendInvitation(PseudoRandom r,
 			long timeout) {
-		synchronized(this) {
-			if(!running) return null;
-		}
+		if(!running) return null;
 		// Use the same pseudo-random UUID as the contact
 		String uuid = generateUuid(r.nextBytes(16));
 		// Discover nearby devices and connect to any with the right UUID
@@ -265,9 +245,7 @@ class BluetoothPlugin implements DuplexPlugin {
 					return null;
 				}
 			}
-			synchronized(this) {
-				if(!running) return null;
-			}
+			if(!running) return null;
 		}
 		if(url == null) return null;
 		return connect(url);
@@ -280,9 +258,7 @@ class BluetoothPlugin implements DuplexPlugin {
 
 	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
 			long timeout) {
-		synchronized(this) {
-			if(!running) return null;
-		}
+		if(!running) return null;
 		// Use the same pseudo-random UUID as the contact
 		String uuid = generateUuid(r.nextBytes(16));
 		String url = makeUrl("localhost", uuid);
@@ -296,11 +272,9 @@ class BluetoothPlugin implements DuplexPlugin {
 			if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
 			return null;
 		}
-		synchronized(this) {
-			if(!running) {
-				tryToClose(scn);
-				return null;
-			}
+		if(!running) {
+			tryToClose(scn);
+			return null;
 		}
 		// Close the socket when the invitation times out
 		Runnable close = new Runnable() {
@@ -324,9 +298,7 @@ class BluetoothPlugin implements DuplexPlugin {
 
 	private void makeDeviceDiscoverable() {
 		// Try to make the device discoverable (requires root on Linux)
-		synchronized(this) {
-			if(!running) return;
-		}
+		if(!running) return;
 		try {
 			localDevice.setDiscoverable(GIAC);
 		} catch(BluetoothStateException e) {
diff --git a/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java b/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java
index df250dede35cad9c12c3df79b57900dd89c062f9..9f134200b51134ef6a66eae9196b7de3bff14fa6 100644
--- a/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java
+++ b/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java
@@ -61,8 +61,8 @@ class DroidtoothPlugin implements DuplexPlugin {
 	private final DuplexPluginCallback callback;
 	private final long pollingInterval;
 
-	private boolean running = false; // Locking: this
-	private BluetoothServerSocket socket = null; // Locking: this
+	private volatile boolean running = false;
+	private volatile BluetoothServerSocket socket = null;
 
 	// Non-null if running has ever been true
 	private volatile BluetoothAdapter adapter = null;
@@ -86,7 +86,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 		return "BLUETOOTH_PLUGIN_NAME";
 	}
 
-	public void start() throws IOException {
+	public boolean start() throws IOException {
 		// BluetoothAdapter.getDefaultAdapter() must be called on a thread
 		// with a message queue, so submit it to the AndroidExecutor
 		try {
@@ -100,21 +100,18 @@ class DroidtoothPlugin implements DuplexPlugin {
 		} catch(ExecutionException e) {
 			throw new IOException(e.toString());
 		}
-		if(adapter == null) throw new IOException(); // Bluetooth not supported
-		synchronized(this) {
-			running = true;
-		}
+		if(adapter == null) return false; // Bluetooth not supported
+		running = true;
 		pluginExecutor.execute(new Runnable() {
 			public void run() {
 				bind();
 			}
 		});
+		return true;
 	}
 
 	private void bind() {
-		synchronized(this) {
-			if(!running) return;
-		}
+		if(!running) return;
 		if(!enableBluetooth()) {
 			if(LOG.isLoggable(INFO)) LOG.info("Could not enable Bluetooth");
 			return;
@@ -133,20 +130,16 @@ class DroidtoothPlugin implements DuplexPlugin {
 			if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
 			return;
 		}
-		synchronized(this) {
-			if(!running) {
-				tryToClose(ss);
-				return;
-			}
-			socket = ss;
+		if(!running) {
+			tryToClose(ss);
+			return;
 		}
+		socket = ss;
 		acceptContactConnections(ss);
 	}
 
 	private boolean enableBluetooth() {
-		synchronized(this) {
-			if(!running) return false;
-		}
+		if(!running) return false;
 		if(adapter.isEnabled()) return true;
 		// Try to enable the adapter and wait for the result
 		IntentFilter filter = new IntentFilter(ACTION_STATE_CHANGED);
@@ -190,20 +183,13 @@ class DroidtoothPlugin implements DuplexPlugin {
 			DroidtoothTransportConnection conn =
 					new DroidtoothTransportConnection(s);
 			callback.incomingConnectionCreated(conn);
-			synchronized(this) {
-				if(!running) return;
-			}
+			if(!running) return;
 		}
 	}
 
 	public void stop() throws IOException {
-		synchronized(this) {
-			running = false;
-			if(socket != null) {
-				tryToClose(socket);
-				socket = null;
-			}
-		}
+		running = false;
+		if(socket != null) tryToClose(socket);
 	}
 
 	public boolean shouldPoll() {
@@ -215,9 +201,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 	}
 
 	public void poll(Collection<ContactId> connected) {
-		synchronized(this) {
-			if(!running) return;
-		}
+		if(!running) return;
 		// Try to connect to known devices in parallel
 		Map<ContactId, TransportProperties> remote =
 				callback.getRemoteProperties();
@@ -229,9 +213,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 			if(address != null && uuid != null) {
 				pluginExecutor.execute(new Runnable() {
 					public void run() {
-						synchronized(DroidtoothPlugin.this) {
-							if(!running) return;
-						}
+						if(!running) return;
 						DuplexTransportConnection conn = connect(address, uuid);
 						if(conn != null)
 							callback.outgoingConnectionCreated(c, conn);
@@ -269,9 +251,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 	}
 
 	public DuplexTransportConnection createConnection(ContactId c) {
-		synchronized(this) {
-			if(!running) return null;
-		}
+		if(!running) return null;
 		TransportProperties p = callback.getRemoteProperties().get(c);
 		if(p == null) return null;
 		String address = p.get("address");
@@ -286,9 +266,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 
 	public DuplexTransportConnection sendInvitation(PseudoRandom r,
 			long timeout) {
-		synchronized(this) {
-			if(!running) return null;
-		}
+		if(!running) return null;
 		// Use the same pseudo-random UUID as the contact
 		String uuid = UUID.nameUUIDFromBytes(r.nextBytes(16)).toString();
 		// Register to receive Bluetooth discovery intents
@@ -311,9 +289,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 
 	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
 			long timeout) {
-		synchronized(this) {
-			if(!running) return null;
-		}
+		if(!running) return null;
 		// Use the same pseudo-random UUID as the contact
 		UUID uuid = UUID.nameUUIDFromBytes(r.nextBytes(16));
 		// Bind a new server socket to accept the invitation connection
@@ -394,9 +370,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 			for(final String address : addresses) {
 				pluginExecutor.execute(new Runnable() {
 					public void run() {
-						synchronized(DroidtoothPlugin.this) {
-							if(!running) return;
-						}
+						if(!running) return;
 						DuplexTransportConnection conn = connect(address, uuid);
 						if(conn != null) {
 							connection = conn;
diff --git a/src/net/sf/briar/plugins/file/RemovableDrivePlugin.java b/src/net/sf/briar/plugins/file/RemovableDrivePlugin.java
index 90c00821733710d0f61e7910930495d66d8ef47f..15914a70d6f3175d307cdc2af3a7d96fb5cdbf7a 100644
--- a/src/net/sf/briar/plugins/file/RemovableDrivePlugin.java
+++ b/src/net/sf/briar/plugins/file/RemovableDrivePlugin.java
@@ -47,9 +47,10 @@ implements RemovableDriveMonitor.Callback {
 		return "REMOVABLE_DRIVE_PLUGIN_NAME";
 	}
 
-	public void start() throws IOException {
+	public boolean start() throws IOException {
 		running = true;
 		monitor.start(this);
+		return true;
 	}
 
 	public void stop() throws IOException {
diff --git a/src/net/sf/briar/plugins/modem/ModemPlugin.java b/src/net/sf/briar/plugins/modem/ModemPlugin.java
index b985fbb644d41ef7f2ae5e4439650917162a98ae..d97d6232ee4ed5c2cba91c8f31dd34a99ce8eb8f 100644
--- a/src/net/sf/briar/plugins/modem/ModemPlugin.java
+++ b/src/net/sf/briar/plugins/modem/ModemPlugin.java
@@ -43,8 +43,9 @@ class ModemPlugin implements DuplexPlugin {
 		return "MODEM_PLUGIN_NAME";
 	}
 
-	public void start() throws IOException {
+	public boolean start() throws IOException {
 		// FIXME
+		return false;
 	}
 
 	public void stop() throws IOException {
diff --git a/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java b/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
index 47bf16d7ec2fa1d888ee4944d9b430bfcca3d15e..ecfbfcb3f92c08fc903d2dfda46ed896d793e3c1 100644
--- a/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
+++ b/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
@@ -110,9 +110,7 @@ class LanTcpPlugin extends TcpPlugin {
 
 	public DuplexTransportConnection sendInvitation(PseudoRandom r,
 			long timeout) {
-		synchronized(this) {
-			if(!running) return null;
-		}
+		if(!running) return null;
 		// Use the invitation code to choose the group address and port
 		InetSocketAddress mcast = chooseMulticastGroup(r);
 		// Bind a multicast socket for receiving packets
@@ -157,9 +155,7 @@ class LanTcpPlugin extends TcpPlugin {
 					break;
 				}
 				now = System.currentTimeMillis();
-				synchronized(this) {
-					if(!running) return null;
-				}
+				if(!running) return null;
 			}
 			if(LOG.isLoggable(INFO))
 				LOG.info("Timeout while sending invitation");
@@ -242,9 +238,7 @@ class LanTcpPlugin extends TcpPlugin {
 
 	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
 			long timeout) {
-		synchronized(this) {
-			if(!running) return null;
-		}
+		if(!running) return null;
 		// Use the invitation code to choose the group address and port
 		InetSocketAddress mcast = chooseMulticastGroup(r);
 		// Bind a TCP socket for receiving connections
@@ -299,9 +293,7 @@ class LanTcpPlugin extends TcpPlugin {
 						interval += 1000;
 					}
 				}
-				synchronized(this) {
-					if(!running) return null;
-				}
+				if(!running) return null;
 			}
 			if(LOG.isLoggable(INFO))
 				LOG.info("Timeout while accepting invitation");
diff --git a/src/net/sf/briar/plugins/tcp/TcpPlugin.java b/src/net/sf/briar/plugins/tcp/TcpPlugin.java
index 0ca380a0109128216fc1910a7adeb130a5e88581..b0b77ca59bfac40f88bdf485aae1ba5ad9780726 100644
--- a/src/net/sf/briar/plugins/tcp/TcpPlugin.java
+++ b/src/net/sf/briar/plugins/tcp/TcpPlugin.java
@@ -32,8 +32,8 @@ abstract class TcpPlugin implements DuplexPlugin {
 	protected final DuplexPluginCallback callback;
 	protected final long pollingInterval;
 
-	protected boolean running = false; // Locking: this
-	private ServerSocket socket = null; // Locking: this
+	protected volatile boolean running = false;
+	private volatile ServerSocket socket = null;
 
 	/**
 	 * Returns zero or more socket addresses on which the plugin should listen,
@@ -48,15 +48,14 @@ abstract class TcpPlugin implements DuplexPlugin {
 		this.pollingInterval = pollingInterval;
 	}
 
-	public void start() throws IOException {
-		synchronized(this) {
-			running = true;
-		}
+	public boolean start() throws IOException {
+		running = true;
 		pluginExecutor.execute(new Runnable() {
 			public void run() {
 				bind();
 			}
 		});
+		return true;
 	}
 
 	private void bind() {
@@ -83,13 +82,11 @@ abstract class TcpPlugin implements DuplexPlugin {
 			if(LOG.isLoggable(INFO)) LOG.info("Could not bind server socket");
 			return;
 		}
-		synchronized(this) {
-			if(!running) {
-				tryToClose(ss);
-				return;
-			}
-			socket = ss;
+		if(!running) {
+			tryToClose(ss);
+			return;
 		}
+		socket = ss;
 		if(LOG.isLoggable(INFO)) {
 			String addr = ss.getInetAddress().getHostAddress();
 			int port = ss.getLocalPort();
@@ -129,18 +126,13 @@ abstract class TcpPlugin implements DuplexPlugin {
 			}
 			TcpTransportConnection conn = new TcpTransportConnection(s);
 			callback.incomingConnectionCreated(conn);
-			synchronized(this) {
-				if(!running) return;
-			}
+			if(!running) return;
 		}
 	}
 
-	public synchronized void stop() throws IOException {
+	public void stop() throws IOException {
 		running = false;
-		if(socket != null) {
-			tryToClose(socket);
-			socket = null;
-		}
+		if(socket != null) tryToClose(socket);
 	}
 
 	public boolean shouldPoll() {
@@ -152,9 +144,7 @@ abstract class TcpPlugin implements DuplexPlugin {
 	}
 
 	public void poll(Collection<ContactId> connected) {
-		synchronized(this) {
-			if(!running) return;
-		}
+		if(!running) return;
 		Map<ContactId, TransportProperties> remote =
 				callback.getRemoteProperties();
 		for(final ContactId c : remote.keySet()) {
@@ -173,9 +163,7 @@ abstract class TcpPlugin implements DuplexPlugin {
 	}
 
 	public DuplexTransportConnection createConnection(ContactId c) {
-		synchronized(this) {
-			if(!running) return null;
-		}
+		if(!running) return null;
 		SocketAddress addr = getRemoteSocketAddress(c);
 		Socket s = new Socket();
 		if(addr == null || s == null) return null;
diff --git a/src/net/sf/briar/plugins/tor/TorPlugin.java b/src/net/sf/briar/plugins/tor/TorPlugin.java
index ad555b45bd644512f73e79ffd953e71e246b2664..034603232fb1c43ca37b46a33409b654c57085f9 100644
--- a/src/net/sf/briar/plugins/tor/TorPlugin.java
+++ b/src/net/sf/briar/plugins/tor/TorPlugin.java
@@ -66,7 +66,7 @@ class TorPlugin implements DuplexPlugin {
 		return "TOR_PLUGIN_NAME";
 	}
 
-	public void start() throws IOException {
+	public boolean start() throws IOException {
 		synchronized(this) {
 			running = true;
 		}
@@ -75,6 +75,7 @@ class TorPlugin implements DuplexPlugin {
 				bind();
 			}
 		});
+		return true;
 	}
 
 	private void bind() {