diff --git a/briar-api/src/net/sf/briar/api/plugins/duplex/DuplexPlugin.java b/briar-api/src/net/sf/briar/api/plugins/duplex/DuplexPlugin.java
index 8b7c48437993200753b0c18c4b5acb00e5efa02d..4120e25c41b7a7954f917ee7d30f510d36e1d115 100644
--- a/briar-api/src/net/sf/briar/api/plugins/duplex/DuplexPlugin.java
+++ b/briar-api/src/net/sf/briar/api/plugins/duplex/DuplexPlugin.java
@@ -18,14 +18,10 @@ public interface DuplexPlugin extends Plugin {
 	boolean supportsInvitations();
 
 	/**
-	 * Starts the invitation process from the inviter's side. Returns null if
-	 * no connection can be established within the given timeout.
+	 * Attempts to create and return an invitation connection to the remote
+	 * peer. Returns null if no connection can be established within the given
+	 * time.
 	 */
-	DuplexTransportConnection sendInvitation(PseudoRandom r, long timeout);
-
-	/**
-	 * Starts the invitation process from the invitee's side. Returns null if
-	 * no connection can be established within the given timeout.
-	 */
-	DuplexTransportConnection acceptInvitation(PseudoRandom r, long timeout);
+	DuplexTransportConnection createInvitationConnection(PseudoRandom r,
+			long timeout);
 }
