diff --git a/briar-android/res/values/roboguice.xml b/briar-android/res/values/roboguice.xml
index 0e9ab106648b6025db2628f2331fb90f8a66b102..55873674e6ed23f67559ef3db463284d3404c185 100644
--- a/briar-android/res/values/roboguice.xml
+++ b/briar-android/res/values/roboguice.xml
@@ -12,7 +12,6 @@
 		<item>net.sf.briar.messaging.duplex.DuplexMessagingModule</item>
 		<item>net.sf.briar.messaging.simplex.SimplexMessagingModule</item>
 		<item>net.sf.briar.plugins.PluginsModule</item>
-		<item>net.sf.briar.reliability.ReliabilityModule</item>
 		<item>net.sf.briar.serial.SerialModule</item>
 		<item>net.sf.briar.transport.TransportModule</item>
 	</string-array>
diff --git a/briar-android/src/net/sf/briar/android/SplashScreenActivity.java b/briar-android/src/net/sf/briar/android/SplashScreenActivity.java
index 2ff1a7d050b9c06de72887eebaccf3508a2cd983..a0683b5467c8d8bf68ac6bc8d92ce290aa69a1c5 100644
--- a/briar-android/src/net/sf/briar/android/SplashScreenActivity.java
+++ b/briar-android/src/net/sf/briar/android/SplashScreenActivity.java
@@ -16,7 +16,6 @@ import com.google.inject.Injector;
 public class SplashScreenActivity extends RoboSplashActivity {
 
 	public SplashScreenActivity() {
-		super();
 		minDisplayMs = 0;
 	}
 
diff --git a/briar-api/src/net/sf/briar/api/crypto/CryptoExecutor.java b/briar-api/src/net/sf/briar/api/crypto/CryptoExecutor.java
index 8e3ba8dc9cbcb4a9542e823ac3fd01914c0e37bc..fa2a8f11d50d08de5126d4b82850150ec1b4886a 100644
--- a/briar-api/src/net/sf/briar/api/crypto/CryptoExecutor.java
+++ b/briar-api/src/net/sf/briar/api/crypto/CryptoExecutor.java
@@ -1,6 +1,7 @@
 package net.sf.briar.api.crypto;
 
 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;
 
@@ -11,6 +12,6 @@ import com.google.inject.BindingAnnotation;
 
 /** Annotation for injecting the executor for long-running crypto tasks. */
 @BindingAnnotation
-@Target({ FIELD, PARAMETER })
+@Target({ FIELD, METHOD, PARAMETER })
 @Retention(RUNTIME)
 public @interface CryptoExecutor {}
diff --git a/briar-api/src/net/sf/briar/api/crypto/KeyManager.java b/briar-api/src/net/sf/briar/api/crypto/KeyManager.java
index 781ad3b13979d5fab2ab2522eafa6f4355677bb2..803b1037ccab7a622a854976a733cb55c690e72e 100644
--- a/briar-api/src/net/sf/briar/api/crypto/KeyManager.java
+++ b/briar-api/src/net/sf/briar/api/crypto/KeyManager.java
@@ -2,19 +2,11 @@ package net.sf.briar.api.crypto;
 
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
+import net.sf.briar.api.lifecycle.Service;
 import net.sf.briar.api.transport.ConnectionContext;
 import net.sf.briar.api.transport.Endpoint;
 
-public interface KeyManager {
-
-	/**
-	 * Starts the key manager and returns true if it started successfully. This
-	 * method must be called after the database has been opened.
-	 */
-	boolean start();
-
-	/** Stops the key manager. */
-	void stop();
+public interface KeyManager extends Service {
 
 	/**
 	 * Returns a connection context for connecting to the given contact over
diff --git a/briar-api/src/net/sf/briar/api/db/DatabaseExecutor.java b/briar-api/src/net/sf/briar/api/db/DatabaseExecutor.java
index 82da7a7277bd4f4f2f51f11f9021e7a6ee98fc68..cd0f6c909a8cd935fe4235ec7a81c3dbfd015a2a 100644
--- a/briar-api/src/net/sf/briar/api/db/DatabaseExecutor.java
+++ b/briar-api/src/net/sf/briar/api/db/DatabaseExecutor.java
@@ -1,6 +1,7 @@
 package net.sf.briar.api.db;
 
 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;
 
@@ -11,6 +12,6 @@ import com.google.inject.BindingAnnotation;
 
 /** Annotation for injecting the executor for database tasks. */
 @BindingAnnotation
-@Target({ FIELD, PARAMETER })
+@Target({ FIELD, METHOD, PARAMETER })
 @Retention(RUNTIME)
 public @interface DatabaseExecutor {}
diff --git a/briar-api/src/net/sf/briar/api/lifecycle/LifecycleManager.java b/briar-api/src/net/sf/briar/api/lifecycle/LifecycleManager.java
index acbe586ae9d91348faeaea62ea002248670ca927..9d312c789c1e801c4049da06b6b2b56bcf2e2adb 100644
--- a/briar-api/src/net/sf/briar/api/lifecycle/LifecycleManager.java
+++ b/briar-api/src/net/sf/briar/api/lifecycle/LifecycleManager.java
@@ -1,19 +1,37 @@
 package net.sf.briar.api.lifecycle;
 
+import java.util.concurrent.ExecutorService;
+
 public interface LifecycleManager {
 
-	/** Starts any services that need to be started at startup. */
+	/** Registers a {@link Service} to be started and stopped. */
+	public void register(Service s);
+
+	/**
+	 * Registers an {@link java.util.concurrent.ExecutorService ExecutorService}
+	 * to be shut down.
+	 */
+	public void registerForShutdown(ExecutorService e);
+
+	/**  Starts any registered {@link Service}s. */
 	public void startServices();
 
-	/** Stops any services that need to be stopped at shutdown. */
+	/**
+	 * Stops any registered {@link Service}s and shuts down any registered
+	 * {@link java.util.concurrent.ExecutorService ExecutorService}s.
+	 */
 	public void stopServices();
 
 	/** Waits for the database to be opened before returning. */
 	public void waitForDatabase() throws InterruptedException;
 
-	/** Waits for all services to start before returning. */
+	/** Waits for all registered {@link Service}s to start before returning. */
 	public void waitForStartup() throws InterruptedException;
 
-	/** Waits for all services to stop before returning. */
+	/**
+	 * Waits for all registered {@link Service}s to stop and all registered
+	 * {@link java.util.concurrent.ExecutorService ExecutorService}s to shut
+	 * down before returning.
+	 */
 	public void waitForShutdown() throws InterruptedException;
-}
+}
\ No newline at end of file
diff --git a/briar-api/src/net/sf/briar/api/lifecycle/Service.java b/briar-api/src/net/sf/briar/api/lifecycle/Service.java
new file mode 100644
index 0000000000000000000000000000000000000000..8662207a9b71723d992a8eccc6b0721cdbe9b7fc
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/lifecycle/Service.java
@@ -0,0 +1,10 @@
+package net.sf.briar.api.lifecycle;
+
+public interface Service {
+
+	/** Starts the service and returns true if it started successfully. */
+	public boolean start();
+
+	/** Stops the service and returns true if it stopped successfully. */
+	public boolean stop();
+}
diff --git a/briar-api/src/net/sf/briar/api/plugins/PluginExecutor.java b/briar-api/src/net/sf/briar/api/plugins/PluginExecutor.java
index 4012baf740bc8d33dd0b268ebf5d7558f48bebcc..f6ba7edb4f10b25bcf6f1d1b80923cb4ee548e7c 100644
--- a/briar-api/src/net/sf/briar/api/plugins/PluginExecutor.java
+++ b/briar-api/src/net/sf/briar/api/plugins/PluginExecutor.java
@@ -1,5 +1,7 @@
 package net.sf.briar.api.plugins;
 
+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;
 
@@ -10,6 +12,6 @@ import com.google.inject.BindingAnnotation;
 
 /** Annotation for injecting the executor used by transport plugins. */
 @BindingAnnotation
-@Target({ PARAMETER })
+@Target({ FIELD, METHOD, PARAMETER })
 @Retention(RUNTIME)
 public @interface PluginExecutor {}
\ No newline at end of file
diff --git a/briar-api/src/net/sf/briar/api/plugins/PluginManager.java b/briar-api/src/net/sf/briar/api/plugins/PluginManager.java
index 297340661e8705cd5d97ca0addce1e82a7a540d3..871867318acf16e1fb7dbe43c48411a20abe87c2 100644
--- a/briar-api/src/net/sf/briar/api/plugins/PluginManager.java
+++ b/briar-api/src/net/sf/briar/api/plugins/PluginManager.java
@@ -2,25 +2,14 @@ package net.sf.briar.api.plugins;
 
 import java.util.Collection;
 
+import net.sf.briar.api.lifecycle.Service;
 import net.sf.briar.api.plugins.duplex.DuplexPlugin;
 
 /**
  * Responsible for starting transport plugins at startup, stopping them at
  * shutdown, and providing access to plugins for exchanging invitations.
  */
-public interface PluginManager {
-
-	/**
-	 * Starts the plugins and returns the number of plugins successfully
-	 * started. This method must not be called until the database has been
-	 * opened.
-	 */
-	int start();
-
-	/**
-	 * Stops the plugins and returns the number of plugins successfully stopped.
-	 */
-	int stop();
+public interface PluginManager extends Service {
 
 	/** Returns any running duplex plugins that support invitations. */
 	Collection<DuplexPlugin> getInvitationPlugins();
diff --git a/briar-api/src/net/sf/briar/api/reliability/ReliabilityExecutor.java b/briar-api/src/net/sf/briar/api/reliability/ReliabilityExecutor.java
index a7ca755f3da933c883e3657d9c053376a22445fd..ad24a5e1bef37d2940e4fccea509c30d64bc73dd 100644
--- a/briar-api/src/net/sf/briar/api/reliability/ReliabilityExecutor.java
+++ b/briar-api/src/net/sf/briar/api/reliability/ReliabilityExecutor.java
@@ -1,5 +1,7 @@
 package net.sf.briar.api.reliability;
 
+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;
 
@@ -10,6 +12,6 @@ import com.google.inject.BindingAnnotation;
 
 /** Annotation for injecting the executor used by reliability layers. */
 @BindingAnnotation
-@Target({ PARAMETER })
+@Target({ FIELD, METHOD, PARAMETER })
 @Retention(RUNTIME)
 public @interface ReliabilityExecutor {}
\ No newline at end of file
diff --git a/briar-api/src/net/sf/briar/api/transport/IncomingConnectionExecutor.java b/briar-api/src/net/sf/briar/api/transport/IncomingConnectionExecutor.java
index e3048a1eb5730c2d09fd11b3f872cd539b47d879..b42f71061ce0714d55c81358b6e67bddc3e76a12 100644
--- a/briar-api/src/net/sf/briar/api/transport/IncomingConnectionExecutor.java
+++ b/briar-api/src/net/sf/briar/api/transport/IncomingConnectionExecutor.java
@@ -1,5 +1,7 @@
 package net.sf.briar.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;
 
@@ -12,6 +14,6 @@ import com.google.inject.BindingAnnotation;
  * Annotation for injecting the executor for recognising incoming connections.
  */
 @BindingAnnotation
-@Target({ PARAMETER })
+@Target({ FIELD, METHOD, PARAMETER })
 @Retention(RUNTIME)
 public @interface IncomingConnectionExecutor {}
\ No newline at end of file
diff --git a/briar-core/src/net/sf/briar/crypto/CryptoModule.java b/briar-core/src/net/sf/briar/crypto/CryptoModule.java
index 2f23bfc6306408c862433de96d5be7e026aa8a56..00457ed92e297e35aed7065e2a0de9a4262fd5f8 100644
--- a/briar-core/src/net/sf/briar/crypto/CryptoModule.java
+++ b/briar-core/src/net/sf/briar/crypto/CryptoModule.java
@@ -11,8 +11,10 @@ import java.util.concurrent.ThreadPoolExecutor;
 
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.CryptoExecutor;
+import net.sf.briar.api.lifecycle.LifecycleManager;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
 import com.google.inject.Singleton;
 
 public class CryptoModule extends AbstractModule {
@@ -21,20 +23,28 @@ public class CryptoModule extends AbstractModule {
 	private static final int MAX_EXECUTOR_THREADS =
 			Runtime.getRuntime().availableProcessors();
 
-	@Override
-	protected void configure() {
-		bind(CryptoComponent.class).to(
-				CryptoComponentImpl.class).in(Singleton.class);
+	private final ExecutorService cryptoExecutor;
+
+	public CryptoModule() {
 		// The queue is unbounded, so tasks can be dependent
 		BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
 		// Discard tasks that are submitted during shutdown
 		RejectedExecutionHandler policy =
 				new ThreadPoolExecutor.DiscardPolicy();
 		// Create a limited # of threads and keep them in the pool for 60 secs
-		ExecutorService e = new ThreadPoolExecutor(0, MAX_EXECUTOR_THREADS,
+		cryptoExecutor = new ThreadPoolExecutor(0, MAX_EXECUTOR_THREADS,
 				60, SECONDS, queue, policy);
-		bind(Executor.class).annotatedWith(CryptoExecutor.class).toInstance(e);
-		bind(ExecutorService.class).annotatedWith(
-				CryptoExecutor.class).toInstance(e);
+	}
+
+	@Override
+	protected void configure() {
+		bind(CryptoComponent.class).to(
+				CryptoComponentImpl.class).in(Singleton.class);
+	}
+
+	@Provides @Singleton @CryptoExecutor
+	Executor getCryptoExecutor(LifecycleManager lifecycleManager) {
+		lifecycleManager.registerForShutdown(cryptoExecutor);
+		return cryptoExecutor;
 	}
 }
diff --git a/briar-core/src/net/sf/briar/db/DatabaseModule.java b/briar-core/src/net/sf/briar/db/DatabaseModule.java
index e038be9c9ae854f5914c3d9d924221a4526fadb9..50db206686bbecd8ba8c40927f681806cb5763e6 100644
--- a/briar-core/src/net/sf/briar/db/DatabaseModule.java
+++ b/briar-core/src/net/sf/briar/db/DatabaseModule.java
@@ -15,6 +15,7 @@ import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DatabaseConfig;
 import net.sf.briar.api.db.DatabaseExecutor;
+import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.lifecycle.ShutdownManager;
 
 import com.google.inject.AbstractModule;
@@ -26,21 +27,22 @@ public class DatabaseModule extends AbstractModule {
 	/** The maximum number of executor threads. */
 	private static final int MAX_EXECUTOR_THREADS = 10;
 
-	@Override
-	protected void configure() {
-		bind(DatabaseCleaner.class).to(DatabaseCleanerImpl.class);
+	private final ExecutorService databaseExecutor;
+
+	public DatabaseModule() {
 		// The queue is unbounded, so tasks can be dependent
 		BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
 		// Discard tasks that are submitted during shutdown
 		RejectedExecutionHandler policy =
 				new ThreadPoolExecutor.DiscardPolicy();
 		// Create a limited # of threads and keep them in the pool for 60 secs
-		ExecutorService e = new ThreadPoolExecutor(0, MAX_EXECUTOR_THREADS,
+		databaseExecutor = new ThreadPoolExecutor(0, MAX_EXECUTOR_THREADS,
 				60, SECONDS, queue, policy);
-		bind(Executor.class).annotatedWith(
-				DatabaseExecutor.class).toInstance(e);
-		bind(ExecutorService.class).annotatedWith(
-				DatabaseExecutor.class).toInstance(e);
+	}
+
+	@Override
+	protected void configure() {
+		bind(DatabaseCleaner.class).to(DatabaseCleanerImpl.class);
 	}
 
 	@Provides
@@ -54,4 +56,10 @@ public class DatabaseModule extends AbstractModule {
 		return new DatabaseComponentImpl<Connection>(db, cleaner, shutdown,
 				clock);
 	}
+
+	@Provides @Singleton @DatabaseExecutor
+	Executor getDatabaseExecutor(LifecycleManager lifecycleManager) {
+		lifecycleManager.registerForShutdown(databaseExecutor);
+		return databaseExecutor;
+	}
 }
diff --git a/briar-core/src/net/sf/briar/lifecycle/LifecycleManagerImpl.java b/briar-core/src/net/sf/briar/lifecycle/LifecycleManagerImpl.java
index d0c414cf31ff302a2a12b3b983028a559badc030..0c14b3a5ddb345d5a1abb1149ce1d0c6cecc5d98 100644
--- a/briar-core/src/net/sf/briar/lifecycle/LifecycleManagerImpl.java
+++ b/briar-core/src/net/sf/briar/lifecycle/LifecycleManagerImpl.java
@@ -4,20 +4,16 @@ import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 
 import java.io.IOException;
+import java.util.Collection;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.logging.Logger;
 
-import net.sf.briar.api.crypto.CryptoExecutor;
-import net.sf.briar.api.crypto.KeyManager;
 import net.sf.briar.api.db.DatabaseComponent;
-import net.sf.briar.api.db.DatabaseExecutor;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.lifecycle.LifecycleManager;
-import net.sf.briar.api.plugins.PluginExecutor;
-import net.sf.briar.api.plugins.PluginManager;
-import net.sf.briar.api.reliability.ReliabilityExecutor;
-import net.sf.briar.api.transport.IncomingConnectionExecutor;
+import net.sf.briar.api.lifecycle.Service;
 
 import com.google.inject.Inject;
 
@@ -27,33 +23,29 @@ class LifecycleManagerImpl implements LifecycleManager {
 			Logger.getLogger(LifecycleManagerImpl.class.getName());
 
 	private final DatabaseComponent db;
-	private final KeyManager keyManager;
-	private final PluginManager pluginManager;
-	private final ExecutorService cryptoExecutor;
-	private final ExecutorService dbExecutor;
-	private final ExecutorService connExecutor;
-	private final ExecutorService pluginExecutor;
-	private final ExecutorService reliabilityExecutor;
+	private final Collection<Service> services;
+	private final Collection<ExecutorService> executors;
 	private final CountDownLatch dbLatch = new CountDownLatch(1);
 	private final CountDownLatch startupLatch = new CountDownLatch(1);
 	private final CountDownLatch shutdownLatch = new CountDownLatch(1);
 
 	@Inject
-	LifecycleManagerImpl(DatabaseComponent db, KeyManager keyManager,
-			PluginManager pluginManager,
-			@CryptoExecutor ExecutorService cryptoExecutor,
-			@DatabaseExecutor ExecutorService dbExecutor,
-			@IncomingConnectionExecutor ExecutorService connExecutor,
-			@PluginExecutor ExecutorService pluginExecutor,
-			@ReliabilityExecutor ExecutorService reliabilityExecutor) {
+	LifecycleManagerImpl(DatabaseComponent db) {
 		this.db = db;
-		this.keyManager = keyManager;
-		this.pluginManager = pluginManager;
-		this.cryptoExecutor = cryptoExecutor;
-		this.dbExecutor = dbExecutor;
-		this.connExecutor = connExecutor;
-		this.pluginExecutor = pluginExecutor;
-		this.reliabilityExecutor = reliabilityExecutor;
+		services = new CopyOnWriteArrayList<Service>();
+		executors = new CopyOnWriteArrayList<ExecutorService>();
+	}
+
+	public void register(Service s) {
+		if(LOG.isLoggable(INFO))
+			LOG.info("Registering service " + s.getClass().getName());
+		services.add(s);
+	}
+
+	public void registerForShutdown(ExecutorService e) {
+		if(LOG.isLoggable(INFO))
+			LOG.info("Registering executor " + e.getClass().getName());
+		executors.add(e);
 	}
 
 	public void startServices() {
@@ -65,11 +57,14 @@ class LifecycleManagerImpl implements LifecycleManager {
 				else LOG.info("Database created");
 			}
 			dbLatch.countDown();
-			keyManager.start();
-			if(LOG.isLoggable(INFO)) LOG.info("Key manager started");
-			int pluginsStarted = pluginManager.start();
-			if(LOG.isLoggable(INFO))
-				LOG.info(pluginsStarted + " plugins started");
+			for(Service s : services) {
+				boolean started = s.start();
+				if(LOG.isLoggable(INFO)) {
+					String name = s.getClass().getName();
+					if(started) LOG.info("Service started: " + name);
+					else LOG.info("Service failed to start: " + name);
+				}
+			}
 			startupLatch.countDown();
 		} catch(DbException e) {
 			if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
@@ -81,19 +76,19 @@ class LifecycleManagerImpl implements LifecycleManager {
 	public void stopServices() {
 		try {
 			if(LOG.isLoggable(INFO)) LOG.info("Shutting down");
-			int pluginsStopped = pluginManager.stop();
+			for(Service s : services) {
+				boolean stopped = s.stop();
+				if(LOG.isLoggable(INFO)) {
+					String name = s.getClass().getName();
+					if(stopped) LOG.info("Service stopped: " + name);
+					else LOG.warning("Service failed to stop: " + name);
+				}
+			}
+			for(ExecutorService e : executors) e.shutdownNow();
 			if(LOG.isLoggable(INFO))
-				LOG.info(pluginsStopped + " plugins stopped");
-			keyManager.stop();
-			if(LOG.isLoggable(INFO)) LOG.info("Key manager stopped");
+				LOG.info(executors.size() + " executors shut down");
 			db.close();
 			if(LOG.isLoggable(INFO)) LOG.info("Database closed");
-			cryptoExecutor.shutdownNow();
-			dbExecutor.shutdownNow();
-			connExecutor.shutdownNow();
-			pluginExecutor.shutdownNow();
-			reliabilityExecutor.shutdownNow();
-			if(LOG.isLoggable(INFO)) LOG.info("Executors shut down");
 			shutdownLatch.countDown();
 		} catch(DbException e) {
 			if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
diff --git a/briar-core/src/net/sf/briar/plugins/PluginManagerImpl.java b/briar-core/src/net/sf/briar/plugins/PluginManagerImpl.java
index bd259b8e595107d5bbce62055ae21d5256ba1443..b24c322c97292fbb7fb23e37b6c1605179a90514 100644
--- a/briar-core/src/net/sf/briar/plugins/PluginManagerImpl.java
+++ b/briar-core/src/net/sf/briar/plugins/PluginManagerImpl.java
@@ -12,7 +12,6 @@ import java.util.Map;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Logger;
 
 import net.sf.briar.api.ContactId;
@@ -75,23 +74,21 @@ class PluginManagerImpl implements PluginManager {
 		duplexPlugins = new CopyOnWriteArrayList<DuplexPlugin>();
 	}
 
-	public synchronized int start() {
+	public synchronized boolean start() {
 		// Instantiate and start the simplex plugins
 		if(LOG.isLoggable(INFO)) LOG.info("Starting simplex plugins");
 		Collection<SimplexPluginFactory> sFactories =
 				simplexPluginConfig.getFactories();
 		final CountDownLatch sLatch = new CountDownLatch(sFactories.size());
-		for(SimplexPluginFactory factory : sFactories) {
+		for(SimplexPluginFactory factory : sFactories)
 			pluginExecutor.execute(new SimplexPluginStarter(factory, sLatch));
-		}
 		// Instantiate and start the duplex plugins
 		if(LOG.isLoggable(INFO)) LOG.info("Starting duplex plugins");
 		Collection<DuplexPluginFactory> dFactories =
 				duplexPluginConfig.getFactories();
 		final CountDownLatch dLatch = new CountDownLatch(dFactories.size());
-		for(DuplexPluginFactory factory : dFactories) {
+		for(DuplexPluginFactory factory : dFactories)
 			pluginExecutor.execute(new DuplexPluginStarter(factory, dLatch));
-		}
 		// Wait for the plugins to start
 		try {
 			sLatch.await();
@@ -100,7 +97,7 @@ class PluginManagerImpl implements PluginManager {
 			if(LOG.isLoggable(WARNING))
 				LOG.warning("Interrupted while starting plugins");
 			Thread.currentThread().interrupt();
-			return 0;
+			return false;
 		}
 		// Start the poller
 		if(LOG.isLoggable(INFO)) LOG.info("Starting poller");
@@ -108,27 +105,23 @@ class PluginManagerImpl implements PluginManager {
 		plugins.addAll(simplexPlugins);
 		plugins.addAll(duplexPlugins);
 		poller.start(Collections.unmodifiableList(plugins));
-		// Return the number of plugins successfully started
-		return plugins.size();
+		return true;
 	}
 
-	public synchronized int stop() {
+	public synchronized boolean stop() {
 		// Stop the poller
 		if(LOG.isLoggable(INFO)) LOG.info("Stopping poller");
 		poller.stop();
-		final AtomicInteger stopped = new AtomicInteger(0);
 		int plugins = simplexPlugins.size() + duplexPlugins.size();
 		final CountDownLatch latch = new CountDownLatch(plugins);
 		// Stop the simplex plugins
 		if(LOG.isLoggable(INFO)) LOG.info("Stopping simplex plugins");
-		for(SimplexPlugin plugin : simplexPlugins) {
-			pluginExecutor.execute(new PluginStopper(plugin, latch, stopped));
-		}
+		for(SimplexPlugin plugin : simplexPlugins)
+			pluginExecutor.execute(new PluginStopper(plugin, latch));
 		// Stop the duplex plugins
 		if(LOG.isLoggable(INFO)) LOG.info("Stopping duplex plugins");
-		for(DuplexPlugin plugin : duplexPlugins) {
-			pluginExecutor.execute(new PluginStopper(plugin, latch, stopped));
-		}
+		for(DuplexPlugin plugin : duplexPlugins)
+			pluginExecutor.execute(new PluginStopper(plugin, latch));
 		simplexPlugins.clear();
 		duplexPlugins.clear();
 		// Wait for all the plugins to stop
@@ -138,10 +131,9 @@ class PluginManagerImpl implements PluginManager {
 			if(LOG.isLoggable(WARNING))
 				LOG.warning("Interrupted while stopping plugins");
 			Thread.currentThread().interrupt();
-			return 0;
+			return false;
 		}
-		// Return the number of plugins successfully stopped
-		return stopped.get();
+		return true;
 	}
 
 	public Collection<DuplexPlugin> getInvitationPlugins() {
@@ -253,19 +245,15 @@ class PluginManagerImpl implements PluginManager {
 
 		private final Plugin plugin;
 		private final CountDownLatch latch;
-		private final AtomicInteger stopped;
 
-		private PluginStopper(Plugin plugin, CountDownLatch latch,
-				AtomicInteger stopped) {
+		private PluginStopper(Plugin plugin, CountDownLatch latch) {
 			this.plugin = plugin;
 			this.latch = latch;
-			this.stopped = stopped;
 		}
 
 		public void run() {
 			try {
 				plugin.stop();
-				stopped.incrementAndGet();
 			} catch(IOException e) {
 				if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 			} finally {
diff --git a/briar-core/src/net/sf/briar/plugins/PluginsModule.java b/briar-core/src/net/sf/briar/plugins/PluginsModule.java
index 6b584b38ac2acc57047cbeeba0bd7149f44b543b..e93270b85ddff337c970ff88bbb0f47c0095df19 100644
--- a/briar-core/src/net/sf/briar/plugins/PluginsModule.java
+++ b/briar-core/src/net/sf/briar/plugins/PluginsModule.java
@@ -9,29 +9,44 @@ import java.util.concurrent.RejectedExecutionHandler;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 
+import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.plugins.PluginExecutor;
 import net.sf.briar.api.plugins.PluginManager;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
 import com.google.inject.Singleton;
 
 public class PluginsModule extends AbstractModule {
 
-	@Override
-	protected void configure() {
-		bind(PluginManager.class).to(
-				PluginManagerImpl.class).in(Singleton.class);
-		bind(Poller.class).to(PollerImpl.class);
+	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
-		ExecutorService e = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
+		pluginExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
 				60, SECONDS, queue, policy);
-		bind(Executor.class).annotatedWith(PluginExecutor.class).toInstance(e);
-		bind(ExecutorService.class).annotatedWith(
-				PluginExecutor.class).toInstance(e);
+	}
+
+	@Override
+	protected void configure() {
+		bind(Poller.class).to(PollerImpl.class);
+	}
+
+	@Provides @Singleton
+	PluginManager getPluginManager(LifecycleManager lifecycleManager,
+			PluginManagerImpl pluginManager) {
+		lifecycleManager.register(pluginManager);
+		return pluginManager;
+	}
+
+	@Provides @Singleton @PluginExecutor
+	Executor getPluginExecutor(LifecycleManager lifecycleManager) {
+		lifecycleManager.registerForShutdown(pluginExecutor);
+		return pluginExecutor;
 	}
 }
diff --git a/briar-core/src/net/sf/briar/reliability/ReliabilityModule.java b/briar-core/src/net/sf/briar/reliability/ReliabilityModule.java
index 37d4a610efa64c15a2b5568f93741a0c9f765f5b..a9ab994723946e89d6ec3b4b0ce125c15b95c4f3 100644
--- a/briar-core/src/net/sf/briar/reliability/ReliabilityModule.java
+++ b/briar-core/src/net/sf/briar/reliability/ReliabilityModule.java
@@ -9,28 +9,38 @@ import java.util.concurrent.RejectedExecutionHandler;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 
+import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.reliability.ReliabilityExecutor;
 import net.sf.briar.api.reliability.ReliabilityLayerFactory;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
 
 public class ReliabilityModule extends AbstractModule {
 
-	@Override
-	protected void configure() {
-		bind(ReliabilityLayerFactory.class).to(
-				ReliabilityLayerFactoryImpl.class);
+	private final ExecutorService reliabilityExecutor;
+
+	public ReliabilityModule() {
 		// 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
-		ExecutorService e = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
+		reliabilityExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
 				60, SECONDS, queue, policy);
-		bind(Executor.class).annotatedWith(
-				ReliabilityExecutor.class).toInstance(e);
-		bind(ExecutorService.class).annotatedWith(
-				ReliabilityExecutor.class).toInstance(e);
+	}
+
+	@Override
+	protected void configure() {
+		bind(ReliabilityLayerFactory.class).to(
+				ReliabilityLayerFactoryImpl.class);
+	}
+
+	@Provides @Singleton @ReliabilityExecutor
+	Executor getReliabilityExecutor(LifecycleManager lifecycleManager) {
+		lifecycleManager.registerForShutdown(reliabilityExecutor);
+		return reliabilityExecutor;
 	}
 }
diff --git a/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java b/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java
index 778607b9a308e97714fadaee70c9feae4bf417af..cb0fc9b63e87ce27ad3f4f358f2ff0d412d308c3 100644
--- a/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java
+++ b/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java
@@ -213,7 +213,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
 		return created;
 	}
 
-	public synchronized void stop() {
+	public synchronized boolean stop() {
 		db.removeListener(this);
 		timer.cancel();
 		connectionRecogniser.removeSecrets();
@@ -221,6 +221,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
 		removeAndEraseSecrets(oldSecrets);
 		removeAndEraseSecrets(currentSecrets);
 		removeAndEraseSecrets(newSecrets);
+		return true;
 	}
 
 	// Locking: this
diff --git a/briar-core/src/net/sf/briar/transport/TransportModule.java b/briar-core/src/net/sf/briar/transport/TransportModule.java
index 4985bc0654ecf61b69546f6b0c9b2c70a5fa6061..060db58944462719748e641f352bf307fdd82647 100644
--- a/briar-core/src/net/sf/briar/transport/TransportModule.java
+++ b/briar-core/src/net/sf/briar/transport/TransportModule.java
@@ -10,6 +10,7 @@ import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import net.sf.briar.api.crypto.KeyManager;
+import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.transport.ConnectionDispatcher;
 import net.sf.briar.api.transport.ConnectionReaderFactory;
 import net.sf.briar.api.transport.ConnectionRecogniser;
@@ -18,32 +19,46 @@ import net.sf.briar.api.transport.ConnectionWriterFactory;
 import net.sf.briar.api.transport.IncomingConnectionExecutor;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
 import com.google.inject.Singleton;
 
 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(
 				ConnectionReaderFactoryImpl.class);
-		bind(ConnectionRecogniser.class).to(ConnectionRecogniserImpl.class).in(
-				Singleton.class);
+		bind(ConnectionRecogniser.class).to(
+				ConnectionRecogniserImpl.class).in(Singleton.class);
 		bind(ConnectionRegistry.class).toInstance(new ConnectionRegistryImpl());
 		bind(ConnectionWriterFactory.class).to(
 				ConnectionWriterFactoryImpl.class);
-		bind(KeyManager.class).to(KeyManagerImpl.class).in(Singleton.class);
-		// 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
-		ExecutorService e = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
-				60, SECONDS, queue, policy);
-		bind(Executor.class).annotatedWith(
-				IncomingConnectionExecutor.class).toInstance(e);
-		bind(ExecutorService.class).annotatedWith(
-				IncomingConnectionExecutor.class).toInstance(e);
+	}
+
+	@Provides @Singleton
+	KeyManager getKeyManager(LifecycleManager lifecycleManager,
+			KeyManagerImpl keyManager) {
+		lifecycleManager.register(keyManager);
+		return keyManager;
+	}
+
+	@Provides @Singleton @IncomingConnectionExecutor
+	Executor getIncomingConnectionExecutor(LifecycleManager lifecycleManager) {
+		lifecycleManager.registerForShutdown(incomingConnectionExecutor);
+		return incomingConnectionExecutor;
 	}
 }
diff --git a/briar-tests/build.xml b/briar-tests/build.xml
index e2d8d749fba58a6d8356e154e8f0054195945bc4..655e6f80edbd4e7c6b3a48b5d6e4955ef31c3c64 100644
--- a/briar-tests/build.xml
+++ b/briar-tests/build.xml
@@ -55,7 +55,7 @@
 		</javac>
 	</target>
 	<target name='test' depends='compile'>
-		<junit showoutput='yes' printsummary='on' fork='yes' forkmode='once'>
+		<junit printsummary='on' fork='yes' forkmode='once'>
 			<assertions>
 				<enable/>
 			</assertions>
diff --git a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
index ee0be61085707cf7bc221b0e0a0efd8098a7691f..7d87dd82d7c5f46740167663cadfb430107044b3 100644
--- a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
+++ b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
@@ -44,12 +44,9 @@ import net.sf.briar.api.transport.ConnectionWriterFactory;
 import net.sf.briar.clock.ClockModule;
 import net.sf.briar.crypto.CryptoModule;
 import net.sf.briar.db.DatabaseModule;
-import net.sf.briar.lifecycle.LifecycleModule;
 import net.sf.briar.messaging.MessagingModule;
 import net.sf.briar.messaging.duplex.DuplexMessagingModule;
 import net.sf.briar.messaging.simplex.SimplexMessagingModule;
-import net.sf.briar.plugins.JavaSePluginsModule;
-import net.sf.briar.plugins.PluginsModule;
 import net.sf.briar.reliability.ReliabilityModule;
 import net.sf.briar.serial.SerialModule;
 import net.sf.briar.transport.TransportModule;
@@ -82,11 +79,10 @@ public class ProtocolIntegrationTest extends BriarTestCase {
 	public ProtocolIntegrationTest() throws Exception {
 		super();
 		Injector i = Guice.createInjector(new TestDatabaseModule(),
-				new TestUiModule(), new ClockModule(), new CryptoModule(),
-				new DatabaseModule(), new LifecycleModule(),
+				new TestLifecycleModule(), new TestUiModule(),
+				new ClockModule(), new CryptoModule(), new DatabaseModule(),
 				new MessagingModule(), new DuplexMessagingModule(),
-				new SimplexMessagingModule(), new PluginsModule(),
-				new JavaSePluginsModule(), new ReliabilityModule(),
+				new SimplexMessagingModule(), new ReliabilityModule(),
 				new SerialModule(), new TransportModule());
 		connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class);
 		connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);
diff --git a/briar-tests/src/net/sf/briar/TestLifecycleModule.java b/briar-tests/src/net/sf/briar/TestLifecycleModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..0517d7fc7c7689531171b8e00c9fc020a193c860
--- /dev/null
+++ b/briar-tests/src/net/sf/briar/TestLifecycleModule.java
@@ -0,0 +1,42 @@
+package net.sf.briar;
+
+import java.util.concurrent.ExecutorService;
+
+import net.sf.briar.api.lifecycle.LifecycleManager;
+import net.sf.briar.api.lifecycle.Service;
+import net.sf.briar.api.lifecycle.ShutdownManager;
+
+import com.google.inject.AbstractModule;
+
+public class TestLifecycleModule extends AbstractModule {
+
+	@Override
+	protected void configure() {
+		bind(LifecycleManager.class).toInstance(new LifecycleManager() {
+
+			public void register(Service s) {}
+
+			public void registerForShutdown(ExecutorService e) {}
+
+			public void startServices() {}
+
+			public void stopServices() {}
+
+			public void waitForDatabase() throws InterruptedException {}
+
+			public void waitForStartup() throws InterruptedException {}
+
+			public void waitForShutdown() throws InterruptedException {}
+		});
+		bind(ShutdownManager.class).toInstance(new ShutdownManager() {
+
+			public int addShutdownHook(Runnable hook) {
+				return 0;
+			}
+
+			public boolean removeShutdownHook(int handle) {
+				return true;
+			}
+		});
+	}
+}
diff --git a/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java b/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java
index c85f2d313e3c941ad17e6439d9602b6eb1ee2f9e..d7aa694f64a759d9e0c81885a8272a5f917734e9 100644
--- a/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java
+++ b/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java
@@ -20,6 +20,8 @@ import java.util.Collection;
 import java.util.Random;
 
 import net.sf.briar.BriarTestCase;
+import net.sf.briar.TestDatabaseModule;
+import net.sf.briar.TestLifecycleModule;
 import net.sf.briar.TestUtils;
 import net.sf.briar.api.Author;
 import net.sf.briar.api.AuthorFactory;
@@ -40,7 +42,11 @@ import net.sf.briar.api.messaging.SubscriptionUpdate;
 import net.sf.briar.api.messaging.TransportUpdate;
 import net.sf.briar.clock.ClockModule;
 import net.sf.briar.crypto.CryptoModule;
+import net.sf.briar.db.DatabaseModule;
+import net.sf.briar.messaging.duplex.DuplexMessagingModule;
+import net.sf.briar.messaging.simplex.SimplexMessagingModule;
 import net.sf.briar.serial.SerialModule;
+import net.sf.briar.transport.TransportModule;
 
 import org.junit.Test;
 
@@ -56,9 +62,11 @@ public class ConstantsTest extends BriarTestCase {
 	private final PacketWriterFactory packetWriterFactory;
 
 	public ConstantsTest() throws Exception {
-		super();
-		Injector i = Guice.createInjector(new ClockModule(), new CryptoModule(),
-				new MessagingModule(), new SerialModule());
+		Injector i = Guice.createInjector(new TestDatabaseModule(),
+				new TestLifecycleModule(), new ClockModule(),
+				new CryptoModule(), new DatabaseModule(), new MessagingModule(),
+				new DuplexMessagingModule(), new SimplexMessagingModule(),
+				new SerialModule(), new TransportModule());
 		crypto = i.getInstance(CryptoComponent.class);
 		groupFactory = i.getInstance(GroupFactory.class);
 		authorFactory = i.getInstance(AuthorFactory.class);
diff --git a/briar-tests/src/net/sf/briar/messaging/PacketWriterImplTest.java b/briar-tests/src/net/sf/briar/messaging/PacketWriterImplTest.java
index 4cf06c4f50628b4e56bbda71ad862d76d786afbf..2ca4e40be0024325a8930757ca56cb4ca394ba95 100644
--- a/briar-tests/src/net/sf/briar/messaging/PacketWriterImplTest.java
+++ b/briar-tests/src/net/sf/briar/messaging/PacketWriterImplTest.java
@@ -5,13 +5,19 @@ import java.io.IOException;
 import java.util.BitSet;
 
 import net.sf.briar.BriarTestCase;
+import net.sf.briar.TestDatabaseModule;
+import net.sf.briar.TestLifecycleModule;
 import net.sf.briar.api.messaging.PacketWriter;
 import net.sf.briar.api.messaging.Request;
 import net.sf.briar.api.serial.SerialComponent;
 import net.sf.briar.api.serial.WriterFactory;
 import net.sf.briar.clock.ClockModule;
 import net.sf.briar.crypto.CryptoModule;
+import net.sf.briar.db.DatabaseModule;
+import net.sf.briar.messaging.duplex.DuplexMessagingModule;
+import net.sf.briar.messaging.simplex.SimplexMessagingModule;
 import net.sf.briar.serial.SerialModule;
+import net.sf.briar.transport.TransportModule;
 import net.sf.briar.util.StringUtils;
 
 import org.junit.Test;
@@ -28,8 +34,11 @@ public class PacketWriterImplTest extends BriarTestCase {
 
 	public PacketWriterImplTest() {
 		super();
-		Injector i = Guice.createInjector(new ClockModule(), new CryptoModule(),
-				new MessagingModule(), new SerialModule());
+		Injector i = Guice.createInjector(new TestDatabaseModule(),
+				new TestLifecycleModule(), new ClockModule(),
+				new CryptoModule(), new DatabaseModule(), new MessagingModule(),
+				new DuplexMessagingModule(), new SimplexMessagingModule(),
+				new SerialModule(), new TransportModule());
 		serial = i.getInstance(SerialComponent.class);
 		writerFactory = i.getInstance(WriterFactory.class);
 	}
diff --git a/briar-tests/src/net/sf/briar/messaging/simplex/OutgoingSimplexConnectionTest.java b/briar-tests/src/net/sf/briar/messaging/simplex/OutgoingSimplexConnectionTest.java
index 83476b8d58101afa2eaab0e56217283d4347d102..6dbf36984cadfd8e8bdee1b9381c542b5bb6fe84 100644
--- a/briar-tests/src/net/sf/briar/messaging/simplex/OutgoingSimplexConnectionTest.java
+++ b/briar-tests/src/net/sf/briar/messaging/simplex/OutgoingSimplexConnectionTest.java
@@ -13,6 +13,7 @@ import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
 import net.sf.briar.BriarTestCase;
+import net.sf.briar.TestLifecycleModule;
 import net.sf.briar.TestUtils;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
@@ -60,7 +61,6 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
 		context = new Mockery();
 		db = context.mock(DatabaseComponent.class);
 		Module testModule = new AbstractModule() {
-			@Override
 			public void configure() {
 				bind(DatabaseComponent.class).toInstance(db);
 				bind(Executor.class).annotatedWith(
@@ -68,10 +68,11 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
 								Executors.newCachedThreadPool());
 			}
 		};
-		Injector i = Guice.createInjector(testModule, new ClockModule(),
-				new CryptoModule(), new SerialModule(), new TransportModule(),
-				new SimplexMessagingModule(), new MessagingModule(),
-				new DuplexMessagingModule());
+		Injector i = Guice.createInjector(testModule,
+				new TestLifecycleModule(), new ClockModule(),
+				new CryptoModule(), new MessagingModule(),
+				new DuplexMessagingModule(), new SimplexMessagingModule(),
+				new SerialModule(), new TransportModule());
 		connRegistry = i.getInstance(ConnectionRegistry.class);
 		connWriterFactory = i.getInstance(ConnectionWriterFactory.class);
 		packetWriterFactory = i.getInstance(PacketWriterFactory.class);
diff --git a/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java b/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java
index 882245d81983a423ed5988dba1b938e6a86c88fd..fab66c510bf1971ead3cf1abf595fea4a8ae8276 100644
--- a/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java
+++ b/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java
@@ -9,7 +9,7 @@ import java.util.Random;
 
 import net.sf.briar.BriarTestCase;
 import net.sf.briar.TestDatabaseModule;
-import net.sf.briar.TestUiModule;
+import net.sf.briar.TestLifecycleModule;
 import net.sf.briar.TestUtils;
 import net.sf.briar.api.Author;
 import net.sf.briar.api.AuthorId;
@@ -35,13 +35,9 @@ import net.sf.briar.api.transport.Endpoint;
 import net.sf.briar.clock.ClockModule;
 import net.sf.briar.crypto.CryptoModule;
 import net.sf.briar.db.DatabaseModule;
-import net.sf.briar.lifecycle.LifecycleModule;
 import net.sf.briar.messaging.MessagingModule;
 import net.sf.briar.messaging.duplex.DuplexMessagingModule;
 import net.sf.briar.plugins.ImmediateExecutor;
-import net.sf.briar.plugins.JavaSePluginsModule;
-import net.sf.briar.plugins.PluginsModule;
-import net.sf.briar.reliability.ReliabilityModule;
 import net.sf.briar.serial.SerialModule;
 import net.sf.briar.transport.TransportModule;
 
@@ -85,11 +81,9 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
 
 	private Injector createInjector(File dir) {
 		return Guice.createInjector(new TestDatabaseModule(dir),
-				new TestUiModule(), new ClockModule(), new CryptoModule(),
-				new DatabaseModule(), new LifecycleModule(),
-				new MessagingModule(), new DuplexMessagingModule(),
-				new SimplexMessagingModule(), new PluginsModule(),
-				new JavaSePluginsModule(), new ReliabilityModule(),
+				new TestLifecycleModule(), new ClockModule(),
+				new CryptoModule(), new DatabaseModule(), new MessagingModule(),
+				new DuplexMessagingModule(), new SimplexMessagingModule(), 
 				new SerialModule(), new TransportModule());
 	}
 
diff --git a/briar-tests/src/net/sf/briar/plugins/PluginManagerImplTest.java b/briar-tests/src/net/sf/briar/plugins/PluginManagerImplTest.java
index d224b2f142bffc1fb55b33761cb4ad7b1b009df4..3a326c36cc3cf9ff9a57b6004ad684bb15a5786c 100644
--- a/briar-tests/src/net/sf/briar/plugins/PluginManagerImplTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/PluginManagerImplTest.java
@@ -121,8 +121,8 @@ public class PluginManagerImplTest extends BriarTestCase {
 				simplexPluginConfig, duplexPluginConfig, db, poller,
 				dispatcher, uiCallback);
 		// Two plugins should be started and stopped
-		assertEquals(2, p.start());
-		assertEquals(2, p.stop());
+		assertTrue(p.start());
+		assertTrue(p.stop());
 		context.assertIsSatisfied();
 	}
 }
diff --git a/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java b/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java
index 84234fa3ae8ab1ac1ca3e32ab0d24955104de805..952862e88bd37ed38fe8966946ea948fd4dde9c5 100644
--- a/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java
+++ b/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java
@@ -9,6 +9,7 @@ import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
 import java.io.ByteArrayInputStream;
 
 import net.sf.briar.BriarTestCase;
+import net.sf.briar.TestLifecycleModule;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.AuthenticatedCipher;
 import net.sf.briar.api.crypto.CryptoComponent;
@@ -34,7 +35,8 @@ public class IncomingEncryptionLayerTest extends BriarTestCase {
 
 	public IncomingEncryptionLayerTest() {
 		super();
-		Injector i = Guice.createInjector(new CryptoModule());
+		Injector i = Guice.createInjector(new CryptoModule(),
+				new TestLifecycleModule());
 		crypto = i.getInstance(CryptoComponent.class);
 		frameCipher = crypto.getFrameCipher();
 		frameKey = crypto.generateSecretKey();
diff --git a/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java b/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java
index 5c9e129fee84a40910fe11529a326a833f46594d..94797d389c52ec67c26695540804ee7988cf1d98 100644
--- a/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java
+++ b/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java
@@ -10,6 +10,7 @@ import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
 import java.io.ByteArrayOutputStream;
 
 import net.sf.briar.BriarTestCase;
+import net.sf.briar.TestLifecycleModule;
 import net.sf.briar.api.crypto.AuthenticatedCipher;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.ErasableKey;
@@ -34,7 +35,8 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase {
 
 	public OutgoingEncryptionLayerTest() {
 		super();
-		Injector i = Guice.createInjector(new CryptoModule());
+		Injector i = Guice.createInjector(new CryptoModule(),
+				new TestLifecycleModule());
 		crypto = i.getInstance(CryptoComponent.class);
 		frameCipher = crypto.getFrameCipher();
 		tag = new byte[TAG_LENGTH];
diff --git a/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java b/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java
index 493439897c52a0e3ead237af8baa13c968ddcd58..5e77a768fe1159c43ea402fc271f09fee56a96e9 100644
--- a/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java
+++ b/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java
@@ -11,6 +11,7 @@ import java.io.OutputStream;
 import java.util.Random;
 
 import net.sf.briar.BriarTestCase;
+import net.sf.briar.TestLifecycleModule;
 import net.sf.briar.TestUtils;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
@@ -45,13 +46,13 @@ public class TransportIntegrationTest extends BriarTestCase {
 	public TransportIntegrationTest() {
 		super();
 		Module testModule = new AbstractModule() {
-			@Override
 			public void configure() {
 				bind(ConnectionWriterFactory.class).to(
 						ConnectionWriterFactoryImpl.class);
 			}
 		};
-		Injector i = Guice.createInjector(testModule, new CryptoModule());
+		Injector i = Guice.createInjector(testModule, new CryptoModule(),
+				new TestLifecycleModule());
 		crypto = i.getInstance(CryptoComponent.class);
 		connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);
 		contactId = new ContactId(234);