diff --git a/briar-android/build.gradle b/briar-android/build.gradle
index 6ff778d86d62aa63fe035f7a0f434455f2dea65f..815c8de62e1100d526fb247df3e923631d250429 100644
--- a/briar-android/build.gradle
+++ b/briar-android/build.gradle
@@ -8,6 +8,7 @@ apply plugin: 'de.undercouch.download'
 
 repositories {
 	jcenter()
+	maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
 }
 
 dependencies {
@@ -44,6 +45,18 @@ dependencies {
 	compile 'com.google.zxing:core:3.2.1'
 	apt 'com.google.dagger:dagger-compiler:2.0.2'
 	provided 'javax.annotation:jsr250-api:1.0'
+
+	testCompile 'junit:junit:4.12'
+	testCompile 'net.jodah:concurrentunit:0.4.2'
+	testApt 'com.google.dagger:dagger-compiler:2.4'
+	testCompile project(path: ':briar-tests')
+	testCompile 'org.robolectric:robolectric:3.1-SNAPSHOT'
+	testCompile 'org.mockito:mockito-core:1.10.19'
+
+	androidTestCompile 'org.robolectric:robolectric:3.0'
+	androidTestCompile 'org.mockito:mockito-core:1.10.19'
+	androidTestApt 'com.google.dagger:dagger-compiler:2.4'
+	androidTestCompile 'junit:junit:4.12'
 }
 
 dependencyVerification {
@@ -81,7 +94,19 @@ android {
 		}
 
 		// Move the tests to tests/java, tests/res, etc...
-		instrumentTest.setRoot('tests')
+		//instrumentTest.setRoot('tests')
+//		unitTest.setRoot('tests')
+
+		androidTest.setRoot('androidTest')
+		androidTest {
+			java.srcDirs = ['androidTest/java']
+		}
+
+		test.setRoot('test')
+		test {
+			java.srcDirs = ['test/java']
+		}
+
 
 		// Move the build types to build-types/<type>
 		// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
diff --git a/briar-android/proguard-rules.txt b/briar-android/proguard-rules.txt
index 48d785a430ba139f85a88a0a71396a4b5b5671fc..df28bc5075f62e695960851810dc023df16c815a 100644
--- a/briar-android/proguard-rules.txt
+++ b/briar-android/proguard-rules.txt
@@ -48,6 +48,9 @@
 -keep class roboguice.** { *; }
 -keep class dagger.** { *; }
 -keep class com.google.** { *; }
+-keep class javax.** { *; }
+-keep class org.eclipse.** { *; }
+-keep class com.squareup.** { *; }
 
 -dontwarn org.h2.**
 -dontnote org.h2.**
diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java
index 33accc97024d629403cd73066a47f8244d17a3e8..70d3b00ab50423e47010f09743806733e548322c 100644
--- a/briar-android/src/org/briarproject/android/ActivityModule.java
+++ b/briar-android/src/org/briarproject/android/ActivityModule.java
@@ -54,34 +54,33 @@ public class ActivityModule {
 
 	@ActivityScope
 	@Provides
-	SetupController provideSetupController(
-			SetupControllerImp setupControllerImp) {
-		return setupControllerImp;
+	protected SetupController provideSetupController() {
+		return new SetupControllerImp();
 	}
 
 	@ActivityScope
 	@Provides
-	ConfigController provideConfigController(
+	protected ConfigController provideConfigController(
 			ConfigControllerImp configControllerImp) {
 		return configControllerImp;
 	}
 
 	@ActivityScope
 	@Provides
-	SharedPreferences provideSharedPreferences(Activity activity) {
+	protected SharedPreferences provideSharedPreferences(Activity activity) {
 		return activity.getSharedPreferences("db", Context.MODE_PRIVATE);
 	}
 
 	@ActivityScope
 	@Provides
-	PasswordController providePasswordController(
+	protected PasswordController providePasswordController(
 			PasswordControllerImp passwordControllerImp) {
 		return passwordControllerImp;
 	}
 
 	@ActivityScope
 	@Provides
-	BriarController provideBriarController(
+	protected BriarController provideBriarController(
 			BriarControllerImp briarControllerImp) {
 		activity.addLifecycleController(briarControllerImp);
 		return briarControllerImp;
@@ -89,7 +88,7 @@ public class ActivityModule {
 
 	@ActivityScope
 	@Provides
-	NavDrawerController provideNavDrawerController(
+	protected NavDrawerController provideNavDrawerController(
 			NavDrawerControllerImp navDrawerControllerImp) {
 		activity.addLifecycleController(navDrawerControllerImp);
 		if (activity instanceof TransportStateListener) {
@@ -101,7 +100,7 @@ public class ActivityModule {
 
 	@ActivityScope
 	@Provides
-	BriarServiceConnection provideBriarServiceConnection() {
+	protected BriarServiceConnection provideBriarServiceConnection() {
 		return new BriarServiceConnection();
 	}
 
diff --git a/briar-android/src/org/briarproject/android/BaseActivity.java b/briar-android/src/org/briarproject/android/BaseActivity.java
index e14eb29633248662d037fdae8d640b66d8d4ad1f..fa2e68d6b8e57d60d3f7d693a5b14fb0e7a72275 100644
--- a/briar-android/src/org/briarproject/android/BaseActivity.java
+++ b/briar-android/src/org/briarproject/android/BaseActivity.java
@@ -38,7 +38,7 @@ public abstract class BaseActivity extends AppCompatActivity {
 
 		activityComponent = DaggerActivityComponent.builder()
 				.androidComponent(applicationComponent)
-				.activityModule(new ActivityModule(this))
+				.activityModule(getActivityModule())
 				.build();
 
 		injectActivity(activityComponent);
@@ -48,6 +48,11 @@ public abstract class BaseActivity extends AppCompatActivity {
 		}
 	}
 
+	// This exists to make test overrides easier
+	protected ActivityModule getActivityModule() {
+		return new ActivityModule(this);
+	}
+
 	@Override
 	protected void onResume() {
 		super.onResume();
diff --git a/briar-android/src/org/briarproject/android/SetupActivity.java b/briar-android/src/org/briarproject/android/SetupActivity.java
index f54d1e861ac51342aacbdf84388844d9536b32c2..c3cb01cb31c1e6588835ff38cfdba9e983f7c41f 100644
--- a/briar-android/src/org/briarproject/android/SetupActivity.java
+++ b/briar-android/src/org/briarproject/android/SetupActivity.java
@@ -27,8 +27,6 @@ import javax.inject.Inject;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
-import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static org.briarproject.android.TestingConstants.PREVENT_SCREENSHOTS;
 import static org.briarproject.api.crypto.PasswordStrengthEstimator.WEAK;
 import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
 
@@ -66,7 +64,7 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
 		createAccountButton = (Button) findViewById(R.id.create_account);
 		progress = (ProgressBar) findViewById(R.id.progress_wheel);
 
-		if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE);
+//		if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE);
 
 		TextWatcher tw = new TextWatcher() {
 			@Override
@@ -125,6 +123,7 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
 
 	public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
 		hideSoftKeyboard(v);
+
 		return true;
 	}
 
diff --git a/briar-android/src/org/briarproject/system/AndroidSeedProvider.java b/briar-android/src/org/briarproject/system/AndroidSeedProvider.java
index c15e0b411a1efdb7e5805ea1e85acee4a4168592..749d1c68bc465c676eaea169a15557b50356d177 100644
--- a/briar-android/src/org/briarproject/system/AndroidSeedProvider.java
+++ b/briar-android/src/org/briarproject/system/AndroidSeedProvider.java
@@ -8,6 +8,7 @@ import android.provider.Settings;
 
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
@@ -15,6 +16,9 @@ import static android.provider.Settings.Secure.ANDROID_ID;
 
 class AndroidSeedProvider extends LinuxSeedProvider {
 
+	private static final Logger LOG =
+			Logger.getLogger(LinuxSeedProvider.class.getName());
+
 	private final Context appContext;
 
 	@Inject
diff --git a/briar-android/test/java/briarproject/IntroductionIntegrationTest.java b/briar-android/test/java/briarproject/IntroductionIntegrationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e92b545461dc428f5baeacf415e97b6c434a331c
--- /dev/null
+++ b/briar-android/test/java/briarproject/IntroductionIntegrationTest.java
@@ -0,0 +1,859 @@
+package briarproject;
+
+import net.jodah.concurrentunit.Waiter;
+
+import org.briarproject.BriarTestCase;
+import org.briarproject.TestDatabaseModule;
+import org.briarproject.TestUtils;
+import org.briarproject.api.contact.Contact;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.crypto.SecretKey;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.event.Event;
+import org.briarproject.api.event.EventListener;
+import org.briarproject.api.event.IntroductionAbortedEvent;
+import org.briarproject.api.event.IntroductionRequestReceivedEvent;
+import org.briarproject.api.event.IntroductionResponseReceivedEvent;
+import org.briarproject.api.event.IntroductionSucceededEvent;
+import org.briarproject.api.event.MessageValidatedEvent;
+import org.briarproject.api.identity.AuthorFactory;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.introduction.IntroductionManager;
+import org.briarproject.api.introduction.IntroductionRequest;
+import org.briarproject.api.introduction.SessionId;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.properties.TransportProperties;
+import org.briarproject.api.properties.TransportPropertyManager;
+import org.briarproject.api.sync.SyncSession;
+import org.briarproject.api.sync.SyncSessionFactory;
+import org.briarproject.api.system.Clock;
+import org.briarproject.contact.ContactModule;
+import org.briarproject.crypto.CryptoModule;
+import org.briarproject.introduction.IntroductionModule;
+import org.briarproject.lifecycle.LifecycleModule;
+import org.briarproject.properties.PropertiesModule;
+import org.briarproject.sync.SyncModule;
+import org.briarproject.transport.TransportModule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static org.briarproject.TestPluginsModule.MAX_LATENCY;
+import static org.briarproject.TestPluginsModule.TRANSPORT_ID;
+import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class IntroductionIntegrationTest extends BriarTestCase {
+
+	LifecycleManager lifecycleManager0, lifecycleManager1, lifecycleManager2;
+	SyncSessionFactory sync0, sync1, sync2;
+	ContactManager contactManager0, contactManager1, contactManager2;
+	ContactId contactId0, contactId1, contactId2;
+	IdentityManager identityManager0, identityManager1, identityManager2;
+	LocalAuthor author0, author1, author2;
+
+	@Inject
+	Clock clock;
+	@Inject
+	AuthorFactory authorFactory;
+
+	// objects accessed from background threads need to be volatile
+	private volatile IntroductionManager introductionManager0;
+	private volatile IntroductionManager introductionManager1;
+	private volatile IntroductionManager introductionManager2;
+	private volatile Waiter eventWaiter;
+	private volatile Waiter msgWaiter;
+
+	private final File testDir = TestUtils.getTestDirectory();
+	private final SecretKey master = TestUtils.getSecretKey();
+	private final int TIMEOUT = 15000;
+	private final String INTRODUCER = "Introducer";
+	private final String INTRODUCEE1 = "Introducee1";
+	private final String INTRODUCEE2 = "Introducee2";
+
+	private static final Logger LOG =
+			Logger.getLogger(IntroductionIntegrationTest.class.getName());
+
+	private IntroductionIntegrationTestComponent t0, t1, t2;
+
+	@Before
+	public void setUp() {
+		IntroductionIntegrationTestComponent component =
+				DaggerIntroductionIntegrationTestComponent.builder().build();
+		component.inject(this);
+		injectEagerSingletons(component);
+
+		assertTrue(testDir.mkdirs());
+		File t0Dir = new File(testDir, INTRODUCER);
+		t0 = DaggerIntroductionIntegrationTestComponent.builder()
+				.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
+		injectEagerSingletons(t0);
+		File t1Dir = new File(testDir, INTRODUCEE1);
+		t1 = DaggerIntroductionIntegrationTestComponent.builder()
+				.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
+		injectEagerSingletons(t1);
+		File t2Dir = new File(testDir, INTRODUCEE2);
+		t2 = DaggerIntroductionIntegrationTestComponent.builder()
+				.testDatabaseModule(new TestDatabaseModule(t2Dir)).build();
+		injectEagerSingletons(t2);
+
+		identityManager0 = t0.getIdentityManager();
+		identityManager1 = t1.getIdentityManager();
+		identityManager2 = t2.getIdentityManager();
+		contactManager0 = t0.getContactManager();
+		contactManager1 = t1.getContactManager();
+		contactManager2 = t2.getContactManager();
+		introductionManager0 = t0.getIntroductionManager();
+		introductionManager1 = t1.getIntroductionManager();
+		introductionManager2 = t2.getIntroductionManager();
+		sync0 = t0.getSyncSessionFactory();
+		sync1 = t1.getSyncSessionFactory();
+		sync2 = t2.getSyncSessionFactory();
+
+		// initialize waiters fresh for each test
+		eventWaiter = new Waiter();
+		msgWaiter = new Waiter();
+	}
+
+	@Test
+	public void testIntroductionSession() throws Exception {
+		startLifecycles();
+		try {
+			// Add Identities
+			addDefaultIdentities();
+
+			// Add Transport Properties
+			addTransportProperties();
+
+			// Add introducees as contacts
+			contactId1 = contactManager0.addContact(author1,
+					author0.getId(), master, clock.currentTimeMillis(), true,
+					true
+			);
+			contactId2 = contactManager0.addContact(author2,
+					author0.getId(), master, clock.currentTimeMillis(), true,
+					true
+			);
+			// Add introducer back
+			contactId0 = contactManager1.addContact(author0,
+					author1.getId(), master, clock.currentTimeMillis(), true,
+					true
+			);
+			ContactId contactId02 = contactManager2.addContact(author0,
+					author2.getId(), master, clock.currentTimeMillis(), true,
+					true
+			);
+			assertTrue(contactId0.equals(contactId02));
+
+			// listen to events
+			IntroducerListener listener0 = new IntroducerListener();
+			t0.getEventBus().addListener(listener0);
+			IntroduceeListener listener1 = new IntroduceeListener(1, true);
+			t1.getEventBus().addListener(listener1);
+			IntroduceeListener listener2 = new IntroduceeListener(2, true);
+			t2.getEventBus().addListener(listener2);
+
+			// make introduction
+			long time = clock.currentTimeMillis();
+			Contact introducee1 = contactManager0.getContact(contactId1);
+			Contact introducee2 = contactManager0.getContact(contactId2);
+			introductionManager0
+					.makeIntroduction(introducee1, introducee2, "Hi!", time);
+
+			// sync first request message
+			deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
+			eventWaiter.await(TIMEOUT, 1);
+			assertTrue(listener1.requestReceived);
+
+			// sync second request message
+			deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
+			eventWaiter.await(TIMEOUT, 1);
+			assertTrue(listener2.requestReceived);
+
+			// sync first response
+			deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
+			eventWaiter.await(TIMEOUT, 1);
+			assertTrue(listener0.response1Received);
+
+			// sync second response
+			deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
+			eventWaiter.await(TIMEOUT, 1);
+			assertTrue(listener0.response2Received);
+
+			// sync forwarded responses to introducees
+			deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
+			deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
+
+			// sync first ACK and its forward
+			deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
+			deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
+
+			// sync second ACK and its forward
+			deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
+			deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 2");
+
+			// wait for introduction to succeed
+			eventWaiter.await(TIMEOUT, 2);
+			assertTrue(listener1.succeeded);
+			assertTrue(listener2.succeeded);
+
+			assertTrue(contactManager1
+					.contactExists(author2.getId(), author1.getId()));
+			assertTrue(contactManager2
+					.contactExists(author1.getId(), author2.getId()));
+
+			assertDefaultUiMessages();
+		} finally {
+			stopLifecycles();
+		}
+	}
+
+	@Test
+	public void testIntroductionSessionFirstDecline() throws Exception {
+		startLifecycles();
+		try {
+			// Add Identities
+			addDefaultIdentities();
+
+			// Add Transport Properties
+			addTransportProperties();
+
+			// Add introducees as contacts
+			contactId1 = contactManager0.addContact(author1, author0.getId(),
+					master, clock.currentTimeMillis(), true, true
+			);
+			contactId2 = contactManager0.addContact(author2, author0.getId(),
+					master, clock.currentTimeMillis(), true, true
+			);
+			// Add introducer back
+			contactId0 = contactManager1.addContact(author0, author1.getId(),
+					master, clock.currentTimeMillis(), true, true
+			);
+			ContactId contactId02 = contactManager2.addContact(author0,
+					author2.getId(), master, clock.currentTimeMillis(), true,
+					true
+			);
+			assertTrue(contactId0.equals(contactId02));
+
+			// listen to events
+			IntroducerListener listener0 = new IntroducerListener();
+			t0.getEventBus().addListener(listener0);
+			IntroduceeListener listener1 = new IntroduceeListener(1, false);
+			t1.getEventBus().addListener(listener1);
+			IntroduceeListener listener2 = new IntroduceeListener(2, true);
+			t2.getEventBus().addListener(listener2);
+
+			// make introduction
+			long time = clock.currentTimeMillis();
+			Contact introducee1 = contactManager0.getContact(contactId1);
+			Contact introducee2 = contactManager0.getContact(contactId2);
+			introductionManager0
+					.makeIntroduction(introducee1, introducee2, null, time);
+
+			// sync request messages
+			deliverMessage(sync0, contactId0, sync1, contactId1);
+			deliverMessage(sync0, contactId0, sync2, contactId2);
+
+			// wait for requests to arrive
+			eventWaiter.await(TIMEOUT, 2);
+			assertTrue(listener1.requestReceived);
+			assertTrue(listener2.requestReceived);
+
+			// sync first response
+			deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
+			eventWaiter.await(TIMEOUT, 1);
+			assertTrue(listener0.response1Received);
+
+			// sync second response
+			deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
+			eventWaiter.await(TIMEOUT, 1);
+			assertTrue(listener0.response2Received);
+
+			// sync first forwarded response
+			deliverMessage(sync0, contactId0, sync2, contactId2);
+
+			// note how the introducer does not forward the second response,
+			// because after the first decline the protocol finished
+
+			assertFalse(listener1.succeeded);
+			assertFalse(listener2.succeeded);
+
+			assertFalse(contactManager1
+					.contactExists(author2.getId(), author1.getId()));
+			assertFalse(contactManager2
+					.contactExists(author1.getId(), author2.getId()));
+
+			assertEquals(2,
+					introductionManager0.getIntroductionMessages(contactId1)
+							.size());
+			assertEquals(2,
+					introductionManager0.getIntroductionMessages(contactId2)
+							.size());
+			assertEquals(2,
+					introductionManager1.getIntroductionMessages(contactId0)
+							.size());
+			// introducee2 should also have the decline response of introducee1
+			assertEquals(3,
+					introductionManager2.getIntroductionMessages(contactId0)
+							.size());
+		} finally {
+			stopLifecycles();
+		}
+	}
+
+	@Test
+	public void testIntroductionSessionSecondDecline() throws Exception {
+		startLifecycles();
+		try {
+			// Add Identities
+			addDefaultIdentities();
+
+			// Add Transport Properties
+			addTransportProperties();
+
+			// Add introducees as contacts
+			contactId1 = contactManager0.addContact(author1, author0.getId(),
+					master, clock.currentTimeMillis(), true, true
+			);
+			contactId2 = contactManager0.addContact(author2, author0.getId(),
+					master, clock.currentTimeMillis(), true, true
+			);
+			// Add introducer back
+			contactId0 = contactManager1.addContact(author0, author1.getId(),
+					master, clock.currentTimeMillis(), false, true
+			);
+			ContactId contactId02 = contactManager2.addContact(author0,
+					author2.getId(), master, clock.currentTimeMillis(), false,
+					true
+			);
+			assertTrue(contactId0.equals(contactId02));
+
+			// listen to events
+			IntroducerListener listener0 = new IntroducerListener();
+			t0.getEventBus().addListener(listener0);
+			IntroduceeListener listener1 = new IntroduceeListener(1, true);
+			t1.getEventBus().addListener(listener1);
+			IntroduceeListener listener2 = new IntroduceeListener(2, false);
+			t2.getEventBus().addListener(listener2);
+
+			// make introduction
+			long time = clock.currentTimeMillis();
+			Contact introducee1 = contactManager0.getContact(contactId1);
+			Contact introducee2 = contactManager0.getContact(contactId2);
+			introductionManager0
+					.makeIntroduction(introducee1, introducee2, null, time);
+
+			// sync request messages
+			deliverMessage(sync0, contactId0, sync1, contactId1);
+			deliverMessage(sync0, contactId0, sync2, contactId2);
+
+			// wait for requests to arrive
+			eventWaiter.await(TIMEOUT, 2);
+			assertTrue(listener1.requestReceived);
+			assertTrue(listener2.requestReceived);
+
+			// sync first response
+			deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
+			eventWaiter.await(TIMEOUT, 1);
+			assertTrue(listener0.response1Received);
+
+			// sync second response
+			deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
+			eventWaiter.await(TIMEOUT, 1);
+			assertTrue(listener0.response2Received);
+
+			// sync both forwarded response
+			deliverMessage(sync0, contactId0, sync2, contactId2);
+			deliverMessage(sync0, contactId0, sync1, contactId1);
+
+			assertFalse(contactManager1
+					.contactExists(author2.getId(), author1.getId()));
+			assertFalse(contactManager2
+					.contactExists(author1.getId(), author2.getId()));
+
+			assertEquals(2,
+					introductionManager0.getIntroductionMessages(contactId1)
+							.size());
+			assertEquals(2,
+					introductionManager0.getIntroductionMessages(contactId2)
+							.size());
+			// introducee1 also sees the decline response from introducee2
+			assertEquals(3,
+					introductionManager1.getIntroductionMessages(contactId0)
+							.size());
+			assertEquals(2,
+					introductionManager2.getIntroductionMessages(contactId0)
+							.size());
+		} finally {
+			stopLifecycles();
+		}
+	}
+
+	@Test
+	public void testIntroductionSessionDelayedFirstDecline() throws Exception {
+		startLifecycles();
+		try {
+			// Add Identities
+			addDefaultIdentities();
+
+			// Add Transport Properties
+			addTransportProperties();
+
+			// Add introducees as contacts
+			contactId1 = contactManager0.addContact(author1, author0.getId(),
+					master, clock.currentTimeMillis(), true, true
+			);
+			contactId2 = contactManager0.addContact(author2, author0.getId(),
+					master, clock.currentTimeMillis(), true, true
+			);
+			// Add introducer back
+			contactId0 = contactManager1.addContact(author0, author1.getId(),
+					master, clock.currentTimeMillis(), false, true
+			);
+			ContactId contactId02 = contactManager2.addContact(author0,
+					author2.getId(), master, clock.currentTimeMillis(), false,
+					true
+			);
+			assertTrue(contactId0.equals(contactId02));
+
+			// listen to events
+			IntroducerListener listener0 = new IntroducerListener();
+			t0.getEventBus().addListener(listener0);
+			IntroduceeListener listener1 = new IntroduceeListener(1, false);
+			t1.getEventBus().addListener(listener1);
+			IntroduceeListener listener2 = new IntroduceeListener(2, false);
+			t2.getEventBus().addListener(listener2);
+
+			// make introduction
+			long time = clock.currentTimeMillis();
+			Contact introducee1 = contactManager0.getContact(contactId1);
+			Contact introducee2 = contactManager0.getContact(contactId2);
+			introductionManager0
+					.makeIntroduction(introducee1, introducee2, null, time);
+
+			// sync request messages
+			deliverMessage(sync0, contactId0, sync1, contactId1);
+			deliverMessage(sync0, contactId0, sync2, contactId2);
+
+			// wait for requests to arrive
+			eventWaiter.await(TIMEOUT, 2);
+			assertTrue(listener1.requestReceived);
+			assertTrue(listener2.requestReceived);
+
+			// sync first response
+			deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
+			eventWaiter.await(TIMEOUT, 1);
+			assertTrue(listener0.response1Received);
+
+			// sync second response
+			deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
+			eventWaiter.await(TIMEOUT, 1);
+			assertTrue(listener0.response2Received);
+
+			// sync first forwarded response
+			deliverMessage(sync0, contactId0, sync2, contactId2);
+
+			// note how the second response will not be forwarded anymore
+
+			assertFalse(contactManager1
+					.contactExists(author2.getId(), author1.getId()));
+			assertFalse(contactManager2
+					.contactExists(author1.getId(), author2.getId()));
+
+			// since introducee2 was already in FINISHED state when
+			// introducee1's response arrived, she ignores and deletes it
+			assertDefaultUiMessages();
+		} finally {
+			stopLifecycles();
+		}
+	}
+
+	@Test
+	public void testIntroductionToSameContact() throws Exception {
+		startLifecycles();
+		try {
+			// Add Identities
+			addDefaultIdentities();
+
+			// Add Transport Properties
+			addTransportProperties();
+
+			// Add introducee as contact
+			contactId1 = contactManager0.addContact(author1, author0.getId(),
+					master, clock.currentTimeMillis(), true, true
+			);
+			// Add introducer back
+			contactId0 = contactManager1.addContact(author0, author1.getId(),
+					master, clock.currentTimeMillis(), true, true
+			);
+
+			// listen to events
+			IntroducerListener listener0 = new IntroducerListener();
+			t0.getEventBus().addListener(listener0);
+			IntroduceeListener listener1 = new IntroduceeListener(1, true);
+			t1.getEventBus().addListener(listener1);
+
+			// make introduction
+			long time = clock.currentTimeMillis();
+			Contact introducee1 = contactManager0.getContact(contactId1);
+			introductionManager0
+					.makeIntroduction(introducee1, introducee1, null, time);
+
+			// sync request messages
+			deliverMessage(sync0, contactId0, sync1, contactId1);
+
+			// we should not get any event, because the request will be discarded
+			assertFalse(listener1.requestReceived);
+
+			// make really sure we don't have that request
+			assertTrue(introductionManager1.getIntroductionMessages(contactId0)
+					.isEmpty());
+		} finally {
+			stopLifecycles();
+		}
+	}
+
+	@Test
+	public void testIntroductionToIdentitiesOfSameContact() throws Exception {
+		startLifecycles();
+		try {
+			// Add Identities
+			author0 = authorFactory.createLocalAuthor(INTRODUCER,
+					TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
+					TestUtils.getRandomBytes(123));
+			identityManager0.addLocalAuthor(author0);
+			author1 = authorFactory.createLocalAuthor(INTRODUCEE1,
+					TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
+					TestUtils.getRandomBytes(123));
+			identityManager1.addLocalAuthor(author1);
+			author2 = authorFactory.createLocalAuthor(INTRODUCEE2,
+					TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
+					TestUtils.getRandomBytes(123));
+			identityManager1.addLocalAuthor(author2);
+
+			// Add Transport Properties
+			addTransportProperties();
+
+			// Add introducees' authors as contacts
+			contactId1 = contactManager0.addContact(author1,
+					author0.getId(), master, clock.currentTimeMillis(), true,
+					true
+			);
+			contactId2 = contactManager0.addContact(author2,
+					author0.getId(), master, clock.currentTimeMillis(), true,
+					true
+			);
+			// Add introducer back
+			contactId0 = null;
+			ContactId contactId01 = contactManager1.addContact(author0,
+					author1.getId(), master, clock.currentTimeMillis(), false,
+					true
+			);
+			ContactId contactId02 = contactManager1.addContact(author0,
+					author2.getId(), master, clock.currentTimeMillis(), false,
+					true
+			);
+
+			// listen to events
+			IntroducerListener listener0 = new IntroducerListener();
+			t0.getEventBus().addListener(listener0);
+			IntroduceeListener listener1 = new IntroduceeListener(1, true);
+			t1.getEventBus().addListener(listener1);
+
+			// make introduction
+			long time = clock.currentTimeMillis();
+			Contact introducee1 = contactManager0.getContact(contactId1);
+			Contact introducee2 = contactManager0.getContact(contactId2);
+			introductionManager0
+					.makeIntroduction(introducee1, introducee2, "Hi!", time);
+
+			// sync request messages
+			deliverMessage(sync0, contactId01, sync1, contactId1);
+			deliverMessage(sync0, contactId02, sync1, contactId2);
+
+			// wait for request to arrive
+			eventWaiter.await(TIMEOUT, 2);
+			assertTrue(listener1.requestReceived);
+
+			// sync responses
+			deliverMessage(sync1, contactId1, sync0, contactId01);
+			deliverMessage(sync1, contactId2, sync0, contactId02);
+
+			// wait for two responses to arrive
+			eventWaiter.await(TIMEOUT, 2);
+			assertTrue(listener0.response1Received);
+			assertTrue(listener0.response2Received);
+
+			// sync forwarded responses to introducees
+			deliverMessage(sync0, contactId01, sync1, contactId1);
+			deliverMessage(sync0, contactId02, sync1, contactId2);
+
+			// wait for "both" introducees to abort session
+			eventWaiter.await(TIMEOUT, 2);
+			assertTrue(listener1.aborted);
+
+			// sync abort message
+			deliverMessage(sync1, contactId1, sync0, contactId01);
+			deliverMessage(sync1, contactId2, sync0, contactId02);
+
+			// wait for introducer to abort session (gets event twice)
+			eventWaiter.await(TIMEOUT, 2);
+			assertTrue(listener0.aborted);
+
+			assertFalse(contactManager1
+					.contactExists(author1.getId(), author2.getId()));
+			assertFalse(contactManager1
+					.contactExists(author2.getId(), author1.getId()));
+
+			assertEquals(2, introductionManager0.getIntroductionMessages(
+					contactId1).size());
+			assertEquals(2, introductionManager0.getIntroductionMessages(
+					contactId2).size());
+			assertEquals(2, introductionManager1.getIntroductionMessages(
+					contactId01).size());
+			assertEquals(2, introductionManager1.getIntroductionMessages(
+					contactId02).size());
+		} finally {
+			stopLifecycles();
+		}
+	}
+
+	// TODO add a test for faking responses when #256 is implemented
+
+	@After
+	public void tearDown() throws InterruptedException {
+		TestUtils.deleteTestDirectory(testDir);
+	}
+
+	private void startLifecycles() throws InterruptedException {
+		// Start the lifecycle manager and wait for it to finish
+		lifecycleManager0 = t0.getLifecycleManager();
+		lifecycleManager1 = t1.getLifecycleManager();
+		lifecycleManager2 = t2.getLifecycleManager();
+		lifecycleManager0.startServices();
+		lifecycleManager1.startServices();
+		lifecycleManager2.startServices();
+		lifecycleManager0.waitForStartup();
+		lifecycleManager1.waitForStartup();
+		lifecycleManager2.waitForStartup();
+	}
+
+	private void stopLifecycles() throws InterruptedException {
+		// Clean up
+		lifecycleManager0.stopServices();
+		lifecycleManager1.stopServices();
+		lifecycleManager2.stopServices();
+		lifecycleManager0.waitForShutdown();
+		lifecycleManager1.waitForShutdown();
+		lifecycleManager2.waitForShutdown();
+	}
+
+	private void addTransportProperties() throws DbException {
+		TransportPropertyManager tpm0 = t0.getTransportPropertyManager();
+		TransportPropertyManager tpm1 = t1.getTransportPropertyManager();
+		TransportPropertyManager tpm2 = t2.getTransportPropertyManager();
+
+		TransportProperties tp = new TransportProperties(
+				Collections.singletonMap("key", "value"));
+		tpm0.mergeLocalProperties(TRANSPORT_ID, tp);
+		tpm1.mergeLocalProperties(TRANSPORT_ID, tp);
+		tpm2.mergeLocalProperties(TRANSPORT_ID, tp);
+	}
+
+	private void addDefaultIdentities() throws DbException {
+		author0 = authorFactory.createLocalAuthor(INTRODUCER,
+				TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
+				TestUtils.getRandomBytes(123));
+		identityManager0.addLocalAuthor(author0);
+		author1 = authorFactory.createLocalAuthor(INTRODUCEE1,
+				TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
+				TestUtils.getRandomBytes(123));
+		identityManager1.addLocalAuthor(author1);
+		author2 = authorFactory.createLocalAuthor(INTRODUCEE2,
+				TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
+				TestUtils.getRandomBytes(123));
+		identityManager2.addLocalAuthor(author2);
+	}
+
+	private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
+			SyncSessionFactory toSync, ContactId toId)
+			throws IOException, TimeoutException {
+		deliverMessage(fromSync, fromId, toSync, toId, null);
+	}
+
+	private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
+			SyncSessionFactory toSync, ContactId toId, String debug)
+			throws IOException, TimeoutException {
+
+		if (debug != null) LOG.info("TEST: Sending message from " + debug);
+
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		// Create an outgoing sync session
+		SyncSession sessionFrom =
+				fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
+		// Write whatever needs to be written
+		sessionFrom.run();
+		out.close();
+
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		// Create an incoming sync session
+		SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
+		// Read whatever needs to be read
+		sessionTo.run();
+		in.close();
+
+		// wait for message to actually arrive
+		msgWaiter.await(TIMEOUT, 1);
+	}
+
+	private void assertDefaultUiMessages() throws DbException {
+		assertEquals(2, introductionManager0.getIntroductionMessages(
+				contactId1).size());
+		assertEquals(2, introductionManager0.getIntroductionMessages(
+				contactId2).size());
+		assertEquals(2, introductionManager1.getIntroductionMessages(
+				contactId0).size());
+		assertEquals(2, introductionManager2.getIntroductionMessages(
+				contactId0).size());
+	}
+
+	private class IntroduceeListener implements EventListener {
+
+		public volatile boolean requestReceived = false;
+		public volatile boolean succeeded = false;
+		public volatile boolean aborted = false;
+
+		private final int introducee;
+		private final boolean accept;
+
+		IntroduceeListener(int introducee, boolean accept) {
+			this.introducee = introducee;
+			this.accept = accept;
+		}
+
+		public void eventOccurred(Event e) {
+			if (e instanceof MessageValidatedEvent) {
+				MessageValidatedEvent event = (MessageValidatedEvent) e;
+				if (event.getClientId()
+						.equals(introductionManager0.getClientId()) &&
+						!event.isLocal()) {
+					LOG.info("TEST: Introducee" + introducee +
+							" received message in group " +
+							((MessageValidatedEvent) e).getMessage()
+									.getGroupId().hashCode());
+					msgWaiter.resume();
+				}
+			} else if (e instanceof IntroductionRequestReceivedEvent) {
+				IntroductionRequestReceivedEvent introEvent =
+						((IntroductionRequestReceivedEvent) e);
+				requestReceived = true;
+				IntroductionRequest ir = introEvent.getIntroductionRequest();
+				ContactId contactId = introEvent.getContactId();
+				SessionId sessionId = ir.getSessionId();
+				long time = clock.currentTimeMillis();
+				try {
+					if (introducee == 1) {
+						if (accept) {
+							introductionManager1
+									.acceptIntroduction(contactId, sessionId,
+											time);
+						} else {
+							introductionManager1
+									.declineIntroduction(contactId, sessionId,
+											time);
+						}
+					} else if (introducee == 2) {
+						if (accept) {
+							introductionManager2
+									.acceptIntroduction(contactId, sessionId,
+											time);
+						} else {
+							introductionManager2
+									.declineIntroduction(contactId, sessionId,
+											time);
+						}
+					}
+				} catch (DbException exception) {
+					eventWaiter.rethrow(exception);
+				} catch (IOException exception) {
+					eventWaiter.rethrow(exception);
+				} finally {
+					eventWaiter.resume();
+				}
+			} else if (e instanceof IntroductionSucceededEvent) {
+				succeeded = true;
+				Contact contact = ((IntroductionSucceededEvent) e).getContact();
+				eventWaiter.assertFalse(contact.getId().equals(contactId0));
+				eventWaiter.assertTrue(contact.isActive());
+				eventWaiter.resume();
+			} else if (e instanceof IntroductionAbortedEvent) {
+				aborted = true;
+				eventWaiter.resume();
+			}
+		}
+	}
+
+	private class IntroducerListener implements EventListener {
+
+		public volatile boolean response1Received = false;
+		public volatile boolean response2Received = false;
+		public volatile boolean aborted = false;
+
+		public void eventOccurred(Event e) {
+			if (e instanceof MessageValidatedEvent) {
+				MessageValidatedEvent event = (MessageValidatedEvent) e;
+				if (event.getClientId()
+						.equals(introductionManager0.getClientId()) &&
+						!event.isLocal()) {
+					LOG.info("TEST: Introducer received message in group " +
+							((MessageValidatedEvent) e).getMessage()
+									.getGroupId().hashCode());
+					msgWaiter.resume();
+				}
+			} else if (e instanceof IntroductionResponseReceivedEvent) {
+				ContactId c =
+						((IntroductionResponseReceivedEvent) e).getContactId();
+				try {
+					if (c.equals(contactId1)) {
+						response1Received = true;
+					} else if (c.equals(contactId2)) {
+						response2Received = true;
+					}
+				} finally {
+					eventWaiter.resume();
+				}
+			} else if (e instanceof IntroductionAbortedEvent) {
+				aborted = true;
+				eventWaiter.resume();
+			}
+		}
+	}
+
+	private void injectEagerSingletons(
+			IntroductionIntegrationTestComponent component) {
+
+		component.inject(new LifecycleModule.EagerSingletons());
+		component.inject(new LifecycleModule.EagerSingletons());
+		component.inject(new IntroductionModule.EagerSingletons());
+		component.inject(new CryptoModule.EagerSingletons());
+		component.inject(new ContactModule.EagerSingletons());
+		component.inject(new TransportModule.EagerSingletons());
+		component.inject(new SyncModule.EagerSingletons());
+		component.inject(new PropertiesModule.EagerSingletons());
+	}
+
+}
diff --git a/briar-android/test/java/briarproject/IntroductionIntegrationTestComponent.java b/briar-android/test/java/briarproject/IntroductionIntegrationTestComponent.java
new file mode 100644
index 0000000000000000000000000000000000000000..d86b50d7c9fc0421b51f628f024a0173e3e2a48f
--- /dev/null
+++ b/briar-android/test/java/briarproject/IntroductionIntegrationTestComponent.java
@@ -0,0 +1,80 @@
+package briarproject;
+
+import org.briarproject.TestDatabaseModule;
+import org.briarproject.TestPluginsModule;
+import org.briarproject.TestSystemModule;
+import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.event.EventBus;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.introduction.IntroductionManager;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.properties.TransportPropertyManager;
+import org.briarproject.api.sync.SyncSessionFactory;
+import org.briarproject.clients.ClientsModule;
+import org.briarproject.contact.ContactModule;
+import org.briarproject.crypto.CryptoModule;
+import org.briarproject.data.DataModule;
+import org.briarproject.db.DatabaseModule;
+import org.briarproject.event.EventModule;
+import org.briarproject.identity.IdentityModule;
+import org.briarproject.introduction.IntroductionModule;
+import org.briarproject.lifecycle.LifecycleModule;
+import org.briarproject.properties.PropertiesModule;
+import org.briarproject.sync.SyncModule;
+import org.briarproject.transport.TransportModule;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = {
+		TestSystemModule.class,
+		TestDatabaseModule.class,
+		TestPluginsModule.class,
+		LifecycleModule.class,
+		IntroductionModule.class,
+		DatabaseModule.class,
+		CryptoModule.class,
+		EventModule.class,
+		ContactModule.class,
+		IdentityModule.class,
+		TransportModule.class,
+		ClientsModule.class,
+		SyncModule.class,
+		DataModule.class,
+		PropertiesModule.class
+})
+public interface IntroductionIntegrationTestComponent {
+
+	void inject(IntroductionIntegrationTest testCase);
+
+	void inject(ContactModule.EagerSingletons init);
+
+	void inject(CryptoModule.EagerSingletons init);
+
+	void inject(IntroductionModule.EagerSingletons init);
+
+	void inject(LifecycleModule.EagerSingletons init);
+
+	void inject(PropertiesModule.EagerSingletons init);
+
+	void inject(SyncModule.EagerSingletons init);
+
+	void inject(TransportModule.EagerSingletons init);
+
+	LifecycleManager getLifecycleManager();
+
+	EventBus getEventBus();
+
+	IdentityManager getIdentityManager();
+
+	ContactManager getContactManager();
+
+	IntroductionManager getIntroductionManager();
+
+	TransportPropertyManager getTransportPropertyManager();
+
+	SyncSessionFactory getSyncSessionFactory();
+
+}
diff --git a/briar-android/test/java/briarproject/MessageSizeIntegrationTest.java b/briar-android/test/java/briarproject/MessageSizeIntegrationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d46c26f434423879ff9e8b1662b6198a37fddffa
--- /dev/null
+++ b/briar-android/test/java/briarproject/MessageSizeIntegrationTest.java
@@ -0,0 +1,90 @@
+package briarproject;
+
+import org.briarproject.BriarTestCase;
+import org.briarproject.TestUtils;
+import org.briarproject.api.UniqueId;
+import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.crypto.PrivateKey;
+import org.briarproject.api.forum.ForumConstants;
+import org.briarproject.api.forum.ForumPost;
+import org.briarproject.api.forum.ForumPostFactory;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.identity.AuthorFactory;
+import org.briarproject.api.messaging.MessagingConstants;
+import org.briarproject.api.messaging.PrivateMessage;
+import org.briarproject.api.messaging.PrivateMessageFactory;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.MessageId;
+import org.junit.Test;
+
+import javax.inject.Inject;
+
+import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
+import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
+import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
+import static org.briarproject.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH;
+import static org.briarproject.api.sync.SyncConstants.MAX_PACKET_PAYLOAD_LENGTH;
+import static org.junit.Assert.assertTrue;
+
+public class MessageSizeIntegrationTest extends BriarTestCase {
+
+	@Inject
+	CryptoComponent crypto;
+	@Inject
+	AuthorFactory authorFactory;
+	@Inject
+	PrivateMessageFactory privateMessageFactory;
+	@Inject
+	ForumPostFactory forumPostFactory;
+
+	public MessageSizeIntegrationTest() throws Exception {
+		MessageSizeIntegrationTestComponent component =
+				DaggerMessageSizeIntegrationTestComponent.builder().build();
+		component.inject(this);
+	}
+
+	@Test
+	public void testPrivateMessageFitsIntoPacket() throws Exception {
+		// Create a maximum-length private message
+		GroupId groupId = new GroupId(TestUtils.getRandomId());
+		long timestamp = Long.MAX_VALUE;
+		MessageId parent = new MessageId(TestUtils.getRandomId());
+		String contentType = TestUtils.getRandomString(
+				MessagingConstants.MAX_CONTENT_TYPE_LENGTH);
+		byte[] body = new byte[MAX_PRIVATE_MESSAGE_BODY_LENGTH];
+		PrivateMessage message = privateMessageFactory.createPrivateMessage(
+				groupId, timestamp, parent, contentType, body);
+		// Check the size of the serialised message
+		int length = message.getMessage().getRaw().length;
+		assertTrue(length > UniqueId.LENGTH + 8 + UniqueId.LENGTH
+				+ MessagingConstants.MAX_CONTENT_TYPE_LENGTH
+				+ MAX_PRIVATE_MESSAGE_BODY_LENGTH);
+		assertTrue(length <= MAX_PACKET_PAYLOAD_LENGTH);
+	}
+
+	@Test
+	public void testForumPostFitsIntoPacket() throws Exception {
+		// Create a maximum-length author
+		String authorName = TestUtils.getRandomString(
+				MAX_AUTHOR_NAME_LENGTH);
+		byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
+		Author author = authorFactory.createAuthor(authorName, authorPublic);
+		// Create a maximum-length forum post
+		GroupId groupId = new GroupId(TestUtils.getRandomId());
+		long timestamp = Long.MAX_VALUE;
+		MessageId parent = new MessageId(TestUtils.getRandomId());
+		String contentType = TestUtils.getRandomString(
+				ForumConstants.MAX_CONTENT_TYPE_LENGTH);
+		byte[] body = new byte[MAX_FORUM_POST_BODY_LENGTH];
+		PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
+		ForumPost post = forumPostFactory.createPseudonymousPost(groupId,
+				timestamp, parent, author, contentType, body, privateKey);
+		// Check the size of the serialised message
+		int length = post.getMessage().getRaw().length;
+		assertTrue(length > UniqueId.LENGTH + 8 + UniqueId.LENGTH
+				+ MAX_AUTHOR_NAME_LENGTH + MAX_PUBLIC_KEY_LENGTH
+				+ ForumConstants.MAX_CONTENT_TYPE_LENGTH
+				+ MAX_FORUM_POST_BODY_LENGTH);
+		assertTrue(length <= MAX_PACKET_PAYLOAD_LENGTH);
+	}
+}
diff --git a/briar-android/test/java/briarproject/MessageSizeIntegrationTestComponent.java b/briar-android/test/java/briarproject/MessageSizeIntegrationTestComponent.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f112fb6b90fba0275edf85e81494ea37116284f
--- /dev/null
+++ b/briar-android/test/java/briarproject/MessageSizeIntegrationTestComponent.java
@@ -0,0 +1,37 @@
+package briarproject;
+
+import org.briarproject.TestDatabaseModule;
+import org.briarproject.TestLifecycleModule;
+import org.briarproject.TestSystemModule;
+import org.briarproject.clients.ClientsModule;
+import org.briarproject.crypto.CryptoModule;
+import org.briarproject.data.DataModule;
+import org.briarproject.db.DatabaseModule;
+import org.briarproject.event.EventModule;
+import org.briarproject.forum.ForumModule;
+import org.briarproject.identity.IdentityModule;
+import org.briarproject.messaging.MessagingModule;
+import org.briarproject.sync.SyncModule;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = {
+		TestDatabaseModule.class,
+		TestLifecycleModule.class,
+		TestSystemModule.class,
+		ClientsModule.class,
+		CryptoModule.class,
+		DataModule.class,
+		DatabaseModule.class,
+		EventModule.class,
+		ForumModule.class,
+		IdentityModule.class,
+		MessagingModule.class,
+		SyncModule.class
+})
+public interface MessageSizeIntegrationTestComponent {
+	void inject(MessageSizeIntegrationTest testCase);
+}
diff --git a/briar-android/test/java/briarproject/SimplexMessagingIntegrationTest.java b/briar-android/test/java/briarproject/SimplexMessagingIntegrationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..53e7d03a3a6b9bf59f61be5d568823b1b927e3c9
--- /dev/null
+++ b/briar-android/test/java/briarproject/SimplexMessagingIntegrationTest.java
@@ -0,0 +1,193 @@
+package briarproject;
+
+import org.briarproject.BriarTestCase;
+import org.briarproject.TestDatabaseModule;
+import org.briarproject.TestUtils;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.crypto.SecretKey;
+import org.briarproject.api.event.Event;
+import org.briarproject.api.event.EventListener;
+import org.briarproject.api.event.MessageAddedEvent;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.identity.AuthorId;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.messaging.MessagingManager;
+import org.briarproject.api.messaging.PrivateMessage;
+import org.briarproject.api.messaging.PrivateMessageFactory;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.SyncSession;
+import org.briarproject.api.sync.SyncSessionFactory;
+import org.briarproject.api.transport.KeyManager;
+import org.briarproject.api.transport.StreamContext;
+import org.briarproject.api.transport.StreamReaderFactory;
+import org.briarproject.api.transport.StreamWriterFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static org.briarproject.TestPluginsModule.MAX_LATENCY;
+import static org.briarproject.TestPluginsModule.TRANSPORT_ID;
+import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
+import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class SimplexMessagingIntegrationTest extends BriarTestCase {
+
+	private final File testDir = TestUtils.getTestDirectory();
+	private final File aliceDir = new File(testDir, "alice");
+	private final File bobDir = new File(testDir, "bob");
+	private final SecretKey master = TestUtils.getSecretKey();
+	private final long timestamp = System.currentTimeMillis();
+	private final AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
+	private final AuthorId bobId = new AuthorId(TestUtils.getRandomId());
+
+	private SimplexMessagingIntegrationTestComponent alice, bob;
+
+	@Before
+	public void setUp() {
+		assertTrue(testDir.mkdirs());
+		alice = DaggerSimplexMessagingIntegrationTestComponent.builder()
+				.testDatabaseModule(new TestDatabaseModule(aliceDir)).build();
+		bob = DaggerSimplexMessagingIntegrationTestComponent.builder()
+				.testDatabaseModule(new TestDatabaseModule(bobDir)).build();
+	}
+
+	@Test
+	public void testWriteAndRead() throws Exception {
+		read(write());
+	}
+
+	private byte[] write() throws Exception {
+		// Instantiate Alice's services
+		LifecycleManager lifecycleManager = alice.getLifecycleManager();
+		IdentityManager identityManager = alice.getIdentityManager();
+		ContactManager contactManager = alice.getContactManager();
+		MessagingManager messagingManager = alice.getMessagingManager();
+		KeyManager keyManager = alice.getKeyManager();
+		PrivateMessageFactory privateMessageFactory =
+				alice.getPrivateMessageFactory();
+		StreamWriterFactory streamWriterFactory =
+				alice.getStreamWriterFactory();
+		SyncSessionFactory syncSessionFactory = alice.getSyncSessionFactory();
+
+		// Start the lifecycle manager
+		lifecycleManager.startServices();
+		lifecycleManager.waitForStartup();
+		// Add an identity for Alice
+		LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice",
+				new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp);
+		identityManager.addLocalAuthor(aliceAuthor);
+		// Add Bob as a contact
+		Author bobAuthor = new Author(bobId, "Bob",
+				new byte[MAX_PUBLIC_KEY_LENGTH]);
+		ContactId contactId = contactManager.addContact(bobAuthor, aliceId,
+				master, timestamp, true, true);
+
+		// Send Bob a message
+		GroupId groupId = messagingManager.getConversationId(contactId);
+		byte[] body = "Hi Bob!".getBytes("UTF-8");
+		PrivateMessage message = privateMessageFactory.createPrivateMessage(
+				groupId, timestamp, null, "text/plain", body);
+		messagingManager.addLocalMessage(message);
+		// Get a stream context
+		StreamContext ctx = keyManager.getStreamContext(contactId,
+				TRANSPORT_ID);
+		assertNotNull(ctx);
+		// Create a stream writer
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		OutputStream streamWriter = streamWriterFactory.createStreamWriter(
+				out, ctx);
+		// Create an outgoing sync session
+		SyncSession session = syncSessionFactory.createSimplexOutgoingSession(
+				contactId, MAX_LATENCY, streamWriter);
+		// Write whatever needs to be written
+		session.run();
+		streamWriter.close();
+
+		// Clean up
+		lifecycleManager.stopServices();
+		lifecycleManager.waitForShutdown();
+
+		// Return the contents of the stream
+		return out.toByteArray();
+	}
+
+	private void read(byte[] stream) throws Exception {
+		// Instantiate Bob's services
+		LifecycleManager lifecycleManager = bob.getLifecycleManager();
+		IdentityManager identityManager = bob.getIdentityManager();
+		ContactManager contactManager = bob.getContactManager();
+		KeyManager keyManager = bob.getKeyManager();
+		StreamReaderFactory streamReaderFactory = bob.getStreamReaderFactory();
+		SyncSessionFactory syncSessionFactory = bob.getSyncSessionFactory();
+		// Bob needs a MessagingManager even though we're not using it directly
+		bob.getMessagingManager();
+
+		// Start the lifecyle manager
+		lifecycleManager.startServices();
+		lifecycleManager.waitForStartup();
+		// Add an identity for Bob
+		LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob",
+				new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp);
+		identityManager.addLocalAuthor(bobAuthor);
+		// Add Alice as a contact
+		Author aliceAuthor = new Author(aliceId, "Alice",
+				new byte[MAX_PUBLIC_KEY_LENGTH]);
+		ContactId contactId = contactManager.addContact(aliceAuthor, bobId,
+				master, timestamp, false, true);
+
+		// Set up an event listener
+		MessageListener listener = new MessageListener();
+		bob.getEventBus().addListener(listener);
+		// Read and recognise the tag
+		ByteArrayInputStream in = new ByteArrayInputStream(stream);
+		byte[] tag = new byte[TAG_LENGTH];
+		int read = in.read(tag);
+		assertEquals(tag.length, read);
+		StreamContext ctx = keyManager.getStreamContext(TRANSPORT_ID, tag);
+		assertNotNull(ctx);
+		// Create a stream reader
+		InputStream streamReader = streamReaderFactory.createStreamReader(
+				in, ctx);
+		// Create an incoming sync session
+		SyncSession session = syncSessionFactory.createIncomingSession(
+				contactId, streamReader);
+		// No messages should have been added yet
+		assertFalse(listener.messageAdded);
+		// Read whatever needs to be read
+		session.run();
+		streamReader.close();
+		// The private message from Alice should have been added
+		assertTrue(listener.messageAdded);
+
+		// Clean up
+		lifecycleManager.stopServices();
+		lifecycleManager.waitForShutdown();
+	}
+
+	@After
+	public void tearDown() {
+		TestUtils.deleteTestDirectory(testDir);
+	}
+
+	private static class MessageListener implements EventListener {
+
+		private volatile boolean messageAdded = false;
+
+		public void eventOccurred(Event e) {
+			if (e instanceof MessageAddedEvent) messageAdded = true;
+		}
+	}
+}
diff --git a/briar-android/test/java/briarproject/SimplexMessagingIntegrationTestComponent.java b/briar-android/test/java/briarproject/SimplexMessagingIntegrationTestComponent.java
new file mode 100644
index 0000000000000000000000000000000000000000..7691b81924be4882a19a2e26500345cd722e2436
--- /dev/null
+++ b/briar-android/test/java/briarproject/SimplexMessagingIntegrationTestComponent.java
@@ -0,0 +1,74 @@
+package briarproject;
+
+import org.briarproject.TestDatabaseModule;
+import org.briarproject.TestPluginsModule;
+import org.briarproject.TestSystemModule;
+import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.event.EventBus;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.messaging.MessagingManager;
+import org.briarproject.api.messaging.PrivateMessageFactory;
+import org.briarproject.api.sync.SyncSessionFactory;
+import org.briarproject.api.transport.KeyManager;
+import org.briarproject.api.transport.StreamReaderFactory;
+import org.briarproject.api.transport.StreamWriterFactory;
+import org.briarproject.clients.ClientsModule;
+import org.briarproject.contact.ContactModule;
+import org.briarproject.crypto.CryptoModule;
+import org.briarproject.data.DataModule;
+import org.briarproject.db.DatabaseModule;
+import org.briarproject.event.EventModule;
+import org.briarproject.identity.IdentityModule;
+import org.briarproject.lifecycle.LifecycleModule;
+import org.briarproject.messaging.MessagingModule;
+import org.briarproject.plugins.PluginsModule;
+import org.briarproject.sync.SyncModule;
+import org.briarproject.transport.TransportModule;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = {
+		TestDatabaseModule.class,
+		TestPluginsModule.class,
+		TestSystemModule.class,
+		ClientsModule.class,
+		ContactModule.class,
+		CryptoModule.class,
+		DataModule.class,
+		DatabaseModule.class,
+		EventModule.class,
+		IdentityModule.class,
+		LifecycleModule.class,
+		MessagingModule.class,
+		PluginsModule.class,
+		SyncModule.class,
+		TransportModule.class
+})
+public interface SimplexMessagingIntegrationTestComponent {
+
+	void inject(SimplexMessagingIntegrationTest testCase);
+
+	LifecycleManager getLifecycleManager();
+
+	IdentityManager getIdentityManager();
+
+	ContactManager getContactManager();
+
+	MessagingManager getMessagingManager();
+
+	KeyManager getKeyManager();
+
+	PrivateMessageFactory getPrivateMessageFactory();
+
+	EventBus getEventBus();
+
+	StreamWriterFactory getStreamWriterFactory();
+
+	StreamReaderFactory getStreamReaderFactory();
+
+	SyncSessionFactory getSyncSessionFactory();
+}
diff --git a/briar-android/test/java/briarproject/SyncIntegrationTest.java b/briar-android/test/java/briarproject/SyncIntegrationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d5dabfc33fe352d4af2511d0d3bd4f3c80a34b1
--- /dev/null
+++ b/briar-android/test/java/briarproject/SyncIntegrationTest.java
@@ -0,0 +1,169 @@
+package briarproject;
+
+import org.briarproject.BriarTestCase;
+import org.briarproject.TestUtils;
+import org.briarproject.api.TransportId;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.crypto.SecretKey;
+import org.briarproject.api.sync.Ack;
+import org.briarproject.api.sync.ClientId;
+import org.briarproject.api.sync.Group;
+import org.briarproject.api.sync.GroupFactory;
+import org.briarproject.api.sync.Message;
+import org.briarproject.api.sync.MessageFactory;
+import org.briarproject.api.sync.MessageId;
+import org.briarproject.api.sync.Offer;
+import org.briarproject.api.sync.PacketReader;
+import org.briarproject.api.sync.PacketReaderFactory;
+import org.briarproject.api.sync.PacketWriter;
+import org.briarproject.api.sync.PacketWriterFactory;
+import org.briarproject.api.sync.Request;
+import org.briarproject.api.transport.StreamContext;
+import org.briarproject.api.transport.StreamReaderFactory;
+import org.briarproject.api.transport.StreamWriterFactory;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+
+import javax.inject.Inject;
+
+import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
+import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class SyncIntegrationTest extends BriarTestCase {
+
+	@Inject
+	GroupFactory groupFactory;
+	@Inject
+	MessageFactory messageFactory;
+	@Inject
+	StreamReaderFactory streamReaderFactory;
+	@Inject
+	StreamWriterFactory streamWriterFactory;
+	@Inject
+	PacketReaderFactory packetReaderFactory;
+	@Inject
+	PacketWriterFactory packetWriterFactory;
+	@Inject
+	CryptoComponent crypto;
+
+	private final ContactId contactId;
+	private final TransportId transportId;
+	private final SecretKey tagKey, headerKey;
+	private final long streamNumber;
+	private final Message message, message1;
+	private final Collection<MessageId> messageIds;
+
+	public SyncIntegrationTest() throws Exception {
+
+		SyncIntegrationTestComponent component =
+				DaggerSyncIntegrationTestComponent.builder().build();
+		component.inject(this);
+
+		contactId = new ContactId(234);
+		transportId = new TransportId("id");
+		// Create the transport keys
+		tagKey = TestUtils.getSecretKey();
+		headerKey = TestUtils.getSecretKey();
+		streamNumber = 123;
+		// Create a group
+		ClientId clientId = new ClientId(TestUtils.getRandomId());
+		byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
+		Group group = groupFactory.createGroup(clientId, descriptor);
+		// Add two messages to the group
+		long timestamp = System.currentTimeMillis();
+		byte[] body = "Hello world".getBytes("UTF-8");
+		message = messageFactory.createMessage(group.getId(), timestamp, body);
+		message1 = messageFactory.createMessage(group.getId(), timestamp, body);
+		messageIds = Arrays.asList(message.getId(), message1.getId());
+	}
+
+	@Test
+	public void testWriteAndRead() throws Exception {
+		read(write());
+	}
+
+	private byte[] write() throws Exception {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
+				headerKey, streamNumber);
+		OutputStream streamWriter = streamWriterFactory.createStreamWriter(out,
+				ctx);
+		PacketWriter packetWriter = packetWriterFactory.createPacketWriter(
+				streamWriter);
+
+		packetWriter.writeAck(new Ack(messageIds));
+		packetWriter.writeMessage(message.getRaw());
+		packetWriter.writeMessage(message1.getRaw());
+		packetWriter.writeOffer(new Offer(messageIds));
+		packetWriter.writeRequest(new Request(messageIds));
+
+		streamWriter.flush();
+		return out.toByteArray();
+	}
+
+	private void read(byte[] connectionData) throws Exception {
+		// Calculate the expected tag
+		byte[] expectedTag = new byte[TAG_LENGTH];
+		crypto.encodeTag(expectedTag, tagKey, streamNumber);
+
+		// Read the tag
+		InputStream in = new ByteArrayInputStream(connectionData);
+		byte[] tag = new byte[TAG_LENGTH];
+		assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH));
+		assertArrayEquals(expectedTag, tag);
+
+		// Create the readers
+		StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
+				headerKey, 0);
+		InputStream streamReader = streamReaderFactory.createStreamReader(in,
+				ctx);
+		PacketReader packetReader = packetReaderFactory.createPacketReader(
+				streamReader);
+
+		// Read the ack
+		assertTrue(packetReader.hasAck());
+		Ack a = packetReader.readAck();
+		assertEquals(messageIds, a.getMessageIds());
+
+		// Read the messages
+		assertTrue(packetReader.hasMessage());
+		Message m = packetReader.readMessage();
+		checkMessageEquality(message, m);
+		assertTrue(packetReader.hasMessage());
+		m = packetReader.readMessage();
+		checkMessageEquality(message1, m);
+		assertFalse(packetReader.hasMessage());
+
+		// Read the offer
+		assertTrue(packetReader.hasOffer());
+		Offer o = packetReader.readOffer();
+		assertEquals(messageIds, o.getMessageIds());
+
+		// Read the request
+		assertTrue(packetReader.hasRequest());
+		Request req = packetReader.readRequest();
+		assertEquals(messageIds, req.getMessageIds());
+
+		in.close();
+	}
+
+	private void checkMessageEquality(Message m1, Message m2) {
+		assertArrayEquals(m1.getId().getBytes(), m2.getId().getBytes());
+		assertArrayEquals(m1.getGroupId().getBytes(),
+				m2.getGroupId().getBytes());
+		assertEquals(m1.getTimestamp(), m2.getTimestamp());
+		assertEquals(m1.getLength(), m2.getLength());
+		assertArrayEquals(m1.getRaw(), m2.getRaw());
+	}
+}
diff --git a/briar-android/test/java/briarproject/SyncIntegrationTestComponent.java b/briar-android/test/java/briarproject/SyncIntegrationTestComponent.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad3d4dfde1b5540991a2258a007ed7e7b84fd978
--- /dev/null
+++ b/briar-android/test/java/briarproject/SyncIntegrationTestComponent.java
@@ -0,0 +1,21 @@
+package briarproject;
+
+import org.briarproject.TestSystemModule;
+import org.briarproject.crypto.CryptoModule;
+import org.briarproject.sync.SyncModule;
+import org.briarproject.transport.TransportModule;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = {
+		TestSystemModule.class,
+		CryptoModule.class,
+		SyncModule.class,
+		TransportModule.class
+})
+public interface SyncIntegrationTestComponent {
+	void inject(SyncIntegrationTest testCase);
+}
diff --git a/briar-android/test/java/briarproject/activity/SetupActivityTest.java b/briar-android/test/java/briarproject/activity/SetupActivityTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a730cf91f83c3bb60383c0fd4344445e10ffac74
--- /dev/null
+++ b/briar-android/test/java/briarproject/activity/SetupActivityTest.java
@@ -0,0 +1,69 @@
+package briarproject.activity;
+
+import android.support.design.widget.TextInputLayout;
+import android.widget.Button;
+import android.widget.EditText;
+
+import org.briarproject.BuildConfig;
+import org.briarproject.R;
+import org.briarproject.android.ActivityModule;
+import org.briarproject.android.SetupActivity;
+import org.briarproject.android.util.StrengthMeter;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricGradleTestRunner;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = BuildConfig.class, sdk = 22)
+public class SetupActivityTest {
+
+	private SetupActivity setupActivity;
+	TextInputLayout nicknameEntryWrapper;
+	TextInputLayout passwordEntryWrapper;
+	TextInputLayout passwordConfirmationWrapper;
+	EditText nicknameEntry;
+	EditText passwordEntry;
+	EditText passwordConfirmation;
+	StrengthMeter strengthMeter;
+	Button createAccountButton;
+
+	class TestSetupActivity extends SetupActivity {
+
+		@Override
+		protected ActivityModule getActivityModule() {
+			return super.getActivityModule();
+		}
+	}
+
+	@Before
+	public void setUp() {
+		setupActivity = Robolectric.setupActivity(SetupActivity.class);
+		nicknameEntryWrapper = (TextInputLayout) setupActivity
+				.findViewById(R.id.nickname_entry_wrapper);
+		passwordEntryWrapper = (TextInputLayout) setupActivity
+				.findViewById(R.id.password_entry_wrapper);
+		passwordConfirmationWrapper = (TextInputLayout) setupActivity
+				.findViewById(R.id.password_confirm_wrapper);
+		nicknameEntry =
+				(EditText) setupActivity.findViewById(R.id.nickname_entry);
+		passwordEntry =
+				(EditText) setupActivity.findViewById(R.id.password_entry);
+		passwordConfirmation =
+				(EditText) setupActivity.findViewById(R.id.password_confirm);
+		strengthMeter =
+				(StrengthMeter) setupActivity.findViewById(R.id.strength_meter);
+		createAccountButton =
+				(Button) setupActivity.findViewById(R.id.create_account);
+
+	}
+
+	@Test
+	public void test() {
+
+
+
+	}
+}
diff --git a/briar-android/test/java/briarproject/activity/TestActivityModule.java b/briar-android/test/java/briarproject/activity/TestActivityModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5eba02f2950c960be5505884070564aff697a36
--- /dev/null
+++ b/briar-android/test/java/briarproject/activity/TestActivityModule.java
@@ -0,0 +1,19 @@
+package briarproject.activity;
+
+import org.briarproject.android.ActivityModule;
+import org.briarproject.android.BaseActivity;
+
+public class TestActivityModule extends ActivityModule {
+
+	public TestActivityModule(BaseActivity activity) {
+		super(activity);
+	}
+
+
+	/*
+	@Override
+	protected SetupController provideSetupController(
+			SetupControllerImp setupControllerImp) {
+	}
+	*/
+}
diff --git a/briar-android/test/java/briarproject/controller/SetupControllerTest.java b/briar-android/test/java/briarproject/controller/SetupControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8564a739395b4371a6b47b9becae8ad8b5b25144
--- /dev/null
+++ b/briar-android/test/java/briarproject/controller/SetupControllerTest.java
@@ -0,0 +1,17 @@
+package briarproject.controller;
+
+import org.briarproject.BriarTestCase;
+import org.briarproject.android.controller.SetupController;
+import org.junit.Before;
+
+import javax.inject.Inject;
+
+public class SetupControllerTest extends BriarTestCase {
+
+	@Inject
+	SetupController setupController;
+
+	@Before public void setUp() {
+
+	}
+}
diff --git a/briar-core/src/org/briarproject/system/LinuxSeedProvider.java b/briar-core/src/org/briarproject/system/LinuxSeedProvider.java
index 58b22d32adc29c43285a7c27aa6ce395b2e8af5d..730beef97af299b445c8179b39f9fc320dc4fc49 100644
--- a/briar-core/src/org/briarproject/system/LinuxSeedProvider.java
+++ b/briar-core/src/org/briarproject/system/LinuxSeedProvider.java
@@ -1,6 +1,6 @@
 package org.briarproject.system;
 
-import static java.util.logging.Level.WARNING;
+import org.briarproject.api.system.SeedProvider;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -13,7 +13,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.logging.Logger;
 
-import org.briarproject.api.system.SeedProvider;
+import static java.util.logging.Level.WARNING;
 
 class LinuxSeedProvider implements SeedProvider {
 
@@ -43,6 +43,8 @@ class LinuxSeedProvider implements SeedProvider {
 		} catch (IOException e) {
 			// On some devices /dev/urandom isn't writable - this isn't fatal
 			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+		} catch (NullPointerException e) {
+			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 		}
 		// Read the seed from the pool
 		try {