diff --git a/briar-core/src/net/sf/briar/invitation/AliceConnector.java b/briar-core/src/net/sf/briar/invitation/AliceConnector.java
index b4e1b0cdf4ceb08c331148efe270c0e458cef6e6..84cab4286e94fd4dab35293bf08c9d5b4cce5bad 100644
--- a/briar-core/src/net/sf/briar/invitation/AliceConnector.java
+++ b/briar-core/src/net/sf/briar/invitation/AliceConnector.java
@@ -2,7 +2,6 @@ package net.sf.briar.invitation;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
-import static net.sf.briar.api.invitation.InvitationConstants.CONNECTION_TIMEOUT;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -57,13 +56,8 @@ class AliceConnector extends Connector {
 
 	@Override
 	public void run() {
-		// Try an outgoing connection first, then an incoming connection
-		long halfTime = clock.currentTimeMillis() + CONNECTION_TIMEOUT;
-		DuplexTransportConnection conn = makeOutgoingConnection();
-		if(conn == null) {
-			waitForHalfTime(halfTime);
-			conn = acceptIncomingConnection();
-		}
+		// Create an incoming or outgoing connection
+		DuplexTransportConnection conn = createInvitationConnection();
 		if(conn == null) return;
 		if(LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
 		// Don't proceed with more than one connection
diff --git a/briar-core/src/net/sf/briar/invitation/BobConnector.java b/briar-core/src/net/sf/briar/invitation/BobConnector.java
index 20bb1b71ee6d039c187f0b27538de5dde0344fc4..ae7c02144cbecb9d81fd1b5f8d3446c284ce8b5f 100644
--- a/briar-core/src/net/sf/briar/invitation/BobConnector.java
+++ b/briar-core/src/net/sf/briar/invitation/BobConnector.java
@@ -2,7 +2,6 @@ package net.sf.briar.invitation;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
-import static net.sf.briar.api.invitation.InvitationConstants.CONNECTION_TIMEOUT;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -57,13 +56,8 @@ class BobConnector extends Connector {
 
 	@Override
 	public void run() {
-		// Try an incoming connection first, then an outgoing connection
-		long halfTime = clock.currentTimeMillis() + CONNECTION_TIMEOUT;
-		DuplexTransportConnection conn = acceptIncomingConnection();
-		if(conn == null) {
-			waitForHalfTime(halfTime);
-			conn = makeOutgoingConnection();
-		}
+		// Create an incoming or outgoing connection
+		DuplexTransportConnection conn = createInvitationConnection();
 		if(conn == null) return;
 		if(LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
 		// Carry out the key agreement protocol
diff --git a/briar-core/src/net/sf/briar/invitation/Connector.java b/briar-core/src/net/sf/briar/invitation/Connector.java
index f0fd6b09559b8658e8d3303a7dfc057df730aa21..076f77cf304b84c629c4651e018e265a725be6d5 100644
--- a/briar-core/src/net/sf/briar/invitation/Connector.java
+++ b/briar-core/src/net/sf/briar/invitation/Connector.java
@@ -112,31 +112,10 @@ abstract class Connector extends Thread {
 		messageDigest = crypto.getMessageDigest();
 	}
 
-	protected DuplexTransportConnection acceptIncomingConnection() {
+	protected DuplexTransportConnection createInvitationConnection() {
 		if(LOG.isLoggable(INFO))
-			LOG.info(pluginName + " accepting incoming connection");
-		return plugin.acceptInvitation(random, CONNECTION_TIMEOUT);
-	}
-
-	protected DuplexTransportConnection makeOutgoingConnection() {
-		if(LOG.isLoggable(INFO))
-			LOG.info(pluginName + " making outgoing connection");
-		return plugin.sendInvitation(random, CONNECTION_TIMEOUT);
-	}
-
-	protected void waitForHalfTime(long halfTime) {
-		long now = clock.currentTimeMillis();
-		if(now < halfTime) {
-			if(LOG.isLoggable(INFO))
-				LOG.info(pluginName + " sleeping until half-time");
-			try {
-				clock.sleep(halfTime - now);
-			} catch(InterruptedException e) {
-				if(LOG.isLoggable(INFO)) LOG.info("Interrupted while sleeping");
-				Thread.currentThread().interrupt();
-				return;
-			}
-		}
+			LOG.info(pluginName + " creating invitation connection");
+		return plugin.createInvitationConnection(random, CONNECTION_TIMEOUT);
 	}
 
 	protected void sendPublicKeyHash(Writer w) throws IOException {
diff --git a/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java b/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
index 4036e10d871daaecd195be6f54d0c39cc8071ba4..f522fea0b2ac40941b93f078349654fc5d1a7fc9 100644
--- a/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
@@ -166,13 +166,15 @@ class BluetoothPlugin implements DuplexPlugin {
 				tryToClose(ss);
 				return;
 			}
-			BluetoothTransportConnection conn =
-					new BluetoothTransportConnection(this, s);
-			callback.incomingConnectionCreated(conn);
+			callback.incomingConnectionCreated(wrapSocket(s));
 			if(!running) return;
 		}
 	}
 
+	private DuplexTransportConnection wrapSocket(StreamConnection s) {
+		return new BluetoothTransportConnection(this, s);
+	}
+
 	public void stop() {
 		running = false;
 		tryToClose(socket);
@@ -202,11 +204,8 @@ class BluetoothPlugin implements DuplexPlugin {
 				public void run() {
 					if(!running) return;
 					StreamConnection s = connect(makeUrl(address, uuid));
-					if(s != null) {
-						callback.outgoingConnectionCreated(c,
-								new BluetoothTransportConnection(
-										BluetoothPlugin.this, s));
-					}
+					if(s != null)
+						callback.outgoingConnectionCreated(c, wrapSocket(s));
 				}
 			});
 		}
@@ -239,7 +238,7 @@ class BluetoothPlugin implements DuplexPlugin {
 		return true;
 	}
 
-	public DuplexTransportConnection sendInvitation(PseudoRandom r,
+	public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
 			long timeout) {
 		if(!running) return null;
 		// Use the invitation codes to generate the UUID
@@ -282,7 +281,6 @@ class BluetoothPlugin implements DuplexPlugin {
 
 	private void makeDeviceDiscoverable() {
 		// Try to make the device discoverable (requires root on Linux)
-		if(!running) return;
 		try {
 			localDevice.setDiscoverable(GIAC);
 		} catch(BluetoothStateException e) {
@@ -290,12 +288,6 @@ class BluetoothPlugin implements DuplexPlugin {
 		}
 	}
 
-	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
-			long timeout) {
-		// FIXME
-		return sendInvitation(r, timeout);
-	}
-
 	private class DiscoveryThread extends Thread {
 
 		private final LatchedReference<StreamConnection> socketLatch;
@@ -325,18 +317,16 @@ class BluetoothPlugin implements DuplexPlugin {
 							new InvitationListener(discoveryAgent, uuid);
 					discoveryAgent.startInquiry(GIAC, listener);
 					String url = listener.waitForUrl();
-					if(url != null) {
-						StreamConnection s = connect(url);
-						if(s == null) return;
+					if(url == null) continue;
+					StreamConnection s = connect(url);
+					if(s == null) continue;
+					if(LOG.isLoggable(INFO)) LOG.info("Outgoing connection");
+					if(!socketLatch.set(s)) {
 						if(LOG.isLoggable(INFO))
-							LOG.info("Outgoing connection");
-						if(!socketLatch.set(s)) {
-							if(LOG.isLoggable(INFO))
-								LOG.info("Closing redundant connection");
-							tryToClose(s);
-						}
-						return;
+							LOG.info("Closing redundant connection");
+						tryToClose(s);
 					}
+					return;
 				} catch(BluetoothStateException e) {
 					if(LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
diff --git a/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java b/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java
index 12c9aab68dec78de71579f67652c330940b682f8..407daedd68a1c9a13d065705cdc144583b634072 100644
--- a/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java
@@ -217,13 +217,15 @@ class DroidtoothPlugin implements DuplexPlugin {
 				tryToClose(ss);
 				return;
 			}
-			DroidtoothTransportConnection conn =
-					new DroidtoothTransportConnection(this, s);
-			callback.incomingConnectionCreated(conn);
+			callback.incomingConnectionCreated(wrapSocket(s));
 			if(!running) return;
 		}
 	}
 
+	private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
+		return new DroidtoothTransportConnection(this, s);
+	}
+
 	public void stop() {
 		running = false;
 		// Disable Bluetooth if we enabled it at startup
@@ -277,11 +279,8 @@ class DroidtoothPlugin implements DuplexPlugin {
 				public void run() {
 					if(!running) return;
 					BluetoothSocket s = connect(address, uuid);
-					if(s != null) {
-						callback.outgoingConnectionCreated(c,
-								new DroidtoothTransportConnection(
-										DroidtoothPlugin.this, s));
-					}
+					if(s != null)
+						callback.outgoingConnectionCreated(c, wrapSocket(s));
 				}
 			});
 		}
@@ -343,7 +342,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 		return true;
 	}
 
-	public DuplexTransportConnection sendInvitation(PseudoRandom r,
+	public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
 			long timeout) {
 		if(!running) return null;
 		// Use the invitation codes to generate the UUID
@@ -379,12 +378,6 @@ class DroidtoothPlugin implements DuplexPlugin {
 		return null;
 	}
 
-	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
-			long timeout) {
-		// FIXME
-		return sendInvitation(r, timeout);
-	}
-
 	private static class BluetoothStateReceiver extends BroadcastReceiver {
 
 		private final CountDownLatch finished = new CountDownLatch(1);
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 893205225cfac755b6e94929a7c1ee6c83a5247c..ff9025f66ae93788a4816fe33440d4fcabd721aa 100644
--- a/briar-core/src/net/sf/briar/plugins/modem/ModemPlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/modem/ModemPlugin.java
@@ -219,12 +219,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 		return false;
 	}
 
-	public DuplexTransportConnection sendInvitation(PseudoRandom r,
-			long timeout) {
-		throw new UnsupportedOperationException();
-	}
-
-	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
+	public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
 			long timeout) {
 		throw new UnsupportedOperationException();
 	}
diff --git a/briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java b/briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java
index 067984e1dfe4459c6b71e0f003ac7f2c7a7f8df4..7315dab01a2ff11f19016a459ded150ce6c24d4f 100644
--- a/briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java
@@ -25,7 +25,7 @@ class DroidLanTcpPlugin extends LanTcpPlugin {
 	}
 
 	@Override
-	public DuplexTransportConnection sendInvitation(PseudoRandom r,
+	public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
 			long timeout) {
 		WifiManager wifi =
 				(WifiManager) appContext.getSystemService(WIFI_SERVICE);
@@ -33,22 +33,7 @@ class DroidLanTcpPlugin extends LanTcpPlugin {
 		MulticastLock lock = wifi.createMulticastLock("invitation");
 		lock.acquire();
 		try {
-			return super.sendInvitation(r, timeout);
-		} finally {
-			lock.release();
-		}
-	}
-
-	@Override
-	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
-			long timeout) {
-		WifiManager wifi =
-				(WifiManager) appContext.getSystemService(WIFI_SERVICE);
-		if(wifi == null || !wifi.isWifiEnabled()) return null;
-		MulticastLock lock = wifi.createMulticastLock("invitation");
-		lock.acquire();
-		try {
-			return super.acceptInvitation(r, timeout);
+			return super.createInvitationConnection(r, timeout);
 		} finally {
 			lock.release();
 		}
diff --git a/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java b/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
index dff58aa677af1234b4ec6b0d34968f373182d796..a01a5f4344c89128d658d5b7ad5e49cefa40a238 100644
--- a/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
@@ -119,7 +119,7 @@ class LanTcpPlugin extends TcpPlugin {
 		return true;
 	}
 
-	public DuplexTransportConnection sendInvitation(PseudoRandom r,
+	public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
 			long timeout) {
 		if(!running) return null;
 		// Use the invitation codes to generate the group address and port
@@ -244,12 +244,6 @@ class LanTcpPlugin extends TcpPlugin {
 		ms.close();
 	}
 
-	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
-			long timeout) {
-		// FIXME
-		return sendInvitation(r, timeout);
-	}
-
 	private class MulticastListenerThread extends Thread {
 
 		private final LatchedReference<Socket> socketLatch;
diff --git a/briar-core/src/net/sf/briar/plugins/tcp/WanTcpPlugin.java b/briar-core/src/net/sf/briar/plugins/tcp/WanTcpPlugin.java
index 770f2f7a5682350812c22ec3520451d46818bbce..45e145e5f47da7f2854ffa0a96978eff80b0cd01 100644
--- a/briar-core/src/net/sf/briar/plugins/tcp/WanTcpPlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/tcp/WanTcpPlugin.java
@@ -125,12 +125,7 @@ class WanTcpPlugin extends TcpPlugin {
 		return false;
 	}
 
-	public DuplexTransportConnection sendInvitation(PseudoRandom r,
-			long timeout) {
-		throw new UnsupportedOperationException();
-	}
-
-	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
+	public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
 			long timeout) {
 		throw new UnsupportedOperationException();
 	}
diff --git a/briar-core/src/net/sf/briar/plugins/tor/TorPlugin.java b/briar-core/src/net/sf/briar/plugins/tor/TorPlugin.java
index 2b1eacee345ad85d48b206f335b01e4197be734d..d169b3a6da76fba60087b5a886cc99c576e51163 100644
--- a/briar-core/src/net/sf/briar/plugins/tor/TorPlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/tor/TorPlugin.java
@@ -513,12 +513,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
 		return false;
 	}
 
-	public DuplexTransportConnection sendInvitation(PseudoRandom r,
-			long timeout) {
-		throw new UnsupportedOperationException();
-	}
-
-	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
+	public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
 			long timeout) {
 		throw new UnsupportedOperationException();
 	}
diff --git a/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java b/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java
index d00d83906602ddfc73e680b5a0a81e294565efc5..35269a94e07ee2b435bb8a59237efe0249b1231d 100644
--- a/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java
@@ -8,6 +8,7 @@ import java.util.Map;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportProperties;
+import net.sf.briar.api.crypto.PseudoRandom;
 import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
 import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
 
@@ -35,23 +36,13 @@ public abstract class DuplexClientTest extends DuplexTest {
 				receiveChallengeSendResponse(d);
 			}
 			if(!plugin.supportsInvitations()) {
-				System.out.println("Skipping invitation tests");
+				System.out.println("Skipping invitation test");
 				return;
 			}
-			// Try to send an invitation
-			System.out.println("Sending invitation");
-			d = plugin.sendInvitation(getPseudoRandom(123), CONNECTION_TIMEOUT);
-			if(d == null) {
-				System.out.println("Connection failed");
-				return;
-			} else {
-				System.out.println("Connection created");
-				receiveChallengeSendResponse(d);
-			}
-			// Try to accept an invitation
-			System.out.println("Accepting invitation");
-			d = plugin.acceptInvitation(getPseudoRandom(456),
-					CONNECTION_TIMEOUT);
+			// Try to create an invitation connection
+			System.out.println("Creating invitation connection");
+			PseudoRandom r = getPseudoRandom(123);
+			d = plugin.createInvitationConnection(r, CONNECTION_TIMEOUT);
 			if(d == null) {
 				System.out.println("Connection failed");
 				return;
diff --git a/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java b/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java
index f6bfc4b19c3f7878a772edda8839905cd2cc40ce..5a3d0b583f302e95263ebd2be5df7fd155d5a1c5 100644
--- a/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java
@@ -33,23 +33,13 @@ public abstract class DuplexServerTest extends DuplexTest {
 				return;
 			}
 			if(!plugin.supportsInvitations()) {
-				System.out.println("Skipping invitation tests");
+				System.out.println("Skipping invitation test");
 				return;
 			}
-			// Try to accept an invitation
-			System.out.println("Accepting invitation");
-			DuplexTransportConnection d = plugin.acceptInvitation(
+			// Try to create an invitation connection 
+			System.out.println("Creating invitation connection");
+			DuplexTransportConnection d = plugin.createInvitationConnection(
 					getPseudoRandom(123), CONNECTION_TIMEOUT);
-			if(d == null) {
-				System.out.println("Connection failed");
-				return;
-			} else {
-				System.out.println("Connection created");
-				sendChallengeReceiveResponse(d);
-			}
-			// Try to send an invitation
-			System.out.println("Sending invitation");
-			d = plugin.sendInvitation(getPseudoRandom(456), CONNECTION_TIMEOUT);
 			if(d == null) {
 				System.out.println("Connection failed");
 				return;