diff --git a/briar-android/res/values/roboguice.xml b/briar-android/res/values/roboguice.xml
index 0f6ffcd915b60127967ef02d69fd86bc8c3110db..32110839f6c3ba3da878e67e248b2199e2918b55 100644
--- a/briar-android/res/values/roboguice.xml
+++ b/briar-android/res/values/roboguice.xml
@@ -10,7 +10,6 @@
 		<item>org.briarproject.messaging.duplex.DuplexMessagingModule</item>
 		<item>org.briarproject.messaging.simplex.SimplexMessagingModule</item>
 		<item>org.briarproject.plugins.AndroidPluginsModule</item>
-		<item>org.briarproject.plugins.PluginsModule</item>
 		<item>org.briarproject.serial.SerialModule</item>
 		<item>org.briarproject.system.AndroidSystemModule</item>
 		<item>org.briarproject.transport.TransportModule</item>
diff --git a/briar-android/src/org/briarproject/android/SplashScreenActivity.java b/briar-android/src/org/briarproject/android/SplashScreenActivity.java
index ff48381cd3161747087e41f9eedacce3069f997a..c0b7a15146caa7a7fab23caabfd13298378f69f1 100644
--- a/briar-android/src/org/briarproject/android/SplashScreenActivity.java
+++ b/briar-android/src/org/briarproject/android/SplashScreenActivity.java
@@ -30,8 +30,8 @@ public class SplashScreenActivity extends RoboSplashActivity {
 	private static final Logger LOG =
 			Logger.getLogger(SplashScreenActivity.class.getName());
 
-	// This build expires on 12 July 2014
-	private static final long EXPIRY_DATE = 1405123200 * 1000L;
+	// This build expires on 8 October 2014
+	private static final long EXPIRY_DATE = 1412726400 * 1000L;
 
 	private long now = System.currentTimeMillis();
 
diff --git a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java
index 2228b8b5b18c1155a0e4e583a7890149ff401a70..ce9b724b1f8958b0713f24803f7fb6ecedefaa08 100644
--- a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java
+++ b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java
@@ -7,8 +7,8 @@ import java.util.concurrent.Executor;
 
 import org.briarproject.api.android.AndroidExecutor;
 import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.lifecycle.IoExecutor;
 import org.briarproject.api.lifecycle.ShutdownManager;
-import org.briarproject.api.plugins.PluginExecutor;
 import org.briarproject.api.plugins.duplex.DuplexPluginConfig;
 import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
 import org.briarproject.api.plugins.simplex.SimplexPluginConfig;
@@ -21,12 +21,9 @@ import org.briarproject.plugins.tor.TorPluginFactory;
 import android.app.Application;
 import android.content.Context;
 
-import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 
-public class AndroidPluginsModule extends AbstractModule {
-
-	protected void configure() {}
+public class AndroidPluginsModule extends PluginsModule {
 
 	@Provides
 	SimplexPluginConfig getSimplexPluginConfig() {
@@ -38,19 +35,17 @@ public class AndroidPluginsModule extends AbstractModule {
 	}
 
 	@Provides
-	DuplexPluginConfig getDuplexPluginConfig(
-			@PluginExecutor Executor pluginExecutor,
+	DuplexPluginConfig getDuplexPluginConfig(@IoExecutor Executor ioExecutor,
 			AndroidExecutor androidExecutor, Application app,
 			CryptoComponent crypto, LocationUtils locationUtils,
 			ShutdownManager shutdownManager) {
 		Context appContext = app.getApplicationContext();
-		DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(
-				pluginExecutor, androidExecutor, appContext,
-				crypto.getSecureRandom());
-		DuplexPluginFactory tor = new TorPluginFactory(pluginExecutor,
-				appContext, locationUtils, shutdownManager);
-		DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(
-				pluginExecutor, appContext);
+		DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
+				androidExecutor, appContext, crypto.getSecureRandom());
+		DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
+				locationUtils, shutdownManager);
+		DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
+				appContext);
 		final Collection<DuplexPluginFactory> factories =
 				Arrays.asList(bluetooth, tor, lan);
 		return new DuplexPluginConfig() {
diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java
index aaa38352b64f9c5fab29837543d98468ade58897..1b65e45c8717b24e39f9f41baffe93a2614cd6d0 100644
--- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java
+++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java
@@ -63,7 +63,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 	private static final String DISCOVERY_FINISHED =
 			"android.bluetooth.adapter.action.DISCOVERY_FINISHED";
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final AndroidExecutor androidExecutor;
 	private final Context appContext;
 	private final SecureRandom secureRandom;
@@ -80,11 +80,11 @@ class DroidtoothPlugin implements DuplexPlugin {
 	// Non-null if the plugin started successfully
 	private volatile BluetoothAdapter adapter = null;
 
-	DroidtoothPlugin(Executor pluginExecutor, AndroidExecutor androidExecutor,
+	DroidtoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor,
 			Context appContext, SecureRandom secureRandom, Clock clock,
 			DuplexPluginCallback callback, int maxFrameLength, long maxLatency,
 			long pollingInterval) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.androidExecutor = androidExecutor;
 		this.appContext = appContext;
 		this.secureRandom = secureRandom;
@@ -147,7 +147,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 	}
 
 	private void bind() {
-		pluginExecutor.execute(new Runnable() {
+		ioExecutor.execute(new Runnable() {
 			public void run() {
 				if(!isRunning()) return;
 				if(LOG.isLoggable(INFO))
@@ -256,7 +256,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 			if(StringUtils.isNullOrEmpty(address)) continue;
 			final String uuid = e.getValue().get("uuid");
 			if(StringUtils.isNullOrEmpty(uuid)) continue;
-			pluginExecutor.execute(new Runnable() {
+			ioExecutor.execute(new Runnable() {
 				public void run() {
 					if(!running) return;
 					BluetoothSocket s = connect(address, uuid);
diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java
index 4015185fa300dd6056bde1bf91f1dcd32cbd580b..b7bb2028546aaa3dc508cffdbde4da0ab9bb3835 100644
--- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java
+++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java
@@ -19,16 +19,16 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
 	private static final long MAX_LATENCY = 60 * 1000; // 1 minute
 	private static final long POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final AndroidExecutor androidExecutor;
 	private final Context appContext;
 	private final SecureRandom secureRandom;
 	private final Clock clock;
 
-	public DroidtoothPluginFactory(Executor pluginExecutor,
+	public DroidtoothPluginFactory(Executor ioExecutor,
 			AndroidExecutor androidExecutor, Context appContext,
 			SecureRandom secureRandom) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.androidExecutor = androidExecutor;
 		this.appContext = appContext;
 		this.secureRandom = secureRandom;
@@ -40,7 +40,7 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
 	}
 
 	public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
-		return new DroidtoothPlugin(pluginExecutor, androidExecutor, appContext,
+		return new DroidtoothPlugin(ioExecutor, androidExecutor, appContext,
 				secureRandom, clock, callback, MAX_FRAME_LENGTH, MAX_LATENCY,
 				POLLING_INTERVAL);
 	}
diff --git a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java
index 5092d4537744f0157d2709349ea881eca553ac9b..eaec50c41c0873622e5ae4da61fac973c90132e3 100644
--- a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java
+++ b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java
@@ -25,10 +25,10 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
 
 	private volatile BroadcastReceiver networkStateReceiver = null;
 
-	AndroidLanTcpPlugin(Executor pluginExecutor, Context appContext,
+	AndroidLanTcpPlugin(Executor ioExecutor, Context appContext,
 			DuplexPluginCallback callback, int maxFrameLength, long maxLatency,
 			long pollingInterval) {
-		super(pluginExecutor, callback, maxFrameLength, maxLatency,
+		super(ioExecutor, callback, maxFrameLength, maxLatency,
 				pollingInterval);
 		this.appContext = appContext;
 	}
diff --git a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java
index dec3bf711e0f03b0773d45c15fcc6a0f5676d73b..a51326e55bb441263c69f2191935408d5b1b621f 100644
--- a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java
+++ b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java
@@ -15,12 +15,11 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
 	private static final long MAX_LATENCY = 60 * 1000; // 1 minute
 	private static final long POLLING_INTERVAL = 60 * 1000; // 1 minute
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final Context appContext;
 
-	public AndroidLanTcpPluginFactory(Executor pluginExecutor,
-			Context appContext) {
-		this.pluginExecutor = pluginExecutor;
+	public AndroidLanTcpPluginFactory(Executor ioExecutor, Context appContext) {
+		this.ioExecutor = ioExecutor;
 		this.appContext = appContext;
 	}
 
@@ -29,7 +28,7 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
 	}
 
 	public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
-		return new AndroidLanTcpPlugin(pluginExecutor, appContext, callback,
+		return new AndroidLanTcpPlugin(ioExecutor, appContext, callback,
 				MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL);
 	}
 }
diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
index 7ac2f539467c6dde55df7f519097ab9cb8902c13..207f0935dae1c85470c8392fa302d2bdf4a42667 100644
--- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
+++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
@@ -72,7 +72,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
 	private static final Logger LOG =
 			Logger.getLogger(TorPlugin.class.getName());
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final Context appContext;
 	private final LocationUtils locationUtils;
 	private final ShutdownManager shutdownManager;
@@ -92,11 +92,11 @@ class TorPlugin implements DuplexPlugin, EventHandler {
 	private volatile TorControlConnection controlConnection = null;
 	private volatile BroadcastReceiver networkStateReceiver = null;
 
-	TorPlugin(Executor pluginExecutor, Context appContext,
+	TorPlugin(Executor ioExecutor, Context appContext,
 			LocationUtils locationUtils, ShutdownManager shutdownManager,
 			DuplexPluginCallback callback, int maxFrameLength, long maxLatency,
 			long pollingInterval) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.appContext = appContext;
 		this.locationUtils = locationUtils;
 		this.shutdownManager = shutdownManager;
@@ -448,7 +448,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
 	}
 
 	private void bind() {
-		pluginExecutor.execute(new Runnable() {
+		ioExecutor.execute(new Runnable() {
 			public void run() {
 				// If there's already a port number stored in config, reuse it
 				String portString = callback.getConfig().get("port");
@@ -476,7 +476,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
 				c.put("port", localPort);
 				callback.mergeConfig(c);
 				// Create a hidden service if necessary
-				pluginExecutor.execute(new Runnable() {
+				ioExecutor.execute(new Runnable() {
 					public void run() {
 						publishHiddenService(localPort);
 					}
@@ -605,7 +605,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
 	}
 
 	private void connectAndCallBack(final ContactId c) {
-		pluginExecutor.execute(new Runnable() {
+		ioExecutor.execute(new Runnable() {
 			public void run() {
 				DuplexTransportConnection d = createConnection(c);
 				if(d != null) callback.outgoingConnectionCreated(c, d);
diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java
index 0990b6acabbe7f2f3578063c2900cb3bca8c6054..d8947dca909f84e2044b9655e6fbdb9a1823eedb 100644
--- a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java
+++ b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java
@@ -22,14 +22,14 @@ public class TorPluginFactory implements DuplexPluginFactory {
 	private static final long MAX_LATENCY = 60 * 1000; // 1 minute
 	private static final long POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final Context appContext;
 	private final LocationUtils locationUtils;
 	private final ShutdownManager shutdownManager;
 
-	public TorPluginFactory(Executor pluginExecutor, Context appContext,
+	public TorPluginFactory(Executor ioExecutor, Context appContext,
 			LocationUtils locationUtils, ShutdownManager shutdownManager) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.appContext = appContext;
 		this.locationUtils = locationUtils;
 		this.shutdownManager = shutdownManager;
@@ -45,7 +45,7 @@ public class TorPluginFactory implements DuplexPluginFactory {
 			LOG.info("Tor is not supported on this architecture");
 			return null;
 		}
-		return new TorPlugin(pluginExecutor,appContext, locationUtils,
+		return new TorPlugin(ioExecutor,appContext, locationUtils,
 				shutdownManager, callback, MAX_FRAME_LENGTH, MAX_LATENCY,
 				POLLING_INTERVAL);
 	}
diff --git a/briar-api/src/org/briarproject/api/plugins/PluginExecutor.java b/briar-api/src/org/briarproject/api/lifecycle/IoExecutor.java
similarity index 74%
rename from briar-api/src/org/briarproject/api/plugins/PluginExecutor.java
rename to briar-api/src/org/briarproject/api/lifecycle/IoExecutor.java
index fb0892ebf39b65ff8bc33de90a45c6047672f3ba..01e0ea49959963d8a85b0f07a0301572faa88b96 100644
--- a/briar-api/src/org/briarproject/api/plugins/PluginExecutor.java
+++ b/briar-api/src/org/briarproject/api/lifecycle/IoExecutor.java
@@ -1,4 +1,4 @@
-package org.briarproject.api.plugins;
+package org.briarproject.api.lifecycle;
 
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
@@ -10,8 +10,8 @@ import java.lang.annotation.Target;
 
 import com.google.inject.BindingAnnotation;
 
-/** Annotation for injecting the executor used by transport plugins. */
+/** Annotation for injecting the executor used by long-lived IO tasks. */
 @BindingAnnotation
 @Target({ FIELD, METHOD, PARAMETER })
 @Retention(RUNTIME)
-public @interface PluginExecutor {}
\ No newline at end of file
+public @interface IoExecutor {}
\ No newline at end of file
diff --git a/briar-api/src/org/briarproject/api/transport/IncomingConnectionExecutor.java b/briar-api/src/org/briarproject/api/transport/IncomingConnectionExecutor.java
deleted file mode 100644
index d70554de648e4c276f4464ee9d88da8c8dad2e81..0000000000000000000000000000000000000000
--- a/briar-api/src/org/briarproject/api/transport/IncomingConnectionExecutor.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.briarproject.api.transport;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import com.google.inject.BindingAnnotation;
-
-/**
- * Annotation for injecting the executor for recognising incoming connections.
- */
-@BindingAnnotation
-@Target({ FIELD, METHOD, PARAMETER })
-@Retention(RUNTIME)
-public @interface IncomingConnectionExecutor {}
\ No newline at end of file
diff --git a/briar-core/src/org/briarproject/lifecycle/LifecycleModule.java b/briar-core/src/org/briarproject/lifecycle/LifecycleModule.java
index 8c6c7360f218b4c4d5478cd43615831c711b7620..0714a0555e4082817fe08b82bc462036616c69f7 100644
--- a/briar-core/src/org/briarproject/lifecycle/LifecycleModule.java
+++ b/briar-core/src/org/briarproject/lifecycle/LifecycleModule.java
@@ -1,18 +1,49 @@
 package org.briarproject.lifecycle;
 
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+
 import javax.inject.Singleton;
 
+import org.briarproject.api.lifecycle.IoExecutor;
 import org.briarproject.api.lifecycle.LifecycleManager;
 import org.briarproject.api.lifecycle.ShutdownManager;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
 
 public class LifecycleModule extends AbstractModule {
 
+	private final ExecutorService ioExecutor;
+
+	public LifecycleModule() {
+		// The thread pool is unbounded, so use direct handoff
+		BlockingQueue<Runnable> queue = new SynchronousQueue<Runnable>();
+		// Discard tasks that are submitted during shutdown
+		RejectedExecutionHandler policy =
+				new ThreadPoolExecutor.DiscardPolicy();
+		// Create threads as required and keep them in the pool for 60 seconds
+		ioExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
+				60, SECONDS, queue, policy);
+	}
+
+	@Override
 	protected void configure() {
 		bind(LifecycleManager.class).to(
 				LifecycleManagerImpl.class).in(Singleton.class);
 		bind(ShutdownManager.class).to(
 				ShutdownManagerImpl.class).in(Singleton.class);
 	}
+
+	@Provides @Singleton @IoExecutor
+	Executor getIoExecutor(LifecycleManager lifecycleManager) {
+		lifecycleManager.registerForShutdown(ioExecutor);
+		return ioExecutor;
+	}
 }
diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java
index 3cdfbe9a9cb745e25a8ca9ea56b8442389f40d54..722897476e04e8b08f50421f4f9e3156b70d942d 100644
--- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java
+++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java
@@ -23,9 +23,9 @@ import org.briarproject.api.TransportId;
 import org.briarproject.api.TransportProperties;
 import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
+import org.briarproject.api.lifecycle.IoExecutor;
 import org.briarproject.api.plugins.Plugin;
 import org.briarproject.api.plugins.PluginCallback;
-import org.briarproject.api.plugins.PluginExecutor;
 import org.briarproject.api.plugins.PluginManager;
 import org.briarproject.api.plugins.duplex.DuplexPlugin;
 import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
@@ -49,7 +49,7 @@ class PluginManagerImpl implements PluginManager {
 	private static final Logger LOG =
 			Logger.getLogger(PluginManagerImpl.class.getName());
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final SimplexPluginConfig simplexPluginConfig;
 	private final DuplexPluginConfig duplexPluginConfig;
 	private final Clock clock;
@@ -62,12 +62,12 @@ class PluginManagerImpl implements PluginManager {
 	private final List<DuplexPlugin> duplexPlugins;
 
 	@Inject
-	PluginManagerImpl(@PluginExecutor Executor pluginExecutor,
+	PluginManagerImpl(@IoExecutor Executor ioExecutor,
 			SimplexPluginConfig simplexPluginConfig, 
 			DuplexPluginConfig duplexPluginConfig, Clock clock,
 			DatabaseComponent db, Poller poller,
 			ConnectionDispatcher dispatcher, UiCallback uiCallback) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.simplexPluginConfig = simplexPluginConfig;
 		this.duplexPluginConfig = duplexPluginConfig;
 		this.clock = clock;
@@ -87,14 +87,14 @@ class PluginManagerImpl implements PluginManager {
 				simplexPluginConfig.getFactories();
 		final CountDownLatch sLatch = new CountDownLatch(sFactories.size());
 		for(SimplexPluginFactory factory : sFactories)
-			pluginExecutor.execute(new SimplexPluginStarter(factory, sLatch));
+			ioExecutor.execute(new SimplexPluginStarter(factory, sLatch));
 		// Instantiate and start the duplex plugins
 		LOG.info("Starting duplex plugins");
 		Collection<DuplexPluginFactory> dFactories =
 				duplexPluginConfig.getFactories();
 		final CountDownLatch dLatch = new CountDownLatch(dFactories.size());
 		for(DuplexPluginFactory factory : dFactories)
-			pluginExecutor.execute(new DuplexPluginStarter(factory, dLatch));
+			ioExecutor.execute(new DuplexPluginStarter(factory, dLatch));
 		// Wait for the plugins to start
 		try {
 			sLatch.await();
@@ -119,11 +119,11 @@ class PluginManagerImpl implements PluginManager {
 		// Stop the simplex plugins
 		LOG.info("Stopping simplex plugins");
 		for(SimplexPlugin plugin : simplexPlugins)
-			pluginExecutor.execute(new PluginStopper(plugin, latch));
+			ioExecutor.execute(new PluginStopper(plugin, latch));
 		// Stop the duplex plugins
 		LOG.info("Stopping duplex plugins");
 		for(DuplexPlugin plugin : duplexPlugins)
-			pluginExecutor.execute(new PluginStopper(plugin, latch));
+			ioExecutor.execute(new PluginStopper(plugin, latch));
 		plugins.clear();
 		simplexPlugins.clear();
 		duplexPlugins.clear();
diff --git a/briar-core/src/org/briarproject/plugins/PluginsModule.java b/briar-core/src/org/briarproject/plugins/PluginsModule.java
index f905d2c324fcab0a3ddcbd3fd9a7c4bbc34b0987..aca1ef52c17b1bc3129d2bf3ac5f650c81b9443a 100644
--- a/briar-core/src/org/briarproject/plugins/PluginsModule.java
+++ b/briar-core/src/org/briarproject/plugins/PluginsModule.java
@@ -1,18 +1,8 @@
 package org.briarproject.plugins;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-
 import javax.inject.Singleton;
 
 import org.briarproject.api.lifecycle.LifecycleManager;
-import org.briarproject.api.plugins.PluginExecutor;
 import org.briarproject.api.plugins.PluginManager;
 
 import com.google.inject.AbstractModule;
@@ -20,19 +10,7 @@ import com.google.inject.Provides;
 
 public class PluginsModule extends AbstractModule {
 
-	private final ExecutorService pluginExecutor;
-
-	public PluginsModule() {
-		// The thread pool is unbounded, so use direct handoff
-		BlockingQueue<Runnable> queue = new SynchronousQueue<Runnable>();
-		// Discard tasks that are submitted during shutdown
-		RejectedExecutionHandler policy =
-				new ThreadPoolExecutor.DiscardPolicy();
-		// Create threads as required and keep them in the pool for 60 seconds
-		pluginExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
-				60, SECONDS, queue, policy);
-	}
-
+	@Override
 	protected void configure() {
 		bind(Poller.class).to(PollerImpl.class);
 	}
@@ -43,10 +21,4 @@ public class PluginsModule extends AbstractModule {
 		lifecycleManager.register(pluginManager);
 		return pluginManager;
 	}
-
-	@Provides @Singleton @PluginExecutor
-	Executor getPluginExecutor(LifecycleManager lifecycleManager) {
-		lifecycleManager.registerForShutdown(pluginExecutor);
-		return pluginExecutor;
-	}
 }
diff --git a/briar-core/src/org/briarproject/plugins/PollerImpl.java b/briar-core/src/org/briarproject/plugins/PollerImpl.java
index 6d331877b4a7764ecf87e8307ff9b079c963ffa1..a92cdf4cd85c57ede04f0b9ed3dd0c998382d2fb 100644
--- a/briar-core/src/org/briarproject/plugins/PollerImpl.java
+++ b/briar-core/src/org/briarproject/plugins/PollerImpl.java
@@ -9,8 +9,8 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
+import org.briarproject.api.lifecycle.IoExecutor;
 import org.briarproject.api.plugins.Plugin;
-import org.briarproject.api.plugins.PluginExecutor;
 import org.briarproject.api.system.Timer;
 import org.briarproject.api.transport.ConnectionRegistry;
 
@@ -19,14 +19,14 @@ class PollerImpl implements Poller {
 	private static final Logger LOG =
 			Logger.getLogger(PollerImpl.class.getName());
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final ConnectionRegistry connRegistry;
 	private final Timer timer;
 
 	@Inject
-	PollerImpl(@PluginExecutor Executor pluginExecutor,
-			ConnectionRegistry connRegistry, Timer timer) {
-		this.pluginExecutor = pluginExecutor;
+	PollerImpl(@IoExecutor Executor ioExecutor, ConnectionRegistry connRegistry,
+			Timer timer) {
+		this.ioExecutor = ioExecutor;
 		this.connRegistry = connRegistry;
 		this.timer = timer;
 	}
@@ -49,7 +49,7 @@ class PollerImpl implements Poller {
 	}
 
 	public void pollNow(final Plugin p) {
-		pluginExecutor.execute(new Runnable() {
+		ioExecutor.execute(new Runnable() {
 			public void run() {
 				if(LOG.isLoggable(INFO))
 					LOG.info("Polling " + p.getClass().getSimpleName());
@@ -66,6 +66,7 @@ class PollerImpl implements Poller {
 			this.plugin = plugin;
 		}
 
+		@Override
 		public void run() {
 			pollNow(plugin);
 			schedule(plugin, false);
diff --git a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java
index 3e5854524caabfa87a12d93a8ac3cf99cb1c820a..60628f33e066861fe4d23f7ac42b772fd1e6570d 100644
--- a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java
+++ b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java
@@ -25,7 +25,7 @@ public abstract class FilePlugin implements SimplexPlugin {
 	private static final Logger LOG =
 			Logger.getLogger(FilePlugin.class.getName());
 
-	protected final Executor pluginExecutor;
+	protected final Executor ioExecutor;
 	protected final FileUtils fileUtils;
 	protected final SimplexPluginCallback callback;
 	protected final int maxFrameLength;
@@ -38,10 +38,10 @@ public abstract class FilePlugin implements SimplexPlugin {
 	protected abstract void writerFinished(File f);
 	protected abstract void readerFinished(File f);
 
-	protected FilePlugin(Executor pluginExecutor, FileUtils fileUtils,
+	protected FilePlugin(Executor ioExecutor, FileUtils fileUtils,
 			SimplexPluginCallback callback, int maxFrameLength,
 			long maxLatency) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.fileUtils = fileUtils;
 		this.callback = callback;
 		this.maxFrameLength = maxFrameLength;
@@ -100,7 +100,7 @@ public abstract class FilePlugin implements SimplexPlugin {
 
 	protected void createReaderFromFile(final File f) {
 		if(!running) return;
-		pluginExecutor.execute(new ReaderCreator(f));
+		ioExecutor.execute(new ReaderCreator(f));
 	}
 
 	private class ReaderCreator implements Runnable {
diff --git a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java
index d4d3c506246aa67e55fdc6f237d504bbe1b952e4..39e4b94184f48dce3b6c073903713b7a59f34aba 100644
--- a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java
+++ b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java
@@ -16,9 +16,9 @@ class LanTcpPlugin extends TcpPlugin {
 
 	static final TransportId ID = new TransportId("lan");
 
-	LanTcpPlugin(Executor pluginExecutor, DuplexPluginCallback callback,
+	LanTcpPlugin(Executor ioExecutor, DuplexPluginCallback callback,
 			int maxFrameLength, long maxLatency, long pollingInterval) {
-		super(pluginExecutor, callback, maxFrameLength, maxLatency,
+		super(ioExecutor, callback, maxFrameLength, maxLatency,
 				pollingInterval);
 	}
 
diff --git a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java
index eb24e4550c265bf23aad97415fd5c648827c881d..02b53eab951ec8326377073e2a01bdc5d5b2559d 100644
--- a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java
+++ b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java
@@ -13,10 +13,10 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
 	private static final long MAX_LATENCY = 60 * 1000; // 1 minute
 	private static final long POLLING_INTERVAL = 60 * 1000; // 1 minute
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 
-	public LanTcpPluginFactory(Executor pluginExecutor) {
-		this.pluginExecutor = pluginExecutor;
+	public LanTcpPluginFactory(Executor ioExecutor) {
+		this.ioExecutor = ioExecutor;
 	}
 
 	public TransportId getId() {
@@ -24,7 +24,7 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
 	}
 
 	public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
-		return new LanTcpPlugin(pluginExecutor, callback, MAX_FRAME_LENGTH,
+		return new LanTcpPlugin(ioExecutor, callback, MAX_FRAME_LENGTH,
 				MAX_LATENCY, POLLING_INTERVAL);
 	}
 }
diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java
index c5de9d6c86d8c5727463e32e752b355141a090dc..a8d3e8dde839007fbe89ff25e6a22bed292627c4 100644
--- a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java
+++ b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java
@@ -35,7 +35,7 @@ abstract class TcpPlugin implements DuplexPlugin {
 	private static final Logger LOG =
 			Logger.getLogger(TcpPlugin.class.getName());
 
-	protected final Executor pluginExecutor;
+	protected final Executor ioExecutor;
 	protected final DuplexPluginCallback callback;
 	protected final int maxFrameLength;
 	protected final long maxLatency, pollingInterval;
@@ -52,9 +52,9 @@ abstract class TcpPlugin implements DuplexPlugin {
 	/** Returns true if connections to the given address can be attempted. */
 	protected abstract boolean isConnectable(InetSocketAddress remote);
 
-	protected TcpPlugin(Executor pluginExecutor, DuplexPluginCallback callback,
+	protected TcpPlugin(Executor ioExecutor, DuplexPluginCallback callback,
 			int maxFrameLength, long maxLatency, long pollingInterval) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.callback = callback;
 		this.maxFrameLength = maxFrameLength;
 		this.maxLatency = maxLatency;
@@ -76,7 +76,7 @@ abstract class TcpPlugin implements DuplexPlugin {
 	}
 
 	protected void bind() {
-		pluginExecutor.execute(new Runnable() {
+		ioExecutor.execute(new Runnable() {
 			public void run() {
 				if(!running) return;
 				ServerSocket ss = null;
@@ -172,7 +172,7 @@ abstract class TcpPlugin implements DuplexPlugin {
 	}
 
 	private void connectAndCallBack(final ContactId c) {
-		pluginExecutor.execute(new Runnable() {
+		ioExecutor.execute(new Runnable() {
 			public void run() {
 				DuplexTransportConnection d = createConnection(c);
 				if(d != null) callback.outgoingConnectionCreated(c, d);
diff --git a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java
index 7a403b63682d1862200076eda88d8e2804c6151f..6e2e3e754cf814278a0929ad5363db17d50bb466 100644
--- a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java
+++ b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java
@@ -20,10 +20,10 @@ class WanTcpPlugin extends TcpPlugin {
 
 	private volatile MappingResult mappingResult;
 
-	WanTcpPlugin(Executor pluginExecutor, DuplexPluginCallback callback,
+	WanTcpPlugin(Executor ioExecutor, DuplexPluginCallback callback,
 			int maxFrameLength, long maxLatency, long pollingInterval,
 			PortMapper portMapper) {
-		super(pluginExecutor, callback, maxFrameLength, maxLatency,
+		super(ioExecutor, callback, maxFrameLength, maxLatency,
 				pollingInterval);
 		this.portMapper = portMapper;
 	}
diff --git a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java
index a6de8017313a09fc3ade4fe2a4351669c60beacc..f478bbc295fe13b3825536309fadf3be9f590dcc 100644
--- a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java
+++ b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java
@@ -14,12 +14,12 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
 	private static final long MAX_LATENCY = 60 * 1000; // 1 minute
 	private static final long POLLING_INTERVAL = 5 * 60 * 1000; // 5 minutes
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final ShutdownManager shutdownManager;
 
-	public WanTcpPluginFactory(Executor pluginExecutor,
+	public WanTcpPluginFactory(Executor ioExecutor,
 			ShutdownManager shutdownManager) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.shutdownManager = shutdownManager;
 	}
 
@@ -28,7 +28,7 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
 	}
 
 	public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
-		return new WanTcpPlugin(pluginExecutor, callback, MAX_FRAME_LENGTH,
+		return new WanTcpPlugin(ioExecutor, callback, MAX_FRAME_LENGTH,
 				MAX_LATENCY, POLLING_INTERVAL,
 				new PortMapperImpl(shutdownManager));
 	}
diff --git a/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java b/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java
index f37495e9f34e856499aef2ab82a39e8170990fa4..9844ad30e349b8b6837435a870650896e32897cd 100644
--- a/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java
+++ b/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java
@@ -14,6 +14,7 @@ import javax.inject.Inject;
 import org.briarproject.api.ContactId;
 import org.briarproject.api.TransportId;
 import org.briarproject.api.db.DbException;
+import org.briarproject.api.lifecycle.IoExecutor;
 import org.briarproject.api.messaging.duplex.DuplexConnectionFactory;
 import org.briarproject.api.messaging.simplex.SimplexConnectionFactory;
 import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
@@ -22,31 +23,30 @@ import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
 import org.briarproject.api.transport.ConnectionContext;
 import org.briarproject.api.transport.ConnectionDispatcher;
 import org.briarproject.api.transport.ConnectionRecogniser;
-import org.briarproject.api.transport.IncomingConnectionExecutor;
 
 class ConnectionDispatcherImpl implements ConnectionDispatcher {
 
 	private static final Logger LOG =
 			Logger.getLogger(ConnectionDispatcherImpl.class.getName());
 
-	private final Executor connExecutor;
+	private final Executor ioExecutor;
 	private final ConnectionRecogniser recogniser;
 	private final SimplexConnectionFactory simplexConnFactory;
 	private final DuplexConnectionFactory duplexConnFactory;
 
 	@Inject
-	ConnectionDispatcherImpl(@IncomingConnectionExecutor Executor connExecutor,
+	ConnectionDispatcherImpl(@IoExecutor Executor ioExecutor,
 			ConnectionRecogniser recogniser,
 			SimplexConnectionFactory simplexConnFactory,
 			DuplexConnectionFactory duplexConnFactory) {
-		this.connExecutor = connExecutor;
+		this.ioExecutor = ioExecutor;
 		this.recogniser = recogniser;
 		this.simplexConnFactory = simplexConnFactory;
 		this.duplexConnFactory = duplexConnFactory;
 	}
 
 	public void dispatchReader(TransportId t, SimplexTransportReader r) {
-		connExecutor.execute(new DispatchSimplexConnection(t, r));
+		ioExecutor.execute(new DispatchSimplexConnection(t, r));
 	}
 
 	public void dispatchWriter(ContactId c, TransportId t,
@@ -56,7 +56,7 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
 
 	public void dispatchIncomingConnection(TransportId t,
 			DuplexTransportConnection d) {
-		connExecutor.execute(new DispatchDuplexConnection(t, d));
+		ioExecutor.execute(new DispatchDuplexConnection(t, d));
 	}
 
 	public void dispatchOutgoingConnection(ContactId c, TransportId t,
diff --git a/briar-core/src/org/briarproject/transport/TransportModule.java b/briar-core/src/org/briarproject/transport/TransportModule.java
index b53c7f7c1368825102da091dbaebd466ed06ee80..3fec3a8068125b97a06ac2885f1bf7aa68c340b5 100644
--- a/briar-core/src/org/briarproject/transport/TransportModule.java
+++ b/briar-core/src/org/briarproject/transport/TransportModule.java
@@ -1,14 +1,5 @@
 package org.briarproject.transport;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-
 import javax.inject.Singleton;
 
 import org.briarproject.api.crypto.KeyManager;
@@ -18,26 +9,13 @@ import org.briarproject.api.transport.ConnectionReaderFactory;
 import org.briarproject.api.transport.ConnectionRecogniser;
 import org.briarproject.api.transport.ConnectionRegistry;
 import org.briarproject.api.transport.ConnectionWriterFactory;
-import org.briarproject.api.transport.IncomingConnectionExecutor;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 
 public class TransportModule extends AbstractModule {
 
-	private final ExecutorService incomingConnectionExecutor;
-
-	public TransportModule() {
-		// The thread pool is unbounded, so use direct handoff
-		BlockingQueue<Runnable> queue = new SynchronousQueue<Runnable>();
-		// Discard tasks that are submitted during shutdown
-		RejectedExecutionHandler policy =
-				new ThreadPoolExecutor.DiscardPolicy();
-		// Create threads as required and keep them in the pool for 60 seconds
-		incomingConnectionExecutor = new ThreadPoolExecutor(0,
-				Integer.MAX_VALUE, 60, SECONDS, queue, policy);
-	}
-
+	@Override
 	protected void configure() {
 		bind(ConnectionDispatcher.class).to(ConnectionDispatcherImpl.class);
 		bind(ConnectionReaderFactory.class).to(
@@ -55,10 +33,4 @@ public class TransportModule extends AbstractModule {
 		lifecycleManager.register(keyManager);
 		return keyManager;
 	}
-
-	@Provides @Singleton @IncomingConnectionExecutor
-	Executor getIncomingConnectionExecutor(LifecycleManager lifecycleManager) {
-		lifecycleManager.registerForShutdown(incomingConnectionExecutor);
-		return incomingConnectionExecutor;
-	}
 }
diff --git a/briar-desktop/src/org/briarproject/lifecycle/DesktopLifecycleModule.java b/briar-desktop/src/org/briarproject/lifecycle/DesktopLifecycleModule.java
index 8395bc8cf5cbcef2c541378b720e3fed6446af61..ea12c7098756ccb548faac3f66843db4f6573ddc 100644
--- a/briar-desktop/src/org/briarproject/lifecycle/DesktopLifecycleModule.java
+++ b/briar-desktop/src/org/briarproject/lifecycle/DesktopLifecycleModule.java
@@ -4,21 +4,20 @@ import org.briarproject.api.lifecycle.LifecycleManager;
 import org.briarproject.api.lifecycle.ShutdownManager;
 import org.briarproject.util.OsUtils;
 
-import com.google.inject.AbstractModule;
 import com.google.inject.Singleton;
 
-public class DesktopLifecycleModule extends AbstractModule {
+public class DesktopLifecycleModule extends LifecycleModule {
 
+	@Override
 	protected void configure() {
 		bind(LifecycleManager.class).to(
 				LifecycleManagerImpl.class).in(Singleton.class);
 		if(OsUtils.isWindows()) {
 			bind(ShutdownManager.class).to(
-				WindowsShutdownManagerImpl.class).in(
-					Singleton.class);
+					WindowsShutdownManagerImpl.class).in(Singleton.class);
 		} else {
 			bind(ShutdownManager.class).to(
-				ShutdownManagerImpl.class).in(Singleton.class);
+					ShutdownManagerImpl.class).in(Singleton.class);
 		}
 	}
 }
diff --git a/briar-desktop/src/org/briarproject/plugins/DesktopPluginsModule.java b/briar-desktop/src/org/briarproject/plugins/DesktopPluginsModule.java
index 021b80301e2ce692a5cea8a32e9e1badce878053..58be0ce511e66d642d7379adb8c0bc8d2901b5fb 100644
--- a/briar-desktop/src/org/briarproject/plugins/DesktopPluginsModule.java
+++ b/briar-desktop/src/org/briarproject/plugins/DesktopPluginsModule.java
@@ -5,8 +5,8 @@ import java.util.Collection;
 import java.util.concurrent.Executor;
 
 import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.lifecycle.IoExecutor;
 import org.briarproject.api.lifecycle.ShutdownManager;
-import org.briarproject.api.plugins.PluginExecutor;
 import org.briarproject.api.plugins.duplex.DuplexPluginConfig;
 import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
 import org.briarproject.api.plugins.simplex.SimplexPluginConfig;
@@ -19,18 +19,15 @@ import org.briarproject.plugins.modem.ModemPluginFactory;
 import org.briarproject.plugins.tcp.LanTcpPluginFactory;
 import org.briarproject.plugins.tcp.WanTcpPluginFactory;
 
-import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 
-public class DesktopPluginsModule extends AbstractModule {
-
-	public void configure() {}
+public class DesktopPluginsModule extends PluginsModule {
 
 	@Provides
-	SimplexPluginConfig getSimplexPluginConfig(
-			@PluginExecutor Executor pluginExecutor, FileUtils fileUtils) {
+	SimplexPluginConfig getSimplexPluginConfig(@IoExecutor Executor ioExecutor,
+			FileUtils fileUtils) {
 		SimplexPluginFactory removable =
-				new RemovableDrivePluginFactory(pluginExecutor, fileUtils);
+				new RemovableDrivePluginFactory(ioExecutor, fileUtils);
 		final Collection<SimplexPluginFactory> factories =
 				Arrays.asList(removable);
 		return new SimplexPluginConfig() {
@@ -41,16 +38,15 @@ public class DesktopPluginsModule extends AbstractModule {
 	}
 
 	@Provides
-	DuplexPluginConfig getDuplexPluginConfig(
-			@PluginExecutor Executor pluginExecutor,
+	DuplexPluginConfig getDuplexPluginConfig(@IoExecutor Executor ioExecutor,
 			CryptoComponent crypto, ReliabilityLayerFactory reliabilityFactory,
 			ShutdownManager shutdownManager) {
 		DuplexPluginFactory bluetooth = new BluetoothPluginFactory(
-				pluginExecutor, crypto.getSecureRandom());
-		DuplexPluginFactory modem = new ModemPluginFactory(pluginExecutor,
+				ioExecutor, crypto.getSecureRandom());
+		DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
 				reliabilityFactory);
-		DuplexPluginFactory lan = new LanTcpPluginFactory(pluginExecutor);
-		DuplexPluginFactory wan = new WanTcpPluginFactory(pluginExecutor,
+		DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor);
+		DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor,
 				shutdownManager);
 		final Collection<DuplexPluginFactory> factories =
 				Arrays.asList(bluetooth, modem, lan, wan);
diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java
index 364b44a51a9db2dd46c0408391f3e41b053f29b6..82f69bee237d01ba3ccd0de5a4537507eda9b198 100644
--- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java
+++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java
@@ -42,7 +42,7 @@ class BluetoothPlugin implements DuplexPlugin {
 			Logger.getLogger(BluetoothPlugin.class.getName());
 	private static final int UUID_BYTES = 16;
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final Clock clock;
 	private final SecureRandom secureRandom;
 	private final DuplexPluginCallback callback;
@@ -54,10 +54,10 @@ class BluetoothPlugin implements DuplexPlugin {
 	private volatile StreamConnectionNotifier socket = null;
 	private volatile LocalDevice localDevice = null;
 
-	BluetoothPlugin(Executor pluginExecutor, Clock clock,
-			SecureRandom secureRandom, DuplexPluginCallback callback,
-			int maxFrameLength, long maxLatency, long pollingInterval) {
-		this.pluginExecutor = pluginExecutor;
+	BluetoothPlugin(Executor ioExecutor, Clock clock, SecureRandom secureRandom,
+			DuplexPluginCallback callback, int maxFrameLength, long maxLatency,
+			long pollingInterval) {
+		this.ioExecutor = ioExecutor;
 		this.clock = clock;
 		this.secureRandom = secureRandom;
 		this.callback = callback;
@@ -96,7 +96,7 @@ class BluetoothPlugin implements DuplexPlugin {
 	}
 
 	private void bind() {
-		pluginExecutor.execute(new Runnable() {
+		ioExecutor.execute(new Runnable() {
 			public void run() {
 				if(!running) return;
 				// Advertise the Bluetooth address to contacts
@@ -197,7 +197,7 @@ class BluetoothPlugin implements DuplexPlugin {
 			if(StringUtils.isNullOrEmpty(address)) continue;
 			final String uuid = e.getValue().get("uuid");
 			if(StringUtils.isNullOrEmpty(uuid)) continue;
-			pluginExecutor.execute(new Runnable() {
+			ioExecutor.execute(new Runnable() {
 				public void run() {
 					if(!running) return;
 					StreamConnection s = connect(makeUrl(address, uuid));
diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java
index 8e4963f7909e2d038fddc28681c6ea948c03e050..446846305b840605d64c9504fc2a2a3077689920 100644
--- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java
+++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java
@@ -16,13 +16,13 @@ public class BluetoothPluginFactory implements DuplexPluginFactory {
 	private static final long MAX_LATENCY = 60 * 1000; // 1 minute
 	private static final long POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final SecureRandom secureRandom;
 	private final Clock clock;
 
-	public BluetoothPluginFactory(Executor pluginExecutor,
+	public BluetoothPluginFactory(Executor ioExecutor,
 			SecureRandom secureRandom) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.secureRandom = secureRandom;
 		clock = new SystemClock();
 	}
@@ -32,7 +32,7 @@ public class BluetoothPluginFactory implements DuplexPluginFactory {
 	}
 
 	public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
-		return new BluetoothPlugin(pluginExecutor, clock, secureRandom,
-				callback, MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL);
+		return new BluetoothPlugin(ioExecutor, clock, secureRandom, callback,
+				MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL);
 	}
 }
diff --git a/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java b/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java
index a7b0208c747b54746e6e7253a25df196fe4c197e..2d8fd287616093ff0ddd54b5c5f9adbdd75ad412 100644
--- a/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java
+++ b/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java
@@ -11,7 +11,7 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
 	private static final Logger LOG =
 			Logger.getLogger(PollingRemovableDriveMonitor.class.getName());
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final RemovableDriveFinder finder;
 	private final long pollingInterval;
 	private final Object pollingLock = new Object();
@@ -19,9 +19,9 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
 	private volatile boolean running = false;
 	private volatile Callback callback = null;
 
-	public PollingRemovableDriveMonitor(Executor pluginExecutor,
+	public PollingRemovableDriveMonitor(Executor ioExecutor,
 			RemovableDriveFinder finder, long pollingInterval) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.finder = finder;
 		this.pollingInterval = pollingInterval;
 	}
@@ -29,7 +29,7 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
 	public void start(Callback callback) throws IOException {
 		this.callback = callback;
 		running = true;
-		pluginExecutor.execute(this);
+		ioExecutor.execute(this);
 	}
 
 	public void stop() throws IOException {
diff --git a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java
index 76a5a0f67753c3415c2e0bf84f692af3e512049f..09ffd6f663592668cc148c7ae74af1b1cc065005 100644
--- a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java
+++ b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java
@@ -27,11 +27,11 @@ implements RemovableDriveMonitor.Callback {
 	private final RemovableDriveFinder finder;
 	private final RemovableDriveMonitor monitor;
 
-	RemovableDrivePlugin(Executor pluginExecutor, FileUtils fileUtils,
+	RemovableDrivePlugin(Executor ioExecutor, FileUtils fileUtils,
 			SimplexPluginCallback callback, RemovableDriveFinder finder,
 			RemovableDriveMonitor monitor, int maxFrameLength,
 			long maxLatency) {
-		super(pluginExecutor, fileUtils, callback, maxFrameLength, maxLatency);
+		super(ioExecutor, fileUtils, callback, maxFrameLength, maxLatency);
 		this.finder = finder;
 		this.monitor = monitor;
 	}
diff --git a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePluginFactory.java b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePluginFactory.java
index 5b948cde334c156102a9ec643a10717bbdfd9f39..01688d40344aee7171076077cdd4ca3b09ecfda5 100644
--- a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePluginFactory.java
+++ b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePluginFactory.java
@@ -17,12 +17,12 @@ public class RemovableDrivePluginFactory implements SimplexPluginFactory {
 	private static final long MAX_LATENCY = 14 * 24 * 60 * 60 * 1000;
 	private static final long POLLING_INTERVAL = 10 * 1000; // 10 seconds
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final FileUtils fileUtils;
 
-	public RemovableDrivePluginFactory(Executor pluginExecutor,
+	public RemovableDrivePluginFactory(Executor ioExecutor,
 			FileUtils fileUtils) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.fileUtils = fileUtils;
 	}
 
@@ -42,16 +42,16 @@ public class RemovableDrivePluginFactory implements SimplexPluginFactory {
 		} else if(OsUtils.isMac()) {
 			// JNotify requires OS X 10.5 or newer, so we have to poll
 			finder = new MacRemovableDriveFinder();
-			monitor = new PollingRemovableDriveMonitor(pluginExecutor, finder,
+			monitor = new PollingRemovableDriveMonitor(ioExecutor, finder,
 					POLLING_INTERVAL);
 		} else if(OsUtils.isWindows()) {
 			finder = new WindowsRemovableDriveFinder();
-			monitor = new PollingRemovableDriveMonitor(pluginExecutor, finder,
+			monitor = new PollingRemovableDriveMonitor(ioExecutor, finder,
 					POLLING_INTERVAL);
 		} else {
 			return null;
 		}
-		return new RemovableDrivePlugin(pluginExecutor, fileUtils, callback,
+		return new RemovableDrivePlugin(ioExecutor, fileUtils, callback,
 				finder, monitor, MAX_FRAME_LENGTH, MAX_LATENCY);
 	}
 }
diff --git a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java
index 7d23028cd2b4c2183814906c1a6c19c741713db4..22d45eb0254f99298522c1c702c17f47bf49f446 100644
--- a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java
+++ b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java
@@ -32,7 +32,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 	private static final Logger LOG =
 			Logger.getLogger(ModemPlugin.class.getName());
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final ModemFactory modemFactory;
 	private final SerialPortList serialPortList;
 	private final DuplexPluginCallback callback;
@@ -43,11 +43,11 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 	private volatile boolean running = false;
 	private volatile Modem modem = null;
 
-	ModemPlugin(Executor pluginExecutor, ModemFactory modemFactory,
+	ModemPlugin(Executor ioExecutor, ModemFactory modemFactory,
 			SerialPortList serialPortList, DuplexPluginCallback callback,
 			int maxFrameLength, long maxLatency, long pollingInterval,
 			boolean shuffle) {
-		this.pluginExecutor = pluginExecutor;
+		this.ioExecutor = ioExecutor;
 		this.modemFactory = modemFactory;
 		this.serialPortList = serialPortList;
 		this.callback = callback;
@@ -112,7 +112,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 
 	public void poll(Collection<ContactId> connected) {
 		if(!connected.isEmpty()) return; // One at a time please
-		pluginExecutor.execute(new Runnable() {
+		ioExecutor.execute(new Runnable() {
 			public void run() {
 				poll();
 			}
diff --git a/briar-desktop/src/org/briarproject/plugins/modem/ModemPluginFactory.java b/briar-desktop/src/org/briarproject/plugins/modem/ModemPluginFactory.java
index b32dcd01ec71ff29b58682ac7c2b0e4151c74579..4fff4bbfaa066db8b1cbf83a49cef1386fe95703 100644
--- a/briar-desktop/src/org/briarproject/plugins/modem/ModemPluginFactory.java
+++ b/briar-desktop/src/org/briarproject/plugins/modem/ModemPluginFactory.java
@@ -15,14 +15,14 @@ public class ModemPluginFactory implements DuplexPluginFactory {
 	private static final long MAX_LATENCY = 60 * 1000; // 1 minute
 	private static final long POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour
 
-	private final Executor pluginExecutor;
+	private final Executor ioExecutor;
 	private final ModemFactory modemFactory;
 	private final SerialPortList serialPortList;
 
-	public ModemPluginFactory(Executor pluginExecutor,
+	public ModemPluginFactory(Executor ioExecutor,
 			ReliabilityLayerFactory reliabilityFactory) {
-		this.pluginExecutor = pluginExecutor;
-		modemFactory = new ModemFactoryImpl(pluginExecutor, reliabilityFactory);
+		this.ioExecutor = ioExecutor;
+		modemFactory = new ModemFactoryImpl(ioExecutor, reliabilityFactory);
 		serialPortList = new SerialPortListImpl();
 	}
 
@@ -34,7 +34,7 @@ public class ModemPluginFactory implements DuplexPluginFactory {
 		// This plugin is not enabled by default
 		String enabled = callback.getConfig().get("enabled");
 		if(StringUtils.isNullOrEmpty(enabled)) return null;
-		return new ModemPlugin(pluginExecutor, modemFactory, serialPortList,
+		return new ModemPlugin(ioExecutor, modemFactory, serialPortList,
 				callback, MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL,
 				true);
 	}
diff --git a/briar-tests/src/org/briarproject/TestLifecycleModule.java b/briar-tests/src/org/briarproject/TestLifecycleModule.java
index becccd4523a35d4834682595750ccf81b05a698b..580b445e2b67bc25962b917a92071fc48be37f23 100644
--- a/briar-tests/src/org/briarproject/TestLifecycleModule.java
+++ b/briar-tests/src/org/briarproject/TestLifecycleModule.java
@@ -1,7 +1,10 @@
 package org.briarproject;
 
+import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
+import org.briarproject.api.lifecycle.IoExecutor;
 import org.briarproject.api.lifecycle.LifecycleManager;
 import org.briarproject.api.lifecycle.Service;
 import org.briarproject.api.lifecycle.ShutdownManager;
@@ -10,6 +13,7 @@ import com.google.inject.AbstractModule;
 
 public class TestLifecycleModule extends AbstractModule {
 
+	@Override
 	protected void configure() {
 		bind(LifecycleManager.class).toInstance(new LifecycleManager() {
 
@@ -37,5 +41,7 @@ public class TestLifecycleModule extends AbstractModule {
 				return true;
 			}
 		});
+		bind(Executor.class).annotatedWith(IoExecutor.class).toInstance(
+				Executors.newCachedThreadPool());
 	}
 }
diff --git a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java
index 7c861bc72472358c616a5d10724fb291515cf23d..43fcc4e9fdef26af36c74979487f5b9a1f8f0dc3 100644
--- a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java
+++ b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java
@@ -29,7 +29,7 @@ public class PluginManagerImplTest extends BriarTestCase {
 	public void testStartAndStop() throws Exception {
 		Clock clock = new SystemClock();
 		Mockery context = new Mockery();
-		final Executor pluginExecutor = Executors.newCachedThreadPool();
+		final Executor ioExecutor = Executors.newCachedThreadPool();
 		final SimplexPluginConfig simplexPluginConfig =
 				context.mock(SimplexPluginConfig.class);
 		final DuplexPluginConfig duplexPluginConfig =
@@ -116,7 +116,7 @@ public class PluginManagerImplTest extends BriarTestCase {
 			oneOf(simplexPlugin).stop();
 			oneOf(duplexPlugin).stop();
 		}});
-		PluginManagerImpl p = new PluginManagerImpl(pluginExecutor,
+		PluginManagerImpl p = new PluginManagerImpl(ioExecutor,
 				simplexPluginConfig, duplexPluginConfig, clock, db, poller,
 				dispatcher, uiCallback);
 		// Two plugins should be started and stopped
diff --git a/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java b/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java
index 40dc56579a50b02b80a588aba0c00f8a7b6fd96e..d008330a28e3e77cd04c19099b521fea5975c35c 100644
--- a/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java
+++ b/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java
@@ -15,7 +15,6 @@ import org.briarproject.api.ContactId;
 import org.briarproject.api.TransportProperties;
 import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
 import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
-
 import org.hamcrest.Description;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
@@ -194,8 +193,7 @@ public class ModemPluginTest extends BriarTestCase {
 
 	@Test
 	public void testPolling() throws Exception {
-		final ExecutorService pluginExecutor =
-				Executors.newSingleThreadExecutor();
+		final ExecutorService ioExecutor = Executors.newSingleThreadExecutor();
 		Mockery context = new Mockery();
 		final ModemFactory modemFactory = context.mock(ModemFactory.class);
 		final SerialPortList serialPortList =
@@ -203,7 +201,7 @@ public class ModemPluginTest extends BriarTestCase {
 		final DuplexPluginCallback callback =
 				context.mock(DuplexPluginCallback.class);
 		// Disable shuffling for this test, it confuses jMock
-		final ModemPlugin plugin = new ModemPlugin(pluginExecutor, modemFactory,
+		final ModemPlugin plugin = new ModemPlugin(ioExecutor, modemFactory,
 				serialPortList, callback, 0, 0, 0, false);
 		final Modem modem = context.mock(Modem.class);
 		final TransportProperties local = new TransportProperties();
@@ -265,7 +263,7 @@ public class ModemPluginTest extends BriarTestCase {
 		assertTrue(plugin.start());
 		plugin.poll(Collections.<ContactId>emptyList());
 		assertTrue(disposeAction.invoked.await(5, SECONDS));
-		pluginExecutor.shutdown();
+		ioExecutor.shutdown();
 		context.assertIsSatisfied();
 	}