diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index ca2e8b3340930a9e6567b50b412654c2d7bb4888..37fd1f17cda8f1618582ffbd62e33a5e9226aafa 100644
--- a/briar-android/src/org/briarproject/android/ActivityComponent.java
+++ b/briar-android/src/org/briarproject/android/ActivityComponent.java
@@ -13,6 +13,9 @@ import org.briarproject.android.forum.ShareForumActivity;
 import org.briarproject.android.forum.WriteForumPostActivity;
 import org.briarproject.android.fragment.BaseFragment;
 import org.briarproject.android.identity.CreateIdentityActivity;
+import org.briarproject.android.introduction.ContactChooserFragment;
+import org.briarproject.android.introduction.IntroductionActivity;
+import org.briarproject.android.introduction.IntroductionMessageFragment;
 import org.briarproject.android.invitation.AddContactActivity;
 import org.briarproject.android.keyagreement.ChooseIdentityFragment;
 import org.briarproject.android.keyagreement.KeyAgreementActivity;
@@ -74,6 +77,12 @@ public interface ActivityComponent {
 
 	void inject(ShowQrCodeFragment fragment);
 
+	void inject(IntroductionActivity activity);
+
+	void inject(ContactChooserFragment fragment);
+
+	void inject(IntroductionMessageFragment fragment);
+
 	@Named("ContactListFragment")
 	BaseFragment newContactListFragment();
 
@@ -85,4 +94,10 @@ public interface ActivityComponent {
 
 	@Named("ShowQrCodeFragment")
 	BaseFragment newShowQrCodeFragment();
+
+	@Named("ContactChooserFragment")
+	BaseFragment newContactChooserFragment();
+
+	@Named("IntroductionMessageFragment")
+	IntroductionMessageFragment newIntroductionMessageFragment();
 }
diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java
index 33e291abc37dd07b34e0fd141a09767f1f383018..33accc97024d629403cd73066a47f8244d17a3e8 100644
--- a/briar-android/src/org/briarproject/android/ActivityModule.java
+++ b/briar-android/src/org/briarproject/android/ActivityModule.java
@@ -19,6 +19,8 @@ import org.briarproject.android.controller.SetupController;
 import org.briarproject.android.controller.SetupControllerImp;
 import org.briarproject.android.controller.ConfigController;
 import org.briarproject.android.controller.ConfigControllerImp;
+import org.briarproject.android.introduction.ContactChooserFragment;
+import org.briarproject.android.introduction.IntroductionMessageFragment;
 import org.briarproject.android.keyagreement.ChooseIdentityFragment;
 import org.briarproject.android.keyagreement.ShowQrCodeFragment;
 
@@ -135,5 +137,20 @@ public class ActivityModule {
 		return fragment;
 	}
 
+	@Provides
+	@Named("ContactChooserFragment")
+	BaseFragment provideContactChooserFragment() {
+		ContactChooserFragment fragment = new ContactChooserFragment();
+		fragment.setArguments(new Bundle());
+		return fragment;
+	}
+
+	@Provides
+	@Named("IntroductionMessageFragment")
+	IntroductionMessageFragment provideIntroductionMessageFragment() {
+		IntroductionMessageFragment fragment = new IntroductionMessageFragment();
+		fragment.setArguments(new Bundle());
+		return fragment;
+	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/AndroidComponent.java b/briar-android/src/org/briarproject/android/AndroidComponent.java
index c5396da798e8ffcffb34ef4ace5bc2084307690b..623aba6e55561ac00e86ff3d8e8af25608fc5fed 100644
--- a/briar-android/src/org/briarproject/android/AndroidComponent.java
+++ b/briar-android/src/org/briarproject/android/AndroidComponent.java
@@ -17,6 +17,7 @@ import org.briarproject.api.forum.ForumPostFactory;
 import org.briarproject.api.forum.ForumSharingManager;
 import org.briarproject.api.identity.AuthorFactory;
 import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.introduction.IntroductionManager;
 import org.briarproject.api.invitation.InvitationTaskFactory;
 import org.briarproject.api.keyagreement.KeyAgreementTaskFactory;
 import org.briarproject.api.keyagreement.PayloadEncoder;
@@ -33,7 +34,6 @@ import org.briarproject.system.AndroidSystemModule;
 
 import java.util.concurrent.Executor;
 
-import javax.inject.Named;
 import javax.inject.Singleton;
 
 import dagger.Component;
@@ -95,6 +95,8 @@ public interface AndroidComponent extends CoreEagerSingletons {
 
 	PayloadParser payloadParser();
 
+	IntroductionManager introductionManager();
+
 	void inject(BriarService activity);
 
 	void inject(ContactChooserFragment fragment);
diff --git a/briar-android/src/org/briarproject/android/BaseActivity.java b/briar-android/src/org/briarproject/android/BaseActivity.java
index 7e1b7165a78b26f5ed20c1dcfc10a44100875f11..6ae3eb8c20168484be6b9e7a62e4259c3b53395a 100644
--- a/briar-android/src/org/briarproject/android/BaseActivity.java
+++ b/briar-android/src/org/briarproject/android/BaseActivity.java
@@ -2,9 +2,7 @@ package org.briarproject.android;
 
 import android.os.Bundle;
 import android.os.IBinder;
-import android.os.PersistableBundle;
 import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 
@@ -44,6 +42,10 @@ public abstract class BaseActivity extends AppCompatActivity {
 				.build();
 
 		injectActivity(activityComponent);
+
+		for (ActivityLifecycleController alc : lifecycleControllers) {
+			alc.onActivityCreate();
+		}
 	}
 
 	@Override
@@ -51,9 +53,9 @@ public abstract class BaseActivity extends AppCompatActivity {
 		super.onPostCreate(savedInstanceState);
 		// Post call used for controllers to ensure that the onCreate method
 		// override in inherited Activities has finished
-		for (ActivityLifecycleController alc : lifecycleControllers) {
-			alc.onActivityCreate();
-		}
+//		for (ActivityLifecycleController alc : lifecycleControllers) {
+//			alc.onActivityCreate();
+//		}
 	}
 
 	@Override
diff --git a/briar-android/src/org/briarproject/android/BriarActivity.java b/briar-android/src/org/briarproject/android/BriarActivity.java
index 9bd1e27dbfb2b48baf36d2817c90cd0ea44cad39..fca3fd12d59ea5f0be3fc367a5e55f28def1beae 100644
--- a/briar-android/src/org/briarproject/android/BriarActivity.java
+++ b/briar-android/src/org/briarproject/android/BriarActivity.java
@@ -4,18 +4,11 @@ import android.annotation.SuppressLint;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.IBinder;
 
-import org.briarproject.android.BriarService.BriarBinder;
-import org.briarproject.android.BriarService.BriarServiceConnection;
 import org.briarproject.android.controller.BriarController;
-import org.briarproject.android.controller.ResultHandler;
+import org.briarproject.android.controller.handler.UiResultHandler;
 import org.briarproject.android.panic.ExitActivity;
-import org.briarproject.api.db.DatabaseConfig;
-import org.briarproject.api.db.DatabaseExecutor;
-import org.briarproject.api.lifecycle.LifecycleManager;
 
-import java.util.concurrent.Executor;
 import java.util.logging.Logger;
 
 import javax.inject.Inject;
@@ -68,24 +61,14 @@ public abstract class BriarActivity extends BaseActivity {
 		}
 	}
 
-	@Override
-	public void onDestroy() {
-		super.onDestroy();
-		briarController.unbindService();
-	}
-
 	protected void signOut(final boolean removeFromRecentApps) {
-		briarController.signOut(new ResultHandler<Void, RuntimeException>() {
+		briarController.signOut(new UiResultHandler<Void>(this) {
+
 			@Override
-			public void onResult(Void result) {
+			public void onResultUi(Void result) {
 				if (removeFromRecentApps) startExitActivity();
 				else finishAndExit();
 			}
-
-			@Override
-			public void onException(RuntimeException exception) {
-				// TODO ?
-			}
 		});
 	}
 
@@ -111,10 +94,12 @@ public abstract class BriarActivity extends BaseActivity {
 		System.exit(0);
 	}
 
+	@Deprecated
 	public void runOnDbThread(final Runnable task) {
 		briarController.runOnDbThread(task);
 	}
 
+	@Deprecated
 	protected void finishOnUiThread() {
 		runOnUiThread(new Runnable() {
 			public void run() {
diff --git a/briar-android/src/org/briarproject/android/NavDrawerActivity.java b/briar-android/src/org/briarproject/android/NavDrawerActivity.java
index 8545044851d26e7039efcf1e9bf6ea432370fae2..49fb824c2f3c082ee17956b0039b31661dd081ba 100644
--- a/briar-android/src/org/briarproject/android/NavDrawerActivity.java
+++ b/briar-android/src/org/briarproject/android/NavDrawerActivity.java
@@ -10,7 +10,6 @@ import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.app.ActionBarDrawerToggle;
 import android.support.v7.widget.Toolbar;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -21,8 +20,8 @@ import android.widget.TextView;
 
 import org.briarproject.R;
 import org.briarproject.android.controller.NavDrawerController;
-import org.briarproject.android.controller.ResultHandler;
 import org.briarproject.android.controller.TransportStateListener;
+import org.briarproject.android.controller.handler.UiResultExceptionHandler;
 import org.briarproject.android.fragment.BaseFragment;
 import org.briarproject.android.util.CustomAnimations;
 import org.briarproject.api.TransportId;
@@ -160,14 +159,14 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
 
 	private void storeLocalAuthor(final LocalAuthor a) {
 		controller.storeLocalAuthor(a,
-				new ResultHandler<Void, DbException>() {
+				new UiResultExceptionHandler<Void, DbException>(this) {
 					@Override
-					public void onResult(Void result) {
+					public void onResultUi(Void result) {
 						hideLoadingScreen();
 					}
 
 					@Override
-					public void onException(DbException exception) {
+					public void onExceptionUi(DbException exception) {
 
 					}
 				});
diff --git a/briar-android/src/org/briarproject/android/PasswordActivity.java b/briar-android/src/org/briarproject/android/PasswordActivity.java
index 9f004527a628ce978f29b750f24079b21a174e6b..346b2f2cd96f5e69b1db0de6038d744c27b63913 100644
--- a/briar-android/src/org/briarproject/android/PasswordActivity.java
+++ b/briar-android/src/org/briarproject/android/PasswordActivity.java
@@ -3,6 +3,7 @@ package org.briarproject.android;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.design.widget.TextInputLayout;
 import android.support.v7.app.AlertDialog;
 import android.text.Editable;
@@ -16,9 +17,8 @@ import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
 import org.briarproject.R;
-import org.briarproject.android.controller.EncryptedKeyNullException;
 import org.briarproject.android.controller.PasswordController;
-import org.briarproject.android.controller.ResultHandler;
+import org.briarproject.android.controller.handler.UiResultHandler;
 import org.briarproject.android.util.AndroidUtils;
 
 import javax.inject.Inject;
@@ -36,13 +36,13 @@ public class PasswordActivity extends BaseActivity {
 	private EditText password;
 
 	@Inject
-	PasswordController passwordHelper;
+	PasswordController passwordController;
 
 	@Override
 	public void onCreate(Bundle state) {
 		super.onCreate(state);
 
-		if (!passwordHelper.initialized()) {
+		if (!passwordController.initialized()) {
 			clearSharedPrefsAndDeleteEverything();
 			return;
 		}
@@ -92,7 +92,7 @@ public class PasswordActivity extends BaseActivity {
 	}
 
 	private void clearSharedPrefsAndDeleteEverything() {
-		passwordHelper.clearPrefs();
+		passwordController.clearPrefs();
 		AndroidUtils.deleteAppData(this);
 		setResult(RESULT_CANCELED);
 		startActivity(new Intent(this, SetupActivity.class));
@@ -125,22 +125,17 @@ public class PasswordActivity extends BaseActivity {
 		hideSoftKeyboard(password);
 		signInButton.setVisibility(INVISIBLE);
 		progress.setVisibility(VISIBLE);
-		passwordHelper.validatePassword(password.getText().toString(),
-				new ResultHandler<Boolean, EncryptedKeyNullException>() {
+		passwordController.validatePassword(password.getText().toString(),
+				new UiResultHandler<Boolean>(this) {
 					@Override
-					public void onResult(Boolean result) {
-						if (result != null && result) {
+					public void onResultUi(@NonNull Boolean result) {
+						if (result) {
 							setResult(RESULT_OK);
 							finish();
 						} else {
 							tryAgain();
 						}
 					}
-
-					@Override
-					public void onException(EncryptedKeyNullException e) {
-						// TODO ?
-					}
 				});
 	}
 
diff --git a/briar-android/src/org/briarproject/android/SetupActivity.java b/briar-android/src/org/briarproject/android/SetupActivity.java
index 1aaba344c06d3eee69ee9735f4a167c447ca6ece..f54d1e861ac51342aacbdf84388844d9536b32c2 100644
--- a/briar-android/src/org/briarproject/android/SetupActivity.java
+++ b/briar-android/src/org/briarproject/android/SetupActivity.java
@@ -2,6 +2,7 @@ package org.briarproject.android;
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.design.widget.TextInputLayout;
 import android.text.Editable;
 import android.text.TextWatcher;
@@ -15,11 +16,10 @@ import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
 import org.briarproject.R;
-import org.briarproject.android.controller.ResultHandler;
 import org.briarproject.android.controller.SetupController;
+import org.briarproject.android.controller.handler.UiResultHandler;
 import org.briarproject.android.util.AndroidUtils;
 import org.briarproject.android.util.StrengthMeter;
-
 import org.briarproject.util.StringUtils;
 
 import javax.inject.Inject;
@@ -135,16 +135,10 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
 		final String nickname = nicknameEntry.getText().toString();
 		final String password = passwordEntry.getText().toString();
 		setupController.createIdentity(nickname, password,
-				new ResultHandler<Long, RuntimeException>() {
+				new UiResultHandler<Long>(this) {
 					@Override
-					public void onResult(Long result) {
-						if (result != null)
-							showMain(result);
-					}
-
-					@Override
-					public void onException(RuntimeException exception) {
-
+					public void onResultUi(@NonNull Long result) {
+						showMain(result);
 					}
 				});
 	}
diff --git a/briar-android/src/org/briarproject/android/controller/BriarController.java b/briar-android/src/org/briarproject/android/controller/BriarController.java
index edb035cb32f2ad70746b9403629601ceec40843b..342e19359b2e5b0adabee4e778ddedca91058bc1 100644
--- a/briar-android/src/org/briarproject/android/controller/BriarController.java
+++ b/briar-android/src/org/briarproject/android/controller/BriarController.java
@@ -1,6 +1,8 @@
 package org.briarproject.android.controller;
 
 
+import org.briarproject.android.controller.handler.ResultHandler;
+
 public interface BriarController extends ActivityLifecycleController {
 	void runOnDbThread(final Runnable task);
 
@@ -10,5 +12,5 @@ public interface BriarController extends ActivityLifecycleController {
 
 	boolean encryptionKey();
 
-	void signOut(ResultHandler<Void, RuntimeException> eventHandler);
+	void signOut(ResultHandler<Void> eventHandler);
 }
diff --git a/briar-android/src/org/briarproject/android/controller/BriarControllerImp.java b/briar-android/src/org/briarproject/android/controller/BriarControllerImp.java
index 789b27af46b8857e2d4fffd8efd5901edf473314..59ed95d3b02ba7993a925a99a1259be67b1c1ea7 100644
--- a/briar-android/src/org/briarproject/android/controller/BriarControllerImp.java
+++ b/briar-android/src/org/briarproject/android/controller/BriarControllerImp.java
@@ -7,6 +7,7 @@ import android.support.annotation.CallSuper;
 
 import org.briarproject.android.BriarService;
 import org.briarproject.android.BriarService.BriarServiceConnection;
+import org.briarproject.android.controller.handler.ResultHandler;
 import org.briarproject.api.db.DatabaseConfig;
 import org.briarproject.api.db.DatabaseExecutor;
 import org.briarproject.api.lifecycle.LifecycleManager;
@@ -75,7 +76,7 @@ public class BriarControllerImp implements BriarController {
 	}
 
 	@Override
-	public void signOut(final ResultHandler<Void, RuntimeException> eventHandler) {
+	public void signOut(final ResultHandler<Void> eventHandler) {
 		new Thread() {
 			@Override
 			public void run() {
@@ -90,14 +91,8 @@ public class BriarControllerImp implements BriarController {
 					service.waitForShutdown();
 				} catch (InterruptedException e) {
 					LOG.warning("Interrupted while waiting for service");
-					Thread.currentThread().interrupt();
 				}
-				activity.runOnUiThread(new Runnable() {
-					@Override
-					public void run() {
-						eventHandler.onResult(null);
-					}
-				});
+				eventHandler.onResult(null);
 			}
 		}.start();
 	}
diff --git a/briar-android/src/org/briarproject/android/controller/EncryptedKeyNullException.java b/briar-android/src/org/briarproject/android/controller/EncryptedKeyNullException.java
deleted file mode 100644
index 3da38efa493e4cf27937315e1c0c253ee793df8a..0000000000000000000000000000000000000000
--- a/briar-android/src/org/briarproject/android/controller/EncryptedKeyNullException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.briarproject.android.controller;
-
-public class EncryptedKeyNullException extends NullPointerException {
-
-	@Override
-	public String toString() {
-		return "Encrypted key can't be null";
-	}
-}
diff --git a/briar-android/src/org/briarproject/android/controller/NavDrawerController.java b/briar-android/src/org/briarproject/android/controller/NavDrawerController.java
index 13dfbcc29d63f052a702aeb2540236bb4beb79f9..2669cc45511e8f4fd5db69218f99b56cdefbec16 100644
--- a/briar-android/src/org/briarproject/android/controller/NavDrawerController.java
+++ b/briar-android/src/org/briarproject/android/controller/NavDrawerController.java
@@ -1,5 +1,6 @@
 package org.briarproject.android.controller;
 
+import org.briarproject.android.controller.handler.ResultExceptionHandler;
 import org.briarproject.api.TransportId;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.identity.LocalAuthor;
@@ -10,7 +11,7 @@ public interface NavDrawerController extends BriarController {
 	boolean transportRunning(TransportId transportId);
 
 	void storeLocalAuthor(LocalAuthor author,
-			ResultHandler<Void, DbException> resultHandler);
+			ResultExceptionHandler<Void, DbException> resultHandler);
 
 	LocalAuthor removeAuthorHandle(long handle);
 }
diff --git a/briar-android/src/org/briarproject/android/controller/NavDrawerControllerImp.java b/briar-android/src/org/briarproject/android/controller/NavDrawerControllerImp.java
index f6b76c543c9a659b1836d0fec23f9b94f8aea095..0a5d2c86550887142baabd24051957a91d0937e3 100644
--- a/briar-android/src/org/briarproject/android/controller/NavDrawerControllerImp.java
+++ b/briar-android/src/org/briarproject/android/controller/NavDrawerControllerImp.java
@@ -3,6 +3,7 @@ package org.briarproject.android.controller;
 import android.app.Activity;
 
 import org.briarproject.android.api.ReferenceManager;
+import org.briarproject.android.controller.handler.ResultExceptionHandler;
 import org.briarproject.api.TransportId;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.event.Event;
@@ -84,7 +85,8 @@ public class NavDrawerControllerImp extends BriarControllerImp
 		}
 	}
 
-	private void transportStateUpdate(final TransportId id, final boolean enabled) {
+	private void transportStateUpdate(final TransportId id,
+			final boolean enabled) {
 		activity.runOnUiThread(new Runnable() {
 			@Override
 			public void run() {
@@ -108,7 +110,8 @@ public class NavDrawerControllerImp extends BriarControllerImp
 
 	@Override
 	public void storeLocalAuthor(final LocalAuthor author,
-			final ResultHandler<Void, DbException> resultHandler) {
+			final ResultExceptionHandler<Void, DbException> resultHandler) {
+
 		runOnDbThread(new Runnable() {
 			public void run() {
 				try {
@@ -117,22 +120,11 @@ public class NavDrawerControllerImp extends BriarControllerImp
 					long duration = System.currentTimeMillis() - now;
 					if (LOG.isLoggable(INFO))
 						LOG.info("Storing author took " + duration + " ms");
-					activity.runOnUiThread(new Runnable() {
-						@Override
-						public void run() {
-							resultHandler.onResult(null);
-						}
-					});
+					resultHandler.onResult(null);
 				} catch (final DbException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
-
-					activity.runOnUiThread(new Runnable() {
-						@Override
-						public void run() {
-							resultHandler.onException(e);
-						}
-					});
+					resultHandler.onException(e);
 				}
 			}
 		});
diff --git a/briar-android/src/org/briarproject/android/controller/PasswordController.java b/briar-android/src/org/briarproject/android/controller/PasswordController.java
index b40ddb6703eef92961874f3d2a0bb5fa379c9e68..bf1c9b230ec1349c2fda9f076d6880c7dac1fc6e 100644
--- a/briar-android/src/org/briarproject/android/controller/PasswordController.java
+++ b/briar-android/src/org/briarproject/android/controller/PasswordController.java
@@ -1,6 +1,8 @@
 package org.briarproject.android.controller;
 
+import org.briarproject.android.controller.handler.ResultHandler;
+
 public interface PasswordController extends ConfigController {
 	void validatePassword(String password,
-			ResultHandler<Boolean, EncryptedKeyNullException> resultHandler);
+			ResultHandler<Boolean> resultHandler);
 }
diff --git a/briar-android/src/org/briarproject/android/controller/PasswordControllerImp.java b/briar-android/src/org/briarproject/android/controller/PasswordControllerImp.java
index ed86bb9bd11a8be8f4edb1d91e253f0ef2b415a0..8a85fc51be55bf89fd5b1f98310921c31f26fe48 100644
--- a/briar-android/src/org/briarproject/android/controller/PasswordControllerImp.java
+++ b/briar-android/src/org/briarproject/android/controller/PasswordControllerImp.java
@@ -2,6 +2,7 @@ package org.briarproject.android.controller;
 
 import android.app.Activity;
 
+import org.briarproject.android.controller.handler.ResultHandler;
 import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.crypto.CryptoExecutor;
 import org.briarproject.api.crypto.SecretKey;
@@ -29,35 +30,21 @@ public class PasswordControllerImp extends ConfigControllerImp
 
 	@Override
 	public void validatePassword(final String password,
-			final ResultHandler<Boolean, EncryptedKeyNullException> resultHandler) {
+			final ResultHandler<Boolean> resultHandler) {
 		final byte[] encrypted = getEncryptedKey();
-		if (encrypted == null) {
-			resultHandler.onException(new EncryptedKeyNullException());
-		}
 		cryptoExecutor.execute(new Runnable() {
 			public void run() {
 				byte[] key = crypto.decryptWithPassword(encrypted, password);
 				if (key == null) {
-					onPasswordValidated(false, resultHandler);
+					resultHandler.onResult(false);
 				} else {
 					databaseConfig.setEncryptionKey(new SecretKey(key));
-					onPasswordValidated(true, resultHandler);
+					resultHandler.onResult(true);
 				}
 			}
 		});
 	}
 
-	private void onPasswordValidated(final boolean validated,
-			final ResultHandler<Boolean, EncryptedKeyNullException> resultHandler) {
-		activity.runOnUiThread(new Runnable() {
-			@Override
-			public void run() {
-				resultHandler.onResult(validated);
-			}
-		});
-	}
-
-
 	private byte[] getEncryptedKey() {
 		String hex = getEncryptedDatabaseKey();
 		return hex == null ? null : StringUtils.fromHexString(hex);
diff --git a/briar-android/src/org/briarproject/android/controller/ResultHandler.java b/briar-android/src/org/briarproject/android/controller/ResultHandler.java
deleted file mode 100644
index 5f98763aaecdbdba5953261ead7d0b407eec5dd1..0000000000000000000000000000000000000000
--- a/briar-android/src/org/briarproject/android/controller/ResultHandler.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.briarproject.android.controller;
-
-public interface ResultHandler<R, E> {
-	void onResult(R result);
-	void onException(E exception);
-}
diff --git a/briar-android/src/org/briarproject/android/controller/SetupController.java b/briar-android/src/org/briarproject/android/controller/SetupController.java
index 884706c3f9c629d11569df78de24a0dc6b286f4d..476db0f5e8cefddf5fa661e5ad9249281a768730 100644
--- a/briar-android/src/org/briarproject/android/controller/SetupController.java
+++ b/briar-android/src/org/briarproject/android/controller/SetupController.java
@@ -1,8 +1,10 @@
 package org.briarproject.android.controller;
 
+import org.briarproject.android.controller.handler.ResultHandler;
+
 public interface SetupController {
 	float estimatePasswordStrength(String password);
 
 	void createIdentity(String nickname, String password,
-			ResultHandler<Long, RuntimeException> resultHandler);
+			ResultHandler<Long> resultHandler);
 }
diff --git a/briar-android/src/org/briarproject/android/controller/SetupControllerImp.java b/briar-android/src/org/briarproject/android/controller/SetupControllerImp.java
index 312502f42247b07427c409893405312988529e22..61a933e677188ea89440110582d2c75651e6232a 100644
--- a/briar-android/src/org/briarproject/android/controller/SetupControllerImp.java
+++ b/briar-android/src/org/briarproject/android/controller/SetupControllerImp.java
@@ -3,8 +3,8 @@ package org.briarproject.android.controller;
 import android.app.Activity;
 import android.content.SharedPreferences;
 
-import org.briarproject.android.BaseActivity;
 import org.briarproject.android.api.ReferenceManager;
+import org.briarproject.android.controller.handler.ResultHandler;
 import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.crypto.CryptoExecutor;
 import org.briarproject.api.crypto.KeyPair;
@@ -83,7 +83,7 @@ public class SetupControllerImp implements SetupController {
 
 	@Override
 	public void createIdentity(final String nickname, final String password,
-			final ResultHandler<Long, RuntimeException> resultHandler) {
+			final ResultHandler<Long> resultHandler) {
 		cryptoExecutor.execute(new Runnable() {
 			public void run() {
 				SecretKey key = crypto.generateSecretKey();
@@ -93,17 +93,6 @@ public class SetupControllerImp implements SetupController {
 				final LocalAuthor localAuthor = createLocalAuthor(nickname);
 				long handle = referenceManager.putReference(localAuthor,
 						LocalAuthor.class);
-				onIdentityCreated(handle, resultHandler);
-
-			}
-		});
-	}
-
-	private void onIdentityCreated(final long handle,
-			final ResultHandler<Long, RuntimeException> resultHandler) {
-		activity.runOnUiThread(new Runnable() {
-			@Override
-			public void run() {
 				resultHandler.onResult(handle);
 			}
 		});
diff --git a/briar-android/src/org/briarproject/android/controller/handler/ResultExceptionHandler.java b/briar-android/src/org/briarproject/android/controller/handler/ResultExceptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d3cb030109f11e730e54c6ff3ecffe2e8667d60
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/controller/handler/ResultExceptionHandler.java
@@ -0,0 +1,6 @@
+package org.briarproject.android.controller.handler;
+
+public interface ResultExceptionHandler<R, E extends Exception> {
+	void onResult(R result);
+	void onException(E exception);
+}
diff --git a/briar-android/src/org/briarproject/android/controller/handler/ResultHandler.java b/briar-android/src/org/briarproject/android/controller/handler/ResultHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..55516f9422162e458633532d60043989dfb6c58b
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/controller/handler/ResultHandler.java
@@ -0,0 +1,5 @@
+package org.briarproject.android.controller.handler;
+
+public interface ResultHandler<R> {
+	void onResult(R result);
+}
diff --git a/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java b/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..7d9c08f4fb2b1ef3ad67f0806186b9b965b292e1
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java
@@ -0,0 +1,33 @@
+package org.briarproject.android.controller.handler;
+
+import android.app.Activity;
+
+public abstract class UiResultExceptionHandler<R, E extends Exception>
+		implements ResultExceptionHandler<R, E> {
+
+	private final Activity activity;
+
+	public UiResultExceptionHandler(Activity activity) {
+		this.activity = activity;
+	}
+
+	public void onResult(final R result) {
+		activity.runOnUiThread(new Runnable() {
+			public void run() {
+				onResultUi(result);
+			}
+		});
+	}
+
+	public void onException(final E exception) {
+		activity.runOnUiThread(new Runnable() {
+			public void run() {
+				onExceptionUi(exception);
+			}
+		});
+	}
+
+	public abstract void onResultUi(R result);
+
+	public abstract void onExceptionUi(E exception);
+}
diff --git a/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java b/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..1998060a5c9ff4b736f0e4102e3237d19122f4b1
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java
@@ -0,0 +1,22 @@
+package org.briarproject.android.controller.handler;
+
+import android.app.Activity;
+
+public abstract class UiResultHandler<R> implements ResultHandler<R> {
+
+	private final Activity activity;
+
+	public UiResultHandler(Activity activity) {
+		this.activity = activity;
+	}
+
+	public void onResult(final R result) {
+		activity.runOnUiThread(new Runnable() {
+			public void run() {
+				onResultUi(result);
+			}
+		});
+	}
+
+	public abstract void onResultUi(R result);
+}
diff --git a/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java b/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java
index 11e30d6356ab9bfe2b27dffae8a251dc28a88d6d..7b1bcbb5cb90c6465841adf142e066ee8119e3e3 100644
--- a/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java
+++ b/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java
@@ -12,7 +12,6 @@ import android.view.View;
 import android.view.ViewGroup;
 
 import org.briarproject.R;
-import org.briarproject.android.AndroidComponent;
 import org.briarproject.android.contact.ContactListAdapter;
 import org.briarproject.android.contact.ContactListItem;
 import org.briarproject.android.contact.ConversationItem;
@@ -66,6 +65,11 @@ public class ContactChooserFragment extends BaseFragment {
 	@Inject
 	protected volatile ConnectionRegistry connectionRegistry;
 
+	@Inject
+	public ContactChooserFragment() {
+
+	}
+
 	@Override
 	public void onAttach(Context context) {
 		super.onAttach(context);
@@ -77,11 +81,6 @@ public class ContactChooserFragment extends BaseFragment {
 		}
 	}
 
-	@Override
-	public void injectActivity(AndroidComponent component) {
-		component.inject(this);
-	}
-
 	@Override
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
diff --git a/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java b/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java
index 2faee1a8ab22b30d7757d91702bca976a796eb6a..79d511291f0065a8db188f480b819e7a373116e3 100644
--- a/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java
+++ b/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java
@@ -3,6 +3,7 @@ package org.briarproject.android.introduction;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.transition.ChangeBounds;
 import android.transition.Fade;
@@ -10,7 +11,7 @@ import android.view.MenuItem;
 import android.view.View;
 
 import org.briarproject.R;
-import org.briarproject.android.AndroidComponent;
+import org.briarproject.android.ActivityComponent;
 import org.briarproject.android.BriarActivity;
 import org.briarproject.android.fragment.BaseFragment;
 import org.briarproject.api.contact.Contact;
@@ -33,15 +34,15 @@ public class IntroductionActivity extends BriarActivity implements
 		setContentView(R.layout.activity_introduction);
 
 		if (savedInstanceState == null) {
-			ContactChooserFragment chooserFragment =
-					new ContactChooserFragment();
 			getSupportFragmentManager().beginTransaction()
-					.add(R.id.introductionContainer, chooserFragment).commit();
+					.add(R.id.introductionContainer,
+							activityComponent.newContactChooserFragment())
+					.commit();
 		}
 	}
 
 	@Override
-	public void injectActivity(AndroidComponent component) {
+	public void injectActivity(ActivityComponent component) {
 		component.inject(this);
 	}
 
@@ -85,13 +86,14 @@ public class IntroductionActivity extends BriarActivity implements
 			final Contact c2) {
 
 		IntroductionMessageFragment messageFragment =
-				IntroductionMessageFragment
-						.newInstance(c1.getId().getInt(), c2.getId().getInt());
+				activityComponent.newIntroductionMessageFragment();
+		messageFragment.initBundle(c1.getId().getInt(), c2.getId().getInt());
 
 		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 			messageFragment.setSharedElementEnterTransition(new ChangeBounds());
 			messageFragment.setEnterTransition(new Fade());
-			messageFragment.setSharedElementReturnTransition(new ChangeBounds());
+			messageFragment
+					.setSharedElementReturnTransition(new ChangeBounds());
 		}
 
 		getSupportFragmentManager().beginTransaction()
diff --git a/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java b/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java
index ed0547da4e0b9eb8b69e7c8d6ea7433ba392e0b6..ed9c7b3c773e23306ba6ff01a050afbabe4757e4 100644
--- a/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java
+++ b/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java
@@ -13,7 +13,6 @@ import android.widget.TextView;
 import android.widget.Toast;
 
 import org.briarproject.R;
-import org.briarproject.android.AndroidComponent;
 import org.briarproject.android.fragment.BaseFragment;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.contact.Contact;
@@ -49,18 +48,19 @@ public class IntroductionMessageFragment extends BaseFragment {
 	@Inject
 	protected volatile IntroductionManager introductionManager;
 
-	public static IntroductionMessageFragment newInstance(int contactId1,
-			int contactId2) {
-		IntroductionMessageFragment f = new IntroductionMessageFragment();
-
+	public void initBundle(int contactId1, int contactId2) {
 		Bundle args = new Bundle();
 		args.putInt(CONTACT_ID_1, contactId1);
 		args.putInt(CONTACT_ID_2, contactId2);
-		f.setArguments(args);
+		this.setArguments(args);
+	}
+
+	@Inject
+	public IntroductionMessageFragment() {
 
-		return f;
 	}
 
+
 	@Override
 	public void onAttach(Context context) {
 		super.onAttach(context);
@@ -72,11 +72,6 @@ public class IntroductionMessageFragment extends BaseFragment {
 		}
 	}
 
-	@Override
-	public void injectActivity(AndroidComponent component) {
-		component.inject(this);
-	}
-
 	@Override
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
@@ -183,7 +178,8 @@ public class IntroductionMessageFragment extends BaseFragment {
 				// actually make the introduction
 				try {
 					long timestamp = System.currentTimeMillis();
-					introductionManager.makeIntroduction(c1, c2, msg, timestamp);
+					introductionManager
+							.makeIntroduction(c1, c2, msg, timestamp);
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
diff --git a/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java b/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java
index 332efc6555b8d45baba7f9bd3be5c7e9604d05d6..acd8d36a14b4259ab09b09ad201ba00b62c98301 100644
--- a/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java
+++ b/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java
@@ -6,7 +6,6 @@ import android.widget.Toast;
 
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
-import org.briarproject.android.AndroidComponent;
 import org.briarproject.android.BriarActivity;
 import org.briarproject.android.api.ReferenceManager;
 import org.briarproject.api.crypto.CryptoComponent;
@@ -287,7 +286,7 @@ implements InvitationListener {
 				localInvitationCode, code);
 		taskHandle = referenceManager.putReference(task, InvitationTask.class);
 		task.addListener(AddContactActivity.this);
-		// Add a second listener so we can remove the first in onActivityDestroy(),
+		// Add a second listener so we can remove the first in onDestroy(),
 		// allowing the activity to be garbage collected if it's destroyed
 		task.addListener(new ReferenceCleaner(referenceManager, taskHandle));
 		task.connect();
diff --git a/briar-core/src/org/briarproject/introduction/IntroductionModule.java b/briar-core/src/org/briarproject/introduction/IntroductionModule.java
index 9e51aca734a7bec5c1317e413593cf2285861b45..4ea0ac6c3b5a9251d6b41db231f01f02cc3409fe 100644
--- a/briar-core/src/org/briarproject/introduction/IntroductionModule.java
+++ b/briar-core/src/org/briarproject/introduction/IntroductionModule.java
@@ -26,7 +26,7 @@ public class IntroductionModule {
 
 	@Provides
 	@Singleton
-	MessageValidator getValidator(MessageQueueManager messageQueueManager,
+	MessageValidator provideValidator(MessageQueueManager messageQueueManager,
 			IntroductionManager introductionManager,
 			MetadataEncoder metadataEncoder, ClientHelper clientHelper,
 			Clock clock) {
@@ -43,7 +43,7 @@ public class IntroductionModule {
 
 	@Provides
 	@Singleton
-	IntroductionManager getIntroductionManager(
+	IntroductionManager provideIntroductionManager(
 			LifecycleManager lifecycleManager,
 			ContactManager contactManager,
 			MessageQueueManager messageQueueManager,