diff --git a/api/net/sf/briar/api/plugins/BatchTransportCallback.java b/api/net/sf/briar/api/plugins/BatchTransportCallback.java
index 5c65d16cf3c0fcc4308d5c1b7aec4e6e0c8fe0c1..525bbb8b2838734a3fad9924ec40bf6deebab460 100644
--- a/api/net/sf/briar/api/plugins/BatchTransportCallback.java
+++ b/api/net/sf/briar/api/plugins/BatchTransportCallback.java
@@ -3,7 +3,6 @@ package net.sf.briar.api.plugins;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.transport.BatchTransportReader;
 import net.sf.briar.api.transport.BatchTransportWriter;
-import net.sf.briar.api.transport.TransportCallback;
 
 /**
  * An interface for receiving readers and writers created by a batch-mode
diff --git a/api/net/sf/briar/api/plugins/StreamTransportCallback.java b/api/net/sf/briar/api/plugins/StreamTransportCallback.java
index eb468ee4513e5b51b631bb92aa4bc986532a70cb..7496f402680a0abf15833648a6d9eebc5cceba0f 100644
--- a/api/net/sf/briar/api/plugins/StreamTransportCallback.java
+++ b/api/net/sf/briar/api/plugins/StreamTransportCallback.java
@@ -2,7 +2,6 @@ package net.sf.briar.api.plugins;
 
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.transport.StreamTransportConnection;
-import net.sf.briar.api.transport.TransportCallback;
 
 /**
  * An interface for receiving connections created by a stream-mode transport
diff --git a/api/net/sf/briar/api/plugins/TransportCallback.java b/api/net/sf/briar/api/plugins/TransportCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4cac73ceb6c92c8448163d5f154ef28794388ce
--- /dev/null
+++ b/api/net/sf/briar/api/plugins/TransportCallback.java
@@ -0,0 +1,50 @@
+package net.sf.briar.api.plugins;
+
+import java.util.Map;
+
+import net.sf.briar.api.ContactId;
+import net.sf.briar.api.TransportConfig;
+import net.sf.briar.api.TransportProperties;
+
+/**
+ * An interface through which a transport plugin interacts with the rest of
+ * the application.
+ */
+public interface TransportCallback {
+
+	/** Returns the plugin's configuration. */
+	TransportConfig getConfig();
+
+	/** Returns the plugin's local transport properties. */
+	TransportProperties getLocalProperties();
+
+	/** Returns the plugin's remote transport properties. */
+	Map<ContactId, TransportProperties> getRemoteProperties();
+
+	/** Stores the plugin's configuration. */
+	void setConfig(TransportConfig c);
+
+	/** Stores the plugin's local transport properties. */
+	void setLocalProperties(TransportProperties p);
+
+	/**
+	 * Presents the user with a choice among two or more named options and
+	 * returns the user's response. The message may consist of a translatable
+	 * format string and arguments.
+	 * @return An index into the array of options indicating the user's choice,
+	 * or -1 if the user cancelled the choice.
+	 */
+	int showChoice(String[] options, String... message);
+
+	/**
+	 * Asks the user to confirm an action and returns the user's response. The
+	 * message may consist of a translatable format string and arguments.
+	 */
+	boolean showConfirmationMessage(String... message);
+
+	/**
+	 * Shows a message to the user. The message may consist of a translatable
+	 * format string and arguments.
+	 */
+	void showMessage(String... message);
+}
diff --git a/api/net/sf/briar/api/plugins/TransportPlugin.java b/api/net/sf/briar/api/plugins/TransportPlugin.java
index 213a89e3d6a1507f80293c0299d38fe3dc522efd..159553f285c20d17968222cf3b9fb4f1a820441a 100644
--- a/api/net/sf/briar/api/plugins/TransportPlugin.java
+++ b/api/net/sf/briar/api/plugins/TransportPlugin.java
@@ -1,12 +1,8 @@
 package net.sf.briar.api.plugins;
 
 import java.io.IOException;
-import java.util.Map;
 
-import net.sf.briar.api.ContactId;
-import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportId;
-import net.sf.briar.api.TransportProperties;
 
 public interface TransportPlugin {
 
@@ -14,22 +10,11 @@ public interface TransportPlugin {
 	TransportId getId();
 
 	/** Starts the plugin. */
-	void start(TransportProperties localProperties,
-			Map<ContactId, TransportProperties> remoteProperties,
-			TransportConfig config) throws IOException;
+	void start() throws IOException;
 
 	/** Stops the plugin. */
 	void stop() throws IOException;
 
-	/** Updates the plugin's local transport properties. */
-	void setLocalProperties(TransportProperties p);
-
-	/** Updates the plugin's transport properties for the given contact. */
-	void setRemoteProperties(ContactId c, TransportProperties p);
-
-	/** Updates the plugin's configuration properties. */
-	void setConfig(TransportConfig c);
-
 	/**
 	 * Returns true if the plugin's poll() method should be called
 	 * periodically to attempt to establish connections.
diff --git a/api/net/sf/briar/api/transport/TransportCallback.java b/api/net/sf/briar/api/transport/TransportCallback.java
deleted file mode 100644
index ba78a06a13d92fac94acfc095789ee5727271515..0000000000000000000000000000000000000000
--- a/api/net/sf/briar/api/transport/TransportCallback.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package net.sf.briar.api.transport;
-
-import net.sf.briar.api.TransportConfig;
-import net.sf.briar.api.TransportProperties;
-
-public interface TransportCallback {
-
-	void setLocalProperties(TransportProperties p);
-
-	void setConfig(TransportConfig c);
-
-	void showMessage(String... message);
-
-	boolean showConfirmationMessage(String... message);
-
-	int showChoice(String[] choices, String... message);
-}
diff --git a/components/net/sf/briar/plugins/AbstractPlugin.java b/components/net/sf/briar/plugins/AbstractPlugin.java
index 992b89da3ed008967ad28e05f177f1e7a4f0d052..1527a6730134bd8277bc4efa2b897acc1191a2a6 100644
--- a/components/net/sf/briar/plugins/AbstractPlugin.java
+++ b/components/net/sf/briar/plugins/AbstractPlugin.java
@@ -1,56 +1,28 @@
 package net.sf.briar.plugins;
 
 import java.io.IOException;
-import java.util.Map;
 import java.util.concurrent.Executor;
 
-import net.sf.briar.api.ContactId;
-import net.sf.briar.api.TransportConfig;
-import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.plugins.TransportPlugin;
 
 public abstract class AbstractPlugin implements TransportPlugin {
 
 	protected final Executor executor;
 
-	// These fields should be accessed with this's lock held
-	protected TransportProperties localProperties = null;
-	protected Map<ContactId, TransportProperties> remoteProperties = null;
-	protected TransportConfig config = null;
+	// This field must only be accessed with this's lock held
 	protected boolean started = false;
 
 	protected AbstractPlugin(Executor executor) {
 		this.executor = executor;
 	}
 
-	public synchronized void start(TransportProperties localProperties,
-			Map<ContactId, TransportProperties> remoteProperties,
-			TransportConfig config) throws IOException {
+	public synchronized void start() throws IOException {
 		if(started) throw new IllegalStateException();
 		started = true;
-		this.localProperties = localProperties;
-		this.remoteProperties = remoteProperties;
-		this.config = config;
 	}
 
 	public synchronized void stop() throws IOException {
 		if(!started) throw new IllegalStateException();
 		started = false;
 	}
-
-	public synchronized void setLocalProperties(TransportProperties p) {
-		if(!started) throw new IllegalStateException();
-		localProperties = p;
-	}
-
-	public synchronized void setRemoteProperties(ContactId c,
-			TransportProperties p) {
-		if(!started) throw new IllegalStateException();
-		remoteProperties.put(c, p);
-	}
-
-	public synchronized void setConfig(TransportConfig c) {
-		if(!started) throw new IllegalStateException();
-		this.config = c;
-	}
 }
diff --git a/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java b/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
index ac933c783ae25043c31e59c91a874e4a4d8d7d6b..f493728227f90631c056b4bf58d8c9a9f63a308e 100644
--- a/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
+++ b/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
@@ -1,7 +1,6 @@
 package net.sf.briar.plugins.bluetooth;
 
 import java.io.IOException;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -55,13 +54,11 @@ class BluetoothPlugin extends AbstractPlugin implements StreamTransportPlugin {
 	}
 
 	@Override
-	public void start(TransportProperties localProperties,
-			Map<ContactId, TransportProperties> remoteProperties,
-			TransportConfig config) throws IOException {
+	public void start() throws IOException {
 		// Initialise the Bluetooth stack
 		try {
 			synchronized(this) {
-				super.start(localProperties, remoteProperties, config);
+				super.start();
 				localDevice = LocalDevice.getLocalDevice();
 			}
 		} catch(UnsatisfiedLinkError e) {
@@ -95,10 +92,11 @@ class BluetoothPlugin extends AbstractPlugin implements StreamTransportPlugin {
 		LocalDevice ld;
 		synchronized(this) {
 			if(!started) return;
-			uuid = config.get("uuid");
+			TransportConfig c = callback.getConfig();
+			uuid = c.get("uuid");
+			if(uuid == null) uuid = createAndSetUuid(c);
 			ld = localDevice;
 		}
-		if(uuid == null) uuid = createAndSetUuid();
 		// Try to make the device discoverable (requires root on Linux)
 		try {
 			ld.setDiscoverable(DiscoveryAgent.GIAC);
@@ -130,15 +128,10 @@ class BluetoothPlugin extends AbstractPlugin implements StreamTransportPlugin {
 		setLocalBluetoothAddress(ld.getBluetoothAddress());
 	}
 
-	private String createAndSetUuid() {
+	private synchronized String createAndSetUuid(TransportConfig c) {
 		byte[] b = new byte[16];
 		new Random().nextBytes(b); // FIXME: Use a SecureRandom?
 		String uuid = StringUtils.toHexString(b);
-		TransportConfig c;
-		synchronized(this) {
-			if(!started) return uuid;
-			c = new TransportConfig(config);
-		}
 		c.put("uuid", uuid);
 		callback.setConfig(c);
 		return uuid;
@@ -174,12 +167,9 @@ class BluetoothPlugin extends AbstractPlugin implements StreamTransportPlugin {
 		}
 	}
 
-	private void setLocalBluetoothAddress(String address) {
-		TransportProperties p;
-		synchronized(this) {
-			if(!started) return;
-			p = new TransportProperties(localProperties);
-		}
+	private synchronized void setLocalBluetoothAddress(String address) {
+		if(!started) return;
+		TransportProperties p = callback.getLocalProperties();
 		p.put("address", address);
 		callback.setLocalProperties(p);
 	}
@@ -194,28 +184,28 @@ class BluetoothPlugin extends AbstractPlugin implements StreamTransportPlugin {
 
 	public synchronized void poll() {
 		if(!started) return;
-		executor.execute(createConnectors(remoteProperties.keySet()));
+		executor.execute(createConnectors());
 	}
 
-	private Runnable createConnectors(final Collection<ContactId> contacts) {
+	private Runnable createConnectors() {
 		return new Runnable() {
 			public void run() {
-				connectAndCallBack(contacts);
+				connectAndCallBack();
 			}
 		};
 	}
 
-	private void connectAndCallBack(Collection<ContactId> contacts) {
-		Map<ContactId, String> discovered = discover(contacts);
+	private void connectAndCallBack() {
+		Map<ContactId, String> discovered = discover();
 		for(Entry<ContactId, String> e : discovered.entrySet()) {
 			ContactId c = e.getKey();
 			String url = e.getValue();
-			StreamTransportConnection conn = createConnection(c, url);
+			StreamTransportConnection conn = connect(c, url);
 			if(conn != null) callback.outgoingConnectionCreated(c, conn);
 		}
 	}
 
-	private Map<ContactId, String> discover(Collection<ContactId> contacts) {
+	private Map<ContactId, String> discover() {
 		DiscoveryAgent discoveryAgent;
 		Map<String, ContactId> addresses;
 		Map<ContactId, String> uuids;
@@ -225,12 +215,13 @@ class BluetoothPlugin extends AbstractPlugin implements StreamTransportPlugin {
 			discoveryAgent = localDevice.getDiscoveryAgent();
 			addresses = new HashMap<String, ContactId>();
 			uuids = new HashMap<ContactId, String>();
-			for(Entry<ContactId, TransportProperties> e
-					: remoteProperties.entrySet()) {
+			Map<ContactId, TransportProperties> remote =
+				callback.getRemoteProperties();
+			for(Entry<ContactId, TransportProperties> e : remote.entrySet()) {
 				ContactId c = e.getKey();
-				TransportProperties properties = e.getValue();
-				String address = properties.get("address");
-				String uuid = properties.get("uuid");
+				TransportProperties p = e.getValue();
+				String address = p.get("address");
+				String uuid = p.get("uuid");
 				if(address != null && uuid != null) {
 					addresses.put(address, c);
 					uuids.put(c, uuid);
@@ -250,12 +241,13 @@ class BluetoothPlugin extends AbstractPlugin implements StreamTransportPlugin {
 		return listener.getUrls();
 	}
 
-	private StreamTransportConnection createConnection(ContactId c,
-			String url) {
+	private StreamTransportConnection connect(ContactId c, String url) {
 		try {
 			synchronized(this) {
 				if(!started) return null;
-				if(!remoteProperties.containsKey(c)) return null;
+				Map<ContactId, TransportProperties> remote =
+					callback.getRemoteProperties();
+				if(!remote.containsKey(c)) return null;
 			}
 			StreamConnection s = (StreamConnection) Connector.open(url);
 			return new BluetoothTransportConnection(s);
@@ -266,8 +258,8 @@ class BluetoothPlugin extends AbstractPlugin implements StreamTransportPlugin {
 	}
 
 	public StreamTransportConnection createConnection(ContactId c) {
-		Map<ContactId, String> discovered = discover(Collections.singleton(c));
+		Map<ContactId, String> discovered = discover();
 		String url = discovered.get(c);
-		return url == null ? null : createConnection(c, url);
+		return url == null ? null : connect(c, url);
 	}
 }
diff --git a/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java b/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java
index f2680f7a1222d6585d03d5bd34c44d6def6df91b..16093fcfb5b36a613f5c36dc8fbcd84d141b2d96 100644
--- a/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java
+++ b/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java
@@ -3,15 +3,11 @@ package net.sf.briar.plugins.file;
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import net.sf.briar.api.ContactId;
-import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportId;
-import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.plugins.BatchTransportCallback;
 
 class RemovableDrivePlugin extends FilePlugin
@@ -38,15 +34,13 @@ implements RemovableDriveMonitor.Callback {
 	}
 
 	@Override
-	public void start(TransportProperties localProperties,
-			Map<ContactId, TransportProperties> remoteProperties,
-			TransportConfig config) throws IOException {
-		super.start(localProperties, remoteProperties, config);
+	public synchronized void start() throws IOException {
+		super.start();
 		monitor.start(this);
 	}
 
 	@Override
-	public void stop() throws IOException {
+	public synchronized void stop() throws IOException {
 		super.stop();
 		monitor.stop();
 	}
diff --git a/components/net/sf/briar/plugins/socket/SimpleSocketPlugin.java b/components/net/sf/briar/plugins/socket/SimpleSocketPlugin.java
index f03cd2ead1637d4788631347a211466b5a5a4725..4a7359ed8471cd831d33f41478ef73fdab1c45b1 100644
--- a/components/net/sf/briar/plugins/socket/SimpleSocketPlugin.java
+++ b/components/net/sf/briar/plugins/socket/SimpleSocketPlugin.java
@@ -39,23 +39,36 @@ class SimpleSocketPlugin extends SocketPlugin {
 	}
 
 	@Override
-	protected SocketAddress getLocalSocketAddress() {
+	protected Socket createClientSocket() throws IOException {
+		assert started;
+		return new Socket();
+	}
+
+	@Override
+	protected ServerSocket createServerSocket() throws IOException {
 		assert started;
-		return createSocketAddress(localProperties);
+		return new ServerSocket();
 	}
 
 	@Override
-	protected SocketAddress getSocketAddress(ContactId c) {
+	protected synchronized SocketAddress getLocalSocketAddress() {
 		assert started;
-		TransportProperties properties = remoteProperties.get(c);
-		if(properties == null) return null;
-		return createSocketAddress(properties);
+		return createSocketAddress(callback.getLocalProperties());
 	}
 
-	private SocketAddress createSocketAddress(TransportProperties properties) {
-		assert properties != null;
-		String host = properties.get("host");
-		String portString = properties.get("port");
+	@Override
+	protected synchronized SocketAddress getRemoteSocketAddress(ContactId c) {
+		assert started;
+		TransportProperties p = callback.getRemoteProperties().get(c);
+		return p == null ? null : createSocketAddress(p);
+	}
+
+	private synchronized SocketAddress createSocketAddress(
+			TransportProperties p) {
+		assert started;
+		assert p != null;
+		String host = p.get("host");
+		String portString = p.get("port");
 		if(host == null || portString == null) return null;
 		int port;
 		try {
@@ -67,32 +80,17 @@ class SimpleSocketPlugin extends SocketPlugin {
 	}
 
 	@Override
-	protected void setLocalSocketAddress(SocketAddress s) {
-		TransportProperties p;
-		synchronized(this) {
-			if(!started) return;
-			p = new TransportProperties(localProperties);
-		}
+	protected synchronized void setLocalSocketAddress(SocketAddress s) {
+		assert started;
 		if(!(s instanceof InetSocketAddress))
 			throw new IllegalArgumentException();
 		InetSocketAddress i = (InetSocketAddress) s;
 		String host = i.getAddress().getHostAddress();
 		String port = String.valueOf(i.getPort());
 		// FIXME: Special handling for private IP addresses?
+		TransportProperties p = callback.getLocalProperties();
 		p.put("host", host);
 		p.put("port", port);
 		callback.setLocalProperties(p);
 	}
-
-	@Override
-	protected Socket createClientSocket() throws IOException {
-		assert started;
-		return new Socket();
-	}
-
-	@Override
-	protected ServerSocket createServerSocket() throws IOException {
-		assert started;
-		return new ServerSocket();
-	}
 }
diff --git a/components/net/sf/briar/plugins/socket/SocketPlugin.java b/components/net/sf/briar/plugins/socket/SocketPlugin.java
index f8158663b25d3f9f24f72ed41ebb4ebbbf8e2a69..a9debf74e5b7719413467dfe87ef232d69923e64 100644
--- a/components/net/sf/briar/plugins/socket/SocketPlugin.java
+++ b/components/net/sf/briar/plugins/socket/SocketPlugin.java
@@ -4,14 +4,11 @@ import java.io.IOException;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.SocketAddress;
-import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import net.sf.briar.api.ContactId;
-import net.sf.briar.api.TransportConfig;
-import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.plugins.StreamTransportCallback;
 import net.sf.briar.api.plugins.StreamTransportPlugin;
 import net.sf.briar.api.transport.StreamTransportConnection;
@@ -25,16 +22,17 @@ implements StreamTransportPlugin {
 
 	protected final StreamTransportCallback callback;
 
-	// This field should be accessed with this's lock held
+	// This field must only be accessed with this's lock held
 	protected ServerSocket socket = null;
 
 	protected abstract void setLocalSocketAddress(SocketAddress s);
 
-	// These methods should be called with this's lock held and started == true
-	protected abstract SocketAddress getLocalSocketAddress();
-	protected abstract SocketAddress getSocketAddress(ContactId c);
+	// These methods must only be called with this's lock held and
+	// started == true
 	protected abstract Socket createClientSocket() throws IOException;
 	protected abstract ServerSocket createServerSocket() throws IOException;
+	protected abstract SocketAddress getLocalSocketAddress();
+	protected abstract SocketAddress getRemoteSocketAddress(ContactId c);
 
 	protected SocketPlugin(Executor executor,
 			StreamTransportCallback callback) {
@@ -43,10 +41,8 @@ implements StreamTransportPlugin {
 	}
 
 	@Override
-	public synchronized void start(TransportProperties localProperties,
-			Map<ContactId, TransportProperties> remoteProperties,
-			TransportConfig config) throws IOException {
-		super.start(localProperties, remoteProperties, config);
+	public synchronized void start() throws IOException {
+		super.start();
 		executor.execute(createBinder());
 	}
 
@@ -130,30 +126,11 @@ implements StreamTransportPlugin {
 		}
 	}
 
-	@Override
-	public synchronized void setLocalProperties(TransportProperties p) {
-		super.setLocalProperties(p);
-		// Close and reopen the socket if its address has changed
-		if(socket != null) {
-			SocketAddress addr = socket.getLocalSocketAddress();
-			if(!getLocalSocketAddress().equals(addr)) {
-				try {
-					socket.close();
-				} catch(IOException e) {
-					if(LOG.isLoggable(Level.WARNING))
-						LOG.warning(e.getMessage());
-				}
-				socket = null;
-				executor.execute(createBinder());
-			}
-		}
-	}
-
 	public synchronized void poll() {
 		// Subclasses may not support polling
 		if(!shouldPoll()) throw new UnsupportedOperationException();
 		if(!started) return;
-		for(ContactId c : remoteProperties.keySet()) {
+		for(ContactId c : callback.getRemoteProperties().keySet()) {
 			executor.execute(createConnector(c));
 		}
 	}
@@ -177,7 +154,7 @@ implements StreamTransportPlugin {
 		try {
 			synchronized(this) {
 				if(!started) return null;
-				addr = getSocketAddress(c);
+				addr = getRemoteSocketAddress(c);
 				s = createClientSocket();
 			}
 			if(addr == null || s == null) return null;
diff --git a/test/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java b/test/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java
index 4e23ee9dce63ec4a4cd1248a8796ac72ecae85bb..fb0c4e01941c1d375e57a1a58950c98b4847ee3c 100644
--- a/test/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java
+++ b/test/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java
@@ -24,22 +24,18 @@ public class BluetoothClientTest {
 			System.exit(1);
 		}
 		ContactId contactId = new ContactId(0);
-		TransportProperties localProperties = new TransportProperties();
-		Map<ContactId, TransportProperties> remoteProperties =
-			new HashMap<ContactId, TransportProperties>();
-		TransportConfig config = new TransportConfig();
-		StreamTransportCallback callback = new ClientCallback();
+		ClientCallback callback = new ClientCallback();
 		// Store the server's Bluetooth address and UUID
 		TransportProperties p = new TransportProperties();
 		p.put("address", args[0]);
 		p.put("uuid", BluetoothServerTest.UUID);
-		remoteProperties.put(contactId, p);
+		callback.remote.put(contactId, p);
 		// Create the plugin
 		BluetoothPlugin plugin =
 			new BluetoothPlugin(new ImmediateExecutor(), callback, 0L);
 		// Start the plugin
 		System.out.println("Starting plugin");
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 		// Try to connect to the server
 		System.out.println("Creating connection");
 		StreamTransportConnection conn = plugin.createConnection(contactId);
@@ -66,20 +62,41 @@ public class BluetoothClientTest {
 
 	private static class ClientCallback implements StreamTransportCallback {
 
-		public void setLocalProperties(TransportProperties p) {}
+		private TransportConfig config = new TransportConfig();
+		private TransportProperties local = new TransportProperties();
+		private Map<ContactId, TransportProperties> remote =
+			new HashMap<ContactId, TransportProperties>();
 
-		public void setConfig(TransportConfig c) {}
+		public TransportConfig getConfig() {
+			return config;
+		}
 
-		public void showMessage(String... message) {}
+		public TransportProperties getLocalProperties() {
+			return local;
+		}
 
-		public boolean showConfirmationMessage(String... message) {
-			return false;
+		public Map<ContactId, TransportProperties> getRemoteProperties() {
+			return remote;
+		}
+
+		public void setConfig(TransportConfig c) {
+			config = c;
 		}
 
-		public int showChoice(String[] choices, String... message) {
+		public void setLocalProperties(TransportProperties p) {
+			local = p;
+		}
+
+		public int showChoice(String[] options, String... message) {
 			return -1;
 		}
 
+		public boolean showConfirmationMessage(String... message) {
+			return false;
+		}
+
+		public void showMessage(String... message) {}
+
 		public void incomingConnectionCreated(StreamTransportConnection c) {}
 
 		public void outgoingConnectionCreated(ContactId contactId,
diff --git a/test/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java b/test/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java
index 8ccc1b3e4c2ce5ad0b60f58c12cd4f90d9beca73..b1339026bae95a1c222a6aa957dedc459a3f1770 100644
--- a/test/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java
+++ b/test/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java
@@ -2,7 +2,7 @@ package net.sf.briar.plugins.bluetooth;
 
 import java.io.IOException;
 import java.io.PrintStream;
-import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Scanner;
 
@@ -21,19 +21,15 @@ public class BluetoothServerTest {
 	public static final String CHALLENGE = "Potatoes!";
 
 	public static void main(String[] args) throws Exception {
-		TransportProperties localProperties = new TransportProperties();
-		Map<ContactId, TransportProperties> remoteProperties =
-			Collections.emptyMap();
-		TransportConfig config = new TransportConfig();
-		StreamTransportCallback callback = new ServerCallback();
+		ServerCallback callback = new ServerCallback();
 		// Store the UUID
-		config.put("uuid", UUID);
+		callback.config.put("uuid", UUID);
 		// Create the plugin
 		BluetoothPlugin plugin =
 			new BluetoothPlugin(new ImmediateExecutor(), callback, 0L);
 		// Start the plugin
 		System.out.println("Starting plugin");
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 		// Wait for a connection
 		System.out.println("Waiting for connection");
 		synchronized(callback) {
@@ -46,20 +42,41 @@ public class BluetoothServerTest {
 
 	private static class ServerCallback implements StreamTransportCallback {
 
-		public void setLocalProperties(TransportProperties p) {}
+		private TransportConfig config = new TransportConfig();
+		private TransportProperties local = new TransportProperties();
+		private Map<ContactId, TransportProperties> remote =
+			new HashMap<ContactId, TransportProperties>();
 
-		public void setConfig(TransportConfig c) {}
+		public TransportConfig getConfig() {
+			return config;
+		}
 
-		public void showMessage(String... message) {}
+		public TransportProperties getLocalProperties() {
+			return local;
+		}
 
-		public boolean showConfirmationMessage(String... message) {
-			return false;
+		public Map<ContactId, TransportProperties> getRemoteProperties() {
+			return remote;
+		}
+
+		public void setConfig(TransportConfig c) {
+			config = c;
 		}
 
-		public int showChoice(String[] choices, String... message) {
+		public void setLocalProperties(TransportProperties p) {
+			local = p;
+		}
+
+		public int showChoice(String[] options, String... message) {
 			return -1;
 		}
 
+		public boolean showConfirmationMessage(String... message) {
+			return false;
+		}
+
+		public void showMessage(String... message) {}
+
 		public void incomingConnectionCreated(StreamTransportConnection conn) {
 			System.out.println("Connection received");
 			try {
diff --git a/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java b/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java
index 5b4e524697dec44396f22de6479899f1611e3ef8..fb9797551553e5cd12b09bf955d6b37b40485e99 100644
--- a/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java
+++ b/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java
@@ -5,16 +5,12 @@ import java.io.FileOutputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.Executor;
 
 import junit.framework.TestCase;
 import net.sf.briar.TestUtils;
 import net.sf.briar.api.ContactId;
-import net.sf.briar.api.TransportConfig;
-import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.plugins.BatchTransportCallback;
 import net.sf.briar.api.transport.BatchTransportWriter;
 import net.sf.briar.api.transport.TransportConstants;
@@ -32,16 +28,8 @@ public class RemovableDrivePluginTest extends TestCase {
 	private final File testDir = TestUtils.getTestDirectory();
 	private final ContactId contactId = new ContactId(0);
 
-	private TransportProperties localProperties = null;
-	private Map<ContactId, TransportProperties> remoteProperties = null;
-	private TransportConfig config = null;
-
 	@Before
 	public void setUp() {
-		localProperties = new TransportProperties();
-		remoteProperties = new HashMap<ContactId, TransportProperties>();
-		remoteProperties.put(contactId, new TransportProperties());
-		config = new TransportConfig();
 		testDir.mkdirs();
 	}
 
@@ -86,7 +74,7 @@ public class RemovableDrivePluginTest extends TestCase {
 
 		RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
 				callback, finder, monitor);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 
 		assertNull(plugin.createWriter(contactId));
 
@@ -121,7 +109,7 @@ public class RemovableDrivePluginTest extends TestCase {
 
 		RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
 				callback, finder, monitor);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 
 		assertNull(plugin.createWriter(contactId));
 		File[] files = drive1.listFiles();
@@ -158,7 +146,7 @@ public class RemovableDrivePluginTest extends TestCase {
 
 		RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
 				callback, finder, monitor);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 
 		assertNull(plugin.createWriter(contactId));
 		File[] files = drive1.listFiles();
@@ -197,7 +185,7 @@ public class RemovableDrivePluginTest extends TestCase {
 
 		RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
 				callback, finder, monitor);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 
 		assertNull(plugin.createWriter(contactId));
 		File[] files = drive1.listFiles();
@@ -236,7 +224,7 @@ public class RemovableDrivePluginTest extends TestCase {
 
 		RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
 				callback, finder, monitor);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 
 		assertNotNull(plugin.createWriter(contactId));
 		// The output file should exist and should be empty
@@ -279,7 +267,7 @@ public class RemovableDrivePluginTest extends TestCase {
 
 		RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
 				callback, finder, monitor);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 
 		BatchTransportWriter writer = plugin.createWriter(contactId);
 		assertNotNull(writer);
@@ -319,7 +307,7 @@ public class RemovableDrivePluginTest extends TestCase {
 
 		RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
 				callback, finder, monitor);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 
 		plugin.driveInserted(testDir);
 
@@ -366,7 +354,7 @@ public class RemovableDrivePluginTest extends TestCase {
 
 		RemovableDrivePlugin plugin = new RemovableDrivePlugin(
 				new ImmediateExecutor(), callback, finder, monitor);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 
 		File f = new File(testDir, "abcdefgh.dat");
 		OutputStream out = new FileOutputStream(f);
@@ -396,7 +384,7 @@ public class RemovableDrivePluginTest extends TestCase {
 
 		RemovableDrivePlugin plugin = new RemovableDrivePlugin(
 				new ImmediateExecutor(), callback, finder, monitor);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 
 		File f = new File(testDir, "abcdefgh.dat");
 		OutputStream out = new FileOutputStream(f);
diff --git a/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java b/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java
index 16d8ebd6c3befcc9efb708135b970fcb77fb769b..1e96dcc9fa590d136d35cedafec4eee359a249ac 100644
--- a/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java
+++ b/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java
@@ -18,39 +18,25 @@ import net.sf.briar.api.plugins.StreamTransportCallback;
 import net.sf.briar.api.transport.StreamTransportConnection;
 import net.sf.briar.plugins.ImmediateExecutor;
 
-import org.junit.Before;
 import org.junit.Test;
 
 public class SimpleSocketPluginTest extends TestCase {
 
 	private final ContactId contactId = new ContactId(0);
 
-	private TransportProperties localProperties = null;
-	private Map<ContactId, TransportProperties> remoteProperties = null;
-	private TransportConfig config = null;
-
-	@Before
-	public void setUp() {
-		localProperties = new TransportProperties();
-		remoteProperties = new HashMap<ContactId, TransportProperties>();
-		remoteProperties.put(contactId, new TransportProperties());
-		config = new TransportConfig();
-	}
-
 	@Test
 	public void testIncomingConnection() throws Exception {
-		StubCallback callback = new StubCallback();
-		localProperties.put("host", "127.0.0.1");
-		localProperties.put("port", "0");
+		StreamCallback callback = new StreamCallback();
+		callback.local.put("host", "127.0.0.1");
+		callback.local.put("port", "0");
 		SimpleSocketPlugin plugin =
 			new SimpleSocketPlugin(new ImmediateExecutor(), callback, 0L);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 		// The plugin should have bound a socket and stored the port number
-		assertNotNull(callback.localProperties);
-		String host = callback.localProperties.get("host");
+		String host = callback.local.get("host");
 		assertNotNull(host);
 		assertEquals("127.0.0.1", host);
-		String portString = callback.localProperties.get("port");
+		String portString = callback.local.get("port");
 		assertNotNull(portString);
 		int port = Integer.valueOf(portString);
 		assertTrue(port > 0 && port < 65536);
@@ -75,10 +61,10 @@ public class SimpleSocketPluginTest extends TestCase {
 
 	@Test
 	public void testOutgoingConnection() throws Exception {
-		StubCallback callback = new StubCallback();
+		StreamCallback callback = new StreamCallback();
 		SimpleSocketPlugin plugin =
 			new SimpleSocketPlugin(new ImmediateExecutor(), callback, 0L);
-		plugin.start(localProperties, remoteProperties, config);
+		plugin.start();
 		// Listen on a local port
 		final ServerSocket ss = new ServerSocket();
 		ss.bind(new InetSocketAddress("127.0.0.1", 0), 10);
@@ -97,10 +83,10 @@ public class SimpleSocketPluginTest extends TestCase {
 			}
 		}.start();
 		// Tell the plugin about the port
-		TransportProperties properties = new TransportProperties();
-		properties.put("host", "127.0.0.1");
-		properties.put("port", String.valueOf(port));
-		plugin.setRemoteProperties(contactId, properties);
+		TransportProperties p = new TransportProperties();
+		p.put("host", "127.0.0.1");
+		p.put("port", String.valueOf(port));
+		callback.remote.put(contactId, p);
 		// Connect to the port
 		StreamTransportConnection conn = plugin.createConnection(contactId);
 		assertNotNull(conn);
@@ -113,87 +99,45 @@ public class SimpleSocketPluginTest extends TestCase {
 		plugin.stop();
 	}
 
-	@Test
-	public void testUpdatingPropertiesReopensSocket() throws Exception {
-		StubCallback callback = new StubCallback();
-		localProperties.put("host", "127.0.0.1");
-		localProperties.put("port", "0");
-		SimpleSocketPlugin plugin =
-			new SimpleSocketPlugin(new ImmediateExecutor(), callback, 0L);
-		plugin.start(localProperties, remoteProperties, config);
-		// The plugin should have bound a socket and stored the port number
-		assertNotNull(callback.localProperties);
-		String host = callback.localProperties.get("host");
-		assertNotNull(host);
-		assertEquals("127.0.0.1", host);
-		String portString = callback.localProperties.get("port");
-		assertNotNull(portString);
-		int port = Integer.valueOf(portString);
-		assertTrue(port > 0 && port < 65536);
-		// The plugin should be listening on the port
-		InetSocketAddress addr = new InetSocketAddress(host, port);
-		Socket s = new Socket();
-		assertEquals(0, callback.incomingConnections);
-		s.connect(addr, 100);
-		Thread.sleep(10);
-		assertEquals(1, callback.incomingConnections);
-		s.close();
-		// Update the local properties with a new port number
-		localProperties.put("port", "0");
-		plugin.setLocalProperties(localProperties);
-		// The plugin should no longer be listening on the old port
-		try {
-			s = new Socket();
-			s.connect(addr, 100);
-			fail();
-		} catch(IOException expected) {}
-		// Find out what the new port is
-		portString = callback.localProperties.get("port");
-		assertNotNull(portString);
-		int newPort = Integer.valueOf(portString);
-		assertFalse(newPort == port);
-		// The plugin should be listening on the new port
-		addr = new InetSocketAddress(host, newPort);
-		assertEquals(1, callback.incomingConnections);
-		s = new Socket();
-		s.connect(addr, 100);
-		Thread.sleep(10);
-		assertEquals(2, callback.incomingConnections);
-		s.close();
-		// Stop the plugin
-		plugin.stop();
-		Thread.sleep(10);
-		// The plugin should no longer be listening
-		try {
-			s = new Socket();
-			s.connect(addr, 100);
-			fail();
-		} catch(IOException expected) {}
-	}
+	private static class StreamCallback implements StreamTransportCallback {
 
-	private static class StubCallback implements StreamTransportCallback {
+		private TransportConfig config = new TransportConfig();
+		private TransportProperties local = new TransportProperties();
+		private Map<ContactId, TransportProperties> remote =
+			new HashMap<ContactId, TransportProperties>();
 
-		public TransportProperties localProperties = null;
-		public volatile int incomingConnections = 0;
+		private int incomingConnections = 0;
 
-		public void setLocalProperties(TransportProperties properties) {
-			localProperties = properties;
+		public TransportConfig getConfig() {
+			return config;
 		}
 
-		public void setConfig(TransportConfig config) {
+		public TransportProperties getLocalProperties() {
+			return local;
 		}
 
-		public void showMessage(String... message) {
+		public Map<ContactId, TransportProperties> getRemoteProperties() {
+			return remote;
 		}
 
-		public boolean showConfirmationMessage(String... message) {
-			return false;
+		public void setConfig(TransportConfig c) {
+			config = c;
+		}
+
+		public void setLocalProperties(TransportProperties p) {
+			local = p;
 		}
 
-		public int showChoice(String[] choices, String... message) {
+		public int showChoice(String[] options, String... message) {
 			return -1;
 		}
 
+		public boolean showConfirmationMessage(String... message) {
+			return false;
+		}
+
+		public void showMessage(String... message) {}
+
 		public void incomingConnectionCreated(StreamTransportConnection c) {
 			incomingConnections++;
 		}