diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
index bd52792c5b7d0b427b073d69d9ddf9b7dfd621fe..b1352dda77a7f4e2a6b2bd040f53acd03456ad91 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
@@ -125,7 +125,7 @@ public class ContactListFragment extends BaseEventFragment {
 					long now = System.currentTimeMillis();
 					List<ContactListItem> contacts =
 							new ArrayList<ContactListItem>();
-					for (Contact c : contactManager.getContacts()) {
+					for (Contact c : contactManager.getActiveContacts()) {
 						try {
 							ContactId id = c.getId();
 							GroupId groupId =
diff --git a/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java b/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java
index 2d237711938c893aa0e12040503e9f451e73620b..59821f9a126f3efb651c304400fa66cdbed250fa 100644
--- a/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java
@@ -138,7 +138,7 @@ SelectContactsDialog.Listener {
 			public void run() {
 				try {
 					long now = System.currentTimeMillis();
-					contacts = contactManager.getContacts();
+					contacts = contactManager.getActiveContacts();
 					selected = forumSharingManager.getSharedWith(groupId);
 					long duration = System.currentTimeMillis() - now;
 					if (LOG.isLoggable(INFO))
diff --git a/briar-api/src/org/briarproject/api/contact/Contact.java b/briar-api/src/org/briarproject/api/contact/Contact.java
index 5c5b9b197e8ea868595c67d622d0387fab25fa77..12e1efcbfc464df6e094e157dc2cb078c137d477 100644
--- a/briar-api/src/org/briarproject/api/contact/Contact.java
+++ b/briar-api/src/org/briarproject/api/contact/Contact.java
@@ -8,11 +8,14 @@ public class Contact {
 	private final ContactId id;
 	private final Author author;
 	private final AuthorId localAuthorId;
+	private final boolean active;
 
-	public Contact(ContactId id, Author author, AuthorId localAuthorId) {
+	public Contact(ContactId id, Author author, AuthorId localAuthorId,
+			boolean active) {
 		this.id = id;
 		this.author = author;
 		this.localAuthorId = localAuthorId;
+		this.active = active;
 	}
 
 	public ContactId getId() {
@@ -27,6 +30,10 @@ public class Contact {
 		return localAuthorId;
 	}
 
+	public boolean isActive() {
+		return active;
+	}
+
 	@Override
 	public int hashCode() {
 		return id.hashCode();
diff --git a/briar-api/src/org/briarproject/api/contact/ContactManager.java b/briar-api/src/org/briarproject/api/contact/ContactManager.java
index ffc493477dca7244b785414726b819cb50c3c2e7..47095d2ba208553a979138480ec57cb37baf473d 100644
--- a/briar-api/src/org/briarproject/api/contact/ContactManager.java
+++ b/briar-api/src/org/briarproject/api/contact/ContactManager.java
@@ -19,17 +19,21 @@ public interface ContactManager {
 	 * Stores a contact associated with the given local and remote pseudonyms,
 	 * and returns an ID for the contact.
 	 */
-	ContactId addContact(Author remote, AuthorId local) throws DbException;
+	ContactId addContact(Author remote, AuthorId local, boolean active)
+			throws DbException;
 
 	/** Returns the contact with the given ID. */
 	Contact getContact(ContactId c) throws DbException;
 
-	/** Returns all contacts. */
-	Collection<Contact> getContacts() throws DbException;
+	/** Returns all active contacts. */
+	Collection<Contact> getActiveContacts() throws DbException;
 
 	/** Removes a contact and all associated state. */
 	void removeContact(ContactId c) throws DbException;
 
+	/** Marks a contact as active or inactive. */
+	void setContactActive(ContactId c, boolean active) throws DbException;
+
 	interface AddContactHook {
 		void addingContact(Transaction txn, Contact c) throws DbException;
 	}
diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
index ca24976942a3d6b7dbdaa160d6825e19a32fbfa6..08bc9692f9a9f9d77f3d2dbe4724c7d1a539c35a 100644
--- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
+++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
@@ -59,8 +59,8 @@ public interface DatabaseComponent {
 	 * Stores a contact associated with the given local and remote pseudonyms,
 	 * and returns an ID for the contact.
 	 */
-	ContactId addContact(Transaction txn, Author remote, AuthorId local)
-			throws DbException;
+	ContactId addContact(Transaction txn, Author remote, AuthorId local,
+			boolean active) throws DbException;
 
 	/**
 	 * Stores a group.
@@ -317,6 +317,12 @@ public interface DatabaseComponent {
 	 */
 	void removeTransport(Transaction txn, TransportId t) throws DbException;
 
+	/**
+	 * Marks the given contact as active or inactive.
+	 */
+	void setContactActive(Transaction txn, ContactId c, boolean active)
+		throws DbException;
+
 	/**
 	 * Marks the given message as shared or unshared.
 	 */
diff --git a/briar-api/src/org/briarproject/api/event/ContactStatusChangedEvent.java b/briar-api/src/org/briarproject/api/event/ContactStatusChangedEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..ddbdbd5db0514defee7ecb5203405efd4c1d2454
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/event/ContactStatusChangedEvent.java
@@ -0,0 +1,23 @@
+package org.briarproject.api.event;
+
+import org.briarproject.api.contact.ContactId;
+
+/** An event that is broadcast when a contact is marked active or inactive. */
+public class ContactStatusChangedEvent extends Event {
+
+	private final ContactId contactId;
+	private final boolean active;
+
+	public ContactStatusChangedEvent(ContactId contactId, boolean active) {
+		this.contactId = contactId;
+		this.active = active;
+	}
+
+	public ContactId getContactId() {
+		return contactId;
+	}
+
+	public boolean isActive() {
+		return active;
+	}
+}
diff --git a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java
index 2fafe8a168c4c22bdbb97f1d6143cf4ef520d24f..03a4eae72405d0a8c8ee5e8ebd1130522c96387e 100644
--- a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java
+++ b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java
@@ -13,7 +13,9 @@ import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook;
 import org.briarproject.api.identity.LocalAuthor;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -41,12 +43,12 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
 	}
 
 	@Override
-	public ContactId addContact(Author remote, AuthorId local)
+	public ContactId addContact(Author remote, AuthorId local, boolean active)
 			throws DbException {
 		ContactId c;
 		Transaction txn = db.startTransaction();
 		try {
-			c = db.addContact(txn, remote, local);
+			c = db.addContact(txn, remote, local, active);
 			Contact contact = db.getContact(txn, c);
 			for (AddContactHook hook : addHooks)
 				hook.addingContact(txn, contact);
@@ -71,7 +73,7 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
 	}
 
 	@Override
-	public Collection<Contact> getContacts() throws DbException {
+	public Collection<Contact> getActiveContacts() throws DbException {
 		Collection<Contact> contacts;
 		Transaction txn = db.startTransaction();
 		try {
@@ -80,7 +82,9 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
 		} finally {
 			db.endTransaction(txn);
 		}
-		return contacts;
+		List<Contact> active = new ArrayList<Contact>(contacts.size());
+		for (Contact c : contacts) if (c.isActive()) active.add(c);
+		return Collections.unmodifiableList(active);
 	}
 
 	@Override
@@ -94,6 +98,18 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
 		}
 	}
 
+	@Override
+	public void setContactActive(ContactId c, boolean active)
+			throws DbException {
+		Transaction txn = db.startTransaction();
+		try {
+			db.setContactActive(txn, c, active);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+	}
+
 	private void removeContact(Transaction txn, ContactId c)
 			throws DbException {
 		Contact contact = db.getContact(txn, c);
diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java
index 91d17b49df5abd4f1746afb17f62f83f954b659c..2544a96702e2b7b075cf55f4629b26c228e92dfe 100644
--- a/briar-core/src/org/briarproject/db/Database.java
+++ b/briar-core/src/org/briarproject/db/Database.java
@@ -64,7 +64,7 @@ interface Database<T> {
 	 * Stores a contact associated with the given local and remote pseudonyms,
 	 * and returns an ID for the contact.
 	 */
-	ContactId addContact(T txn, Author remote, AuthorId local)
+	ContactId addContact(T txn, Author remote, AuthorId local, boolean active)
 			throws DbException;
 
 	/**
@@ -188,11 +188,6 @@ interface Database<T> {
 	 */
 	Contact getContact(T txn, ContactId c) throws DbException;
 
-	/**
-	 * Returns the IDs of all contacts.
-	 */
-	Collection<ContactId> getContactIds(T txn) throws DbException;
-
 	/**
 	 * Returns all contacts.
 	 */
@@ -240,6 +235,11 @@ interface Database<T> {
 	 */
 	Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException;
 
+	/**
+	 * Returns the IDs of all messages in the given group.
+	 */
+	Collection<MessageId> getMessageIds(T txn, GroupId g) throws DbException;
+
 	/**
 	 * Returns the metadata for all messages in the given group.
 	 */
@@ -424,6 +424,12 @@ interface Database<T> {
 	void removeOfferedMessages(T txn, ContactId c,
 			Collection<MessageId> requested) throws DbException;
 
+	/**
+	 * Removes the status of the given message with respect to the given
+	 * contact.
+	 */
+	void removeStatus(T txn, ContactId c, MessageId m) throws DbException;
+
 	/**
 	 * Removes a transport (and all associated state) from the database.
 	 */
@@ -440,6 +446,12 @@ interface Database<T> {
 	 */
 	void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException;
 
+	/**
+	 * Marks the given contact as active or inactive.
+	 */
+	void setContactActive(T txn, ContactId c, boolean active)
+		throws DbException;
+
 	/**
 	 * Marks the given message as shared or unshared.
 	 */
diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
index 8ed1ece7857cbe02be91db7df2a8b13818d5dbcf..0980962b06d11b0228201359d783f7ddd7882200 100644
--- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
+++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
@@ -16,6 +16,7 @@ import org.briarproject.api.db.NoSuchTransportException;
 import org.briarproject.api.db.Transaction;
 import org.briarproject.api.event.ContactAddedEvent;
 import org.briarproject.api.event.ContactRemovedEvent;
+import org.briarproject.api.event.ContactStatusChangedEvent;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.GroupAddedEvent;
@@ -56,7 +57,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -70,12 +70,6 @@ import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN;
 import static org.briarproject.api.sync.ValidationManager.Validity.VALID;
 import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
 
-/**
- * An implementation of DatabaseComponent using reentrant read-write locks.
- * Depending on the JVM's lock implementation, this implementation may allow
- * writers to starve. LockFairnessTest can be used to test whether this
- * implementation is safe on a given JVM.
- */
 class DatabaseComponentImpl<T> implements DatabaseComponent {
 
 	private static final Logger LOG =
@@ -143,13 +137,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 	}
 
 	public ContactId addContact(Transaction transaction, Author remote,
-			AuthorId local) throws DbException {
+			AuthorId local, boolean active) throws DbException {
 		T txn = unbox(transaction);
 		if (!db.containsLocalAuthor(txn, local))
 			throw new NoSuchLocalAuthorException();
 		if (db.containsContact(txn, remote.getId(), local))
 			throw new ContactExistsException();
-		ContactId c = db.addContact(txn, remote, local);
+		ContactId c = db.addContact(txn, remote, local, active);
 		transaction.attach(new ContactAddedEvent(c));
 		return c;
 	}
@@ -177,7 +171,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		if (!db.containsGroup(txn, m.getGroupId()))
 			throw new NoSuchGroupException();
 		if (!db.containsMessage(txn, m.getId())) {
-			addMessage(txn, m, VALID, shared, null);
+			addMessage(txn, m, VALID, shared);
 			transaction.attach(new MessageAddedEvent(m, null));
 			transaction.attach(new MessageValidatedEvent(m, c, true, true));
 			if (shared) transaction.attach(new MessageSharedEvent(m));
@@ -185,26 +179,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		db.mergeMessageMetadata(txn, m.getId(), meta);
 	}
 
-	/**
-	 * Stores a message and initialises its status with respect to each contact.
-	 *
-	 * @param sender null for a locally generated message.
-	 */
-	private void addMessage(T txn, Message m, Validity validity, boolean shared,
-			ContactId sender) throws DbException {
+	private void addMessage(T txn, Message m, Validity validity, boolean shared)
+			throws DbException {
 		db.addMessage(txn, m, validity, shared);
-		GroupId g = m.getGroupId();
-		Collection<ContactId> visibility = db.getVisibility(txn, g);
-		visibility = new HashSet<ContactId>(visibility);
-		for (ContactId c : db.getContactIds(txn)) {
-			if (visibility.contains(c)) {
-				boolean offered = db.removeOfferedMessage(txn, c, m.getId());
-				boolean seen = offered || c.equals(sender);
-				db.addStatus(txn, c, m.getId(), offered, seen);
-			} else {
-				if (c.equals(sender)) throw new IllegalStateException();
-				db.addStatus(txn, c, m.getId(), false, false);
-			}
+		for (ContactId c : db.getVisibility(txn, m.getGroupId())) {
+			boolean offered = db.removeOfferedMessage(txn, c, m.getId());
+			db.addStatus(txn, c, m.getId(), offered, offered);
 		}
 	}
 
@@ -516,7 +496,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			throw new NoSuchContactException();
 		if (db.containsVisibleGroup(txn, c, m.getGroupId())) {
 			if (!db.containsMessage(txn, m.getId())) {
-				addMessage(txn, m, UNKNOWN, false, c);
+				addMessage(txn, m, UNKNOWN, false);
 				transaction.attach(new MessageAddedEvent(m, c));
 			}
 			db.raiseAckFlag(txn, c, m.getId());
@@ -529,8 +509,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		T txn = unbox(transaction);
 		if (!db.containsContact(txn, c))
 			throw new NoSuchContactException();
-		int count = db.countOfferedMessages(txn, c);
 		boolean ack = false, request = false;
+		int count = db.countOfferedMessages(txn, c);
 		for (MessageId m : o.getMessageIds()) {
 			if (db.containsVisibleMessage(txn, c, m)) {
 				db.raiseSeenFlag(txn, c, m);
@@ -601,6 +581,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		transaction.attach(new TransportRemovedEvent(t));
 	}
 
+	public void setContactActive(Transaction transaction, ContactId c,
+			boolean active) throws DbException {
+		T txn = unbox(transaction);
+		if (!db.containsContact(txn, c))
+			throw new NoSuchContactException();
+		db.setContactActive(txn, c, active);
+		transaction.attach(new ContactStatusChangedEvent(c, active));
+	}
+
 	public void setMessageShared(Transaction transaction, Message m,
 			boolean shared) throws DbException {
 		T txn = unbox(transaction);
@@ -638,8 +627,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		if (!db.containsGroup(txn, g))
 			throw new NoSuchGroupException();
 		boolean wasVisible = db.containsVisibleGroup(txn, c, g);
-		if (visible && !wasVisible) db.addVisibility(txn, c, g);
-		else if (!visible && wasVisible) db.removeVisibility(txn, c, g);
+		if (visible && !wasVisible) {
+			db.addVisibility(txn, c, g);
+			for (MessageId m : db.getMessageIds(txn, g)) {
+				boolean seen = db.removeOfferedMessage(txn, c, m);
+				db.addStatus(txn, c, m, seen, seen);
+			}
+		} else if (!visible && wasVisible) {
+			db.removeVisibility(txn, c, g);
+			for (MessageId m : db.getMessageIds(txn, g))
+				db.removeStatus(txn, c, m);
+		}
 		if (visible != wasVisible) {
 			List<ContactId> affected = Collections.singletonList(c);
 			transaction.attach(new GroupVisibilityUpdatedEvent(affected));
diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java
index 4af26f32006d3e0c44b62e05cc639757baad8a42..7fc836348386c6aa042532889700060295bb69cc 100644
--- a/briar-core/src/org/briarproject/db/JdbcDatabase.java
+++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java
@@ -63,15 +63,15 @@ import static org.briarproject.db.ExponentialBackoff.calculateExpiry;
  */
 abstract class JdbcDatabase implements Database<Connection> {
 
-	private static final int SCHEMA_VERSION = 21;
-	private static final int MIN_SCHEMA_VERSION = 21;
+	private static final int SCHEMA_VERSION = 22;
+	private static final int MIN_SCHEMA_VERSION = 22;
 
 	private static final String CREATE_SETTINGS =
 			"CREATE TABLE settings"
-					+ " (key VARCHAR NOT NULL,"
+					+ " (namespace VARCHAR NOT NULL,"
+					+ " key VARCHAR NOT NULL,"
 					+ " value VARCHAR NOT NULL,"
-					+ " namespace VARCHAR NOT NULL,"
-					+ " PRIMARY KEY (key, namespace))";
+					+ " PRIMARY KEY (namespace, key))";
 
 	private static final String CREATE_LOCAL_AUTHORS =
 			"CREATE TABLE localAuthors"
@@ -89,6 +89,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " name VARCHAR NOT NULL,"
 					+ " publicKey BINARY NOT NULL,"
 					+ " localAuthorId HASH NOT NULL,"
+					+ " active BOOLEAN NOT NULL,"
 					+ " PRIMARY KEY (contactId),"
 					+ " FOREIGN KEY (localAuthorId)"
 					+ " REFERENCES localAuthors (authorId)"
@@ -441,20 +442,21 @@ abstract class JdbcDatabase implements Database<Connection> {
 		return new DeviceId(StringUtils.fromHexString(s.get(DEVICE_ID_KEY)));
 	}
 
-	public ContactId addContact(Connection txn, Author remote, AuthorId local)
-			throws DbException {
+	public ContactId addContact(Connection txn, Author remote, AuthorId local,
+			boolean active) throws DbException {
 		PreparedStatement ps = null;
 		ResultSet rs = null;
 		try {
 			// Create a contact row
 			String sql = "INSERT INTO contacts"
-					+ " (authorId, name, publicKey, localAuthorId)"
-					+ " VALUES (?, ?, ?, ?)";
+					+ " (authorId, name, publicKey, localAuthorId, active)"
+					+ " VALUES (?, ?, ?, ?, ?)";
 			ps = txn.prepareStatement(sql);
 			ps.setBytes(1, remote.getId().getBytes());
 			ps.setString(2, remote.getName());
 			ps.setBytes(3, remote.getPublicKey());
 			ps.setBytes(4, local.getBytes());
+			ps.setBoolean(5, active);
 			int affected = ps.executeUpdate();
 			if (affected != 1) throw new DbStateException();
 			ps.close();
@@ -468,31 +470,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 			if (rs.next()) throw new DbStateException();
 			rs.close();
 			ps.close();
-			// Create a status row for each message
-			sql = "SELECT messageID FROM messages";
-			ps = txn.prepareStatement(sql);
-			rs = ps.executeQuery();
-			Collection<byte[]> ids = new ArrayList<byte[]>();
-			while (rs.next()) ids.add(rs.getBytes(1));
-			rs.close();
-			ps.close();
-			if (!ids.isEmpty()) {
-				sql = "INSERT INTO statuses (messageId, contactId, ack,"
-						+ " seen, requested, expiry, txCount)"
-						+ " VALUES (?, ?, FALSE, FALSE, FALSE, 0, 0)";
-				ps = txn.prepareStatement(sql);
-				ps.setInt(2, c.getInt());
-				for (byte[] id : ids) {
-					ps.setBytes(1, id);
-					ps.addBatch();
-				}
-				int[] batchAffected = ps.executeBatch();
-				if (batchAffected.length != ids.size())
-					throw new DbStateException();
-				for (int rows : batchAffected)
-					if (rows != 1) throw new DbStateException();
-				ps.close();
-			}
 			return c;
 		} catch (SQLException e) {
 			tryToClose(rs);
@@ -951,7 +928,8 @@ abstract class JdbcDatabase implements Database<Connection> {
 		PreparedStatement ps = null;
 		ResultSet rs = null;
 		try {
-			String sql = "SELECT authorId, name, publicKey, localAuthorId"
+			String sql = "SELECT authorId, name, publicKey,"
+					+ " localAuthorId, active"
 					+ " FROM contacts"
 					+ " WHERE contactId = ?";
 			ps = txn.prepareStatement(sql);
@@ -962,30 +940,11 @@ abstract class JdbcDatabase implements Database<Connection> {
 			String name = rs.getString(2);
 			byte[] publicKey = rs.getBytes(3);
 			AuthorId localAuthorId = new AuthorId(rs.getBytes(4));
+			boolean active = rs.getBoolean(5);
 			rs.close();
 			ps.close();
 			Author author = new Author(authorId, name, publicKey);
-			return new Contact(c, author, localAuthorId);
-		} catch (SQLException e) {
-			tryToClose(rs);
-			tryToClose(ps);
-			throw new DbException(e);
-		}
-	}
-
-	public Collection<ContactId> getContactIds(Connection txn)
-			throws DbException {
-		PreparedStatement ps = null;
-		ResultSet rs = null;
-		try {
-			String sql = "SELECT contactId FROM contacts";
-			ps = txn.prepareStatement(sql);
-			rs = ps.executeQuery();
-			List<ContactId> ids = new ArrayList<ContactId>();
-			while (rs.next()) ids.add(new ContactId(rs.getInt(1)));
-			rs.close();
-			ps.close();
-			return Collections.unmodifiableList(ids);
+			return new Contact(c, author, localAuthorId, active);
 		} catch (SQLException e) {
 			tryToClose(rs);
 			tryToClose(ps);
@@ -999,7 +958,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 		ResultSet rs = null;
 		try {
 			String sql = "SELECT contactId, authorId, name, publicKey,"
-					+ " localAuthorId"
+					+ " localAuthorId, active"
 					+ " FROM contacts";
 			ps = txn.prepareStatement(sql);
 			rs = ps.executeQuery();
@@ -1011,7 +970,9 @@ abstract class JdbcDatabase implements Database<Connection> {
 				byte[] publicKey = rs.getBytes(4);
 				Author author = new Author(authorId, name, publicKey);
 				AuthorId localAuthorId = new AuthorId(rs.getBytes(5));
-				contacts.add(new Contact(contactId, author, localAuthorId));
+				boolean active = rs.getBoolean(6);
+				contacts.add(new Contact(contactId, author, localAuthorId,
+						active));
 			}
 			rs.close();
 			ps.close();
@@ -1151,6 +1112,27 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	public Collection<MessageId> getMessageIds(Connection txn, GroupId g)
+			throws DbException {
+		PreparedStatement ps = null;
+		ResultSet rs = null;
+		try {
+			String sql = "SELECT messageId FROM messages WHERE groupId = ?";
+			ps = txn.prepareStatement(sql);
+			ps.setBytes(1, g.getBytes());
+			rs = ps.executeQuery();
+			List<MessageId> ids = new ArrayList<MessageId>();
+			while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
+			rs.close();
+			ps.close();
+			return Collections.unmodifiableList(ids);
+		} catch (SQLException e) {
+			tryToClose(rs);
+			tryToClose(ps);
+			throw new DbException(e);
+		}
+	}
+
 	public Map<MessageId, Metadata> getMessageMetadata(Connection txn,
 			GroupId g) throws DbException {
 		PreparedStatement ps = null;
@@ -1309,15 +1291,12 @@ abstract class JdbcDatabase implements Database<Connection> {
 		ResultSet rs = null;
 		try {
 			String sql = "SELECT m.messageId FROM messages AS m"
-					+ " JOIN groupVisibilities AS gv"
-					+ " ON m.groupId = gv.groupId"
 					+ " JOIN statuses AS s"
 					+ " ON m.messageId = s.messageId"
-					+ " AND gv.contactId = s.contactId"
-					+ " WHERE gv.contactId = ?"
+					+ " WHERE contactId = ?"
 					+ " AND valid = ? AND shared = TRUE AND raw IS NOT NULL"
 					+ " AND seen = FALSE AND requested = FALSE"
-					+ " AND s.expiry < ?"
+					+ " AND expiry < ?"
 					+ " ORDER BY timestamp DESC LIMIT ?";
 			ps = txn.prepareStatement(sql);
 			ps.setInt(1, c.getInt());
@@ -1368,15 +1347,12 @@ abstract class JdbcDatabase implements Database<Connection> {
 		ResultSet rs = null;
 		try {
 			String sql = "SELECT length, m.messageId FROM messages AS m"
-					+ " JOIN groupVisibilities AS gv"
-					+ " ON m.groupId = gv.groupId"
 					+ " JOIN statuses AS s"
 					+ " ON m.messageId = s.messageId"
-					+ " AND gv.contactId = s.contactId"
-					+ " WHERE gv.contactId = ?"
+					+ " WHERE contactId = ?"
 					+ " AND valid = ? AND shared = TRUE AND raw IS NOT NULL"
 					+ " AND seen = FALSE"
-					+ " AND s.expiry < ?"
+					+ " AND expiry < ?"
 					+ " ORDER BY timestamp DESC";
 			ps = txn.prepareStatement(sql);
 			ps.setInt(1, c.getInt());
@@ -1454,15 +1430,12 @@ abstract class JdbcDatabase implements Database<Connection> {
 		ResultSet rs = null;
 		try {
 			String sql = "SELECT length, m.messageId FROM messages AS m"
-					+ " JOIN groupVisibilities AS gv"
-					+ " ON m.groupId = gv.groupId"
 					+ " JOIN statuses AS s"
 					+ " ON m.messageId = s.messageId"
-					+ " AND gv.contactId = s.contactId"
-					+ " WHERE gv.contactId = ?"
+					+ " WHERE contactId = ?"
 					+ " AND valid = ? AND shared = TRUE AND raw IS NOT NULL"
 					+ " AND seen = FALSE AND requested = TRUE"
-					+ " AND s.expiry < ?"
+					+ " AND expiry < ?"
 					+ " ORDER BY timestamp DESC";
 			ps = txn.prepareStatement(sql);
 			ps.setInt(1, c.getInt());
@@ -1777,12 +1750,12 @@ abstract class JdbcDatabase implements Database<Connection> {
 		try {
 			// Update any settings that already exist
 			String sql = "UPDATE settings SET value = ?"
-					+ " WHERE key = ? AND namespace = ?";
+					+ " WHERE namespace = ? AND key = ?";
 			ps = txn.prepareStatement(sql);
 			for (Entry<String, String> e : s.entrySet()) {
 				ps.setString(1, e.getValue());
-				ps.setString(2, e.getKey());
-				ps.setString(3, namespace);
+				ps.setString(2, namespace);
+				ps.setString(3, e.getKey());
 				ps.addBatch();
 			}
 			int[] batchAffected = ps.executeBatch();
@@ -1792,15 +1765,15 @@ abstract class JdbcDatabase implements Database<Connection> {
 				if (rows > 1) throw new DbStateException();
 			}
 			// Insert any settings that don't already exist
-			sql = "INSERT INTO settings (key, value, namespace)"
+			sql = "INSERT INTO settings (namespace, key, value)"
 					+ " VALUES (?, ?, ?)";
 			ps = txn.prepareStatement(sql);
 			int updateIndex = 0, inserted = 0;
 			for (Entry<String, String> e : s.entrySet()) {
 				if (batchAffected[updateIndex] == 0) {
-					ps.setString(1, e.getKey());
-					ps.setString(2, e.getValue());
-					ps.setString(3, namespace);
+					ps.setString(1, namespace);
+					ps.setString(2, e.getKey());
+					ps.setString(3, e.getValue());
 					ps.addBatch();
 					inserted++;
 				}
@@ -1976,6 +1949,24 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	public void removeStatus(Connection txn, ContactId c, MessageId m)
+			throws DbException {
+		PreparedStatement ps = null;
+		try {
+			String sql = "DELETE FROM statuses"
+					+ " WHERE contactId = ? AND messageId = ?";
+			ps = txn.prepareStatement(sql);
+			ps.setInt(1, c.getInt());
+			ps.setBytes(2, m.getBytes());
+			int affected = ps.executeUpdate();
+			if (affected != 1) throw new DbStateException();
+			ps.close();
+		} catch (SQLException e) {
+			tryToClose(ps);
+			throw new DbException(e);
+		}
+	}
+
 	public void removeTransport(Connection txn, TransportId t)
 			throws DbException {
 		PreparedStatement ps = null;
@@ -2028,6 +2019,23 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	public void setContactActive(Connection txn, ContactId c, boolean active)
+			throws DbException {
+		PreparedStatement ps = null;
+		try {
+			String sql = "UPDATE contacts SET active = ? WHERE contactId = ?";
+			ps = txn.prepareStatement(sql);
+			ps.setBoolean(1, active);
+			ps.setInt(2, c.getInt());
+			int affected = ps.executeUpdate();
+			if (affected < 0 || affected > 1) throw new DbStateException();
+			ps.close();
+		} catch (SQLException e) {
+			tryToClose(ps);
+			throw new DbException(e);
+		}
+	}
+
 	public void setMessageShared(Connection txn, MessageId m, boolean shared)
 			throws DbException {
 		PreparedStatement ps = null;
@@ -2037,7 +2045,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 			ps.setBoolean(1, shared);
 			ps.setBytes(2, m.getBytes());
 			int affected = ps.executeUpdate();
-			if (affected < 0) throw new DbStateException();
+			if (affected < 0 || affected > 1) throw new DbStateException();
 			ps.close();
 		} catch (SQLException e) {
 			tryToClose(ps);
@@ -2054,7 +2062,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 			ps.setInt(1, valid ? VALID.getValue() : INVALID.getValue());
 			ps.setBytes(2, m.getBytes());
 			int affected = ps.executeUpdate();
-			if (affected < 0) throw new DbStateException();
+			if (affected < 0 || affected > 1) throw new DbStateException();
 			ps.close();
 		} catch (SQLException e) {
 			tryToClose(ps);
diff --git a/briar-core/src/org/briarproject/invitation/Connector.java b/briar-core/src/org/briarproject/invitation/Connector.java
index a89aef88a5b50fa9ec1d7d15a7646f4d50adc9c3..4bd7fc2ea6f9b5d49e452d89fc95056fa4b541f7 100644
--- a/briar-core/src/org/briarproject/invitation/Connector.java
+++ b/briar-core/src/org/briarproject/invitation/Connector.java
@@ -219,7 +219,7 @@ abstract class Connector extends Thread {
 			long timestamp, boolean alice) throws DbException {
 		// Add the contact to the database
 		contactId = contactManager.addContact(remoteAuthor,
-				localAuthor.getId());
+				localAuthor.getId(), true);
 		// Derive transport keys
 		keyManager.addContact(contactId, master, timestamp, alice);
 		return contactId;
diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
index 3488bb2d1a106e49b9f4cccdcb5ba3a6d3e40f1b..17c27f9541472fff0f63e358f3ab493a33bc4cd7 100644
--- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
+++ b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
@@ -107,7 +107,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 		transportId = new TransportId("id");
 		maxLatency = Integer.MAX_VALUE;
 		contactId = new ContactId(234);
-		contact = new Contact(contactId, author, localAuthorId);
+		contact = new Contact(contactId, author, localAuthorId, true);
 	}
 
 	private DatabaseComponent createDatabaseComponent(Database<Object> database,
@@ -143,7 +143,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			will(returnValue(true));
 			oneOf(database).containsContact(txn, authorId, localAuthorId);
 			will(returnValue(false));
-			oneOf(database).addContact(txn, author, localAuthorId);
+			oneOf(database).addContact(txn, author, localAuthorId, true);
 			will(returnValue(contactId));
 			oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
 			// getContacts()
@@ -193,7 +193,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 		try {
 			db.addLocalAuthor(transaction, localAuthor);
 			assertEquals(contactId,
-					db.addContact(transaction, author, localAuthorId));
+					db.addContact(transaction, author, localAuthorId, true));
 			assertEquals(Collections.singletonList(contact),
 					db.getContacts(transaction));
 			db.addGroup(transaction, group); // First time - listeners called
@@ -261,8 +261,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
 			oneOf(database).getVisibility(txn, groupId);
 			will(returnValue(Collections.singletonList(contactId)));
-			oneOf(database).getContactIds(txn);
-			will(returnValue(Collections.singletonList(contactId)));
 			oneOf(database).removeOfferedMessage(txn, contactId, messageId);
 			will(returnValue(false));
 			oneOf(database).addStatus(txn, contactId, messageId, false, false);
@@ -296,11 +294,11 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
 			// Check whether the contact is in the DB (which it's not)
-			exactly(17).of(database).startTransaction();
+			exactly(18).of(database).startTransaction();
 			will(returnValue(txn));
-			exactly(17).of(database).containsContact(txn, contactId);
+			exactly(18).of(database).containsContact(txn, contactId);
 			will(returnValue(false));
-			exactly(17).of(database).abortTransaction(txn);
+			exactly(18).of(database).abortTransaction(txn);
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, eventBus,
 				shutdown);
@@ -458,6 +456,16 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			db.endTransaction(transaction);
 		}
 
+		transaction = db.startTransaction();
+		try {
+			db.setContactActive(transaction, contactId, true);
+			fail();
+		} catch (NoSuchContactException expected) {
+			// Expected
+		} finally {
+			db.endTransaction(transaction);
+		}
+
 		transaction = db.startTransaction();
 		try {
 			db.setReorderingWindow(transaction, contactId, transportId, 0, 0,
@@ -503,7 +511,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 
 		Transaction transaction = db.startTransaction();
 		try {
-			db.addContact(transaction, author, localAuthorId);
+			db.addContact(transaction, author, localAuthorId, true);
 			fail();
 		} catch (NoSuchLocalAuthorException expected) {
 			// Expected
@@ -757,7 +765,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			will(returnValue(true));
 			oneOf(database).containsContact(txn, authorId, localAuthorId);
 			will(returnValue(false));
-			oneOf(database).addContact(txn, author, localAuthorId);
+			oneOf(database).addContact(txn, author, localAuthorId, true);
 			will(returnValue(contactId));
 			oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
 			// endTransaction()
@@ -778,7 +786,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 		try {
 			db.addLocalAuthor(transaction, localAuthor);
 			assertEquals(contactId,
-					db.addContact(transaction, author, localAuthorId));
+					db.addContact(transaction, author, localAuthorId, true));
 			transaction.setComplete();
 		} finally {
 			db.endTransaction(transaction);
@@ -1074,11 +1082,9 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			oneOf(database).addMessage(txn, message, UNKNOWN, false);
 			oneOf(database).getVisibility(txn, groupId);
 			will(returnValue(Collections.singletonList(contactId)));
-			oneOf(database).getContactIds(txn);
-			will(returnValue(Collections.singletonList(contactId)));
 			oneOf(database).removeOfferedMessage(txn, contactId, messageId);
 			will(returnValue(false));
-			oneOf(database).addStatus(txn, contactId, messageId, false, true);
+			oneOf(database).addStatus(txn, contactId, messageId, false, false);
 			oneOf(database).raiseAckFlag(txn, contactId, messageId);
 			oneOf(database).commitTransaction(txn);
 			// The message was received and added
@@ -1270,6 +1276,11 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			oneOf(database).containsVisibleGroup(txn, contactId, groupId);
 			will(returnValue(false)); // Not yet visible
 			oneOf(database).addVisibility(txn, contactId, groupId);
+			oneOf(database).getMessageIds(txn, groupId);
+			will(returnValue(Collections.singletonList(messageId)));
+			oneOf(database).removeOfferedMessage(txn, contactId, messageId);
+			will(returnValue(false));
+			oneOf(database).addStatus(txn, contactId, messageId, false, false);
 			oneOf(database).commitTransaction(txn);
 			oneOf(eventBus).broadcast(with(any(
 					GroupVisibilityUpdatedEvent.class)));
diff --git a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
index 34a91d63c0e54037593038b7d2795c2df5b7fac3..5abba7f451527ac2664f3fd5ef3ad01aa1377777 100644
--- a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
+++ b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
@@ -108,7 +108,8 @@ public class H2DatabaseTest extends BriarTestCase {
 		Connection txn = db.startTransaction();
 		assertFalse(db.containsContact(txn, contactId));
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		assertTrue(db.containsContact(txn, contactId));
 		assertFalse(db.containsGroup(txn, groupId));
 		db.addGroup(txn, group);
@@ -170,7 +171,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact, a group and a message
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 		db.addVisibility(txn, contactId, groupId);
 		db.addMessage(txn, message, VALID, true);
@@ -207,7 +209,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact, a group and an unvalidated message
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 		db.addVisibility(txn, contactId, groupId);
 		db.addMessage(txn, message, UNKNOWN, true);
@@ -245,7 +248,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact, a group and an unshared message
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 		db.addVisibility(txn, contactId, groupId);
 		db.addMessage(txn, message, VALID, false);
@@ -283,7 +287,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact, a group and a message
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 		db.addVisibility(txn, contactId, groupId);
 		db.addMessage(txn, message, VALID, true);
@@ -302,44 +307,6 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.close();
 	}
 
-	@Test
-	public void testSendableMessagesMustBeVisible() throws Exception {
-		Database<Connection> db = open(false);
-		Connection txn = db.startTransaction();
-
-		// Add a contact, a group and a message
-		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
-		db.addGroup(txn, group);
-		db.addMessage(txn, message, VALID, true);
-		db.addStatus(txn, contactId, messageId, false, false);
-
-		// The group is not visible to the contact, so the message
-		// should not be sendable
-		Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
-				ONE_MEGABYTE);
-		assertTrue(ids.isEmpty());
-		ids = db.getMessagesToOffer(txn, contactId, 100);
-		assertTrue(ids.isEmpty());
-
-		// Making the group visible should make the message sendable
-		db.addVisibility(txn, contactId, groupId);
-		ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
-		assertEquals(Collections.singletonList(messageId), ids);
-		ids = db.getMessagesToOffer(txn, contactId, 100);
-		assertEquals(Collections.singletonList(messageId), ids);
-
-		// Making the group invisible should make the message unsendable
-		db.removeVisibility(txn, contactId, groupId);
-		ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
-		assertTrue(ids.isEmpty());
-		ids = db.getMessagesToOffer(txn, contactId, 100);
-		assertTrue(ids.isEmpty());
-
-		db.commitTransaction(txn);
-		db.close();
-	}
-
 	@Test
 	public void testMessagesToAck() throws Exception {
 		Database<Connection> db = open(false);
@@ -347,7 +314,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact and a group
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 		db.addVisibility(txn, contactId, groupId);
 
@@ -383,7 +351,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact, a group and a message
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 		db.addVisibility(txn, contactId, groupId);
 		db.addMessage(txn, message, VALID, true);
@@ -546,7 +515,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact and a group
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 		db.addVisibility(txn, contactId, groupId);
 
@@ -565,7 +535,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 
 		// The group is not in the database
 		assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
@@ -582,7 +553,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact, a group and a message
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 		db.addMessage(txn, message, VALID, true);
 		db.addStatus(txn, contactId, messageId, false, false);
@@ -601,7 +573,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact and a group
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 
 		// The group should not be visible to the contact
@@ -636,7 +609,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact and the groups
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		for (Group g : groups) db.addGroup(txn, g);
 
 		// Make the groups visible to the contact
@@ -668,7 +642,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add the contact, the transport and the transport keys
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addTransport(txn, transportId, 123);
 		db.addTransportKeys(txn, contactId, keys);
 
@@ -729,7 +704,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add the contact, transport and transport keys
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addTransport(txn, transportId, 123);
 		db.updateTransportKeys(txn, Collections.singletonMap(contactId, keys));
 
@@ -764,7 +740,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add the contact, transport and transport keys
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addTransport(txn, transportId, 123);
 		db.updateTransportKeys(txn, Collections.singletonMap(contactId, keys));
 
@@ -800,7 +777,8 @@ public class H2DatabaseTest extends BriarTestCase {
 		assertEquals(Collections.emptyList(), contacts);
 
 		// Add a contact associated with the local author
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		contacts = db.getContacts(txn, localAuthorId);
 		assertEquals(Collections.singletonList(contactId), contacts);
 
@@ -821,7 +799,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact - initially there should be no offered messages
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		assertEquals(0, db.countOfferedMessages(txn, contactId));
 
 		// Add some offered messages and count them
@@ -959,7 +938,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 
 		// Add a group and make it visible to the contact
 		db.addGroup(txn, group);
@@ -1035,7 +1015,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact and a group
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 
 		// The group should not be visible to the contact
@@ -1068,8 +1049,8 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.addLocalAuthor(txn, localAuthor1);
 
 		// Add the same contact for each local pseudonym
-		ContactId contactId = db.addContact(txn, author, localAuthorId);
-		ContactId contactId1 = db.addContact(txn, author, localAuthorId1);
+		ContactId contactId = db.addContact(txn, author, localAuthorId, true);
+		ContactId contactId1 = db.addContact(txn, author, localAuthorId1, true);
 
 		// The contacts should be distinct
 		assertNotEquals(contactId, contactId1);
@@ -1088,7 +1069,8 @@ public class H2DatabaseTest extends BriarTestCase {
 
 		// Add a contact, a group and a message
 		db.addLocalAuthor(txn, localAuthor);
-		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId,
+				true));
 		db.addGroup(txn, group);
 		db.addVisibility(txn, contactId, groupId);
 		db.addMessage(txn, message, VALID, true);
diff --git a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java
index 25cb91d0352c0f7e3d372d47861436696f7f4626..52b5b5448a30a545c4e8db25c68206aeb91ca9b7 100644
--- a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java
+++ b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java
@@ -38,7 +38,7 @@ public class PluginManagerImplTest extends BriarTestCase {
 		Mockery context = new Mockery() {{
 			setThreadingPolicy(new Synchroniser());
 		}};
-		final Executor ioExecutor = Executors.newCachedThreadPool();
+		final Executor ioExecutor = Executors.newSingleThreadExecutor();
 		final EventBus eventBus = context.mock(EventBus.class);
 		final SimplexPluginConfig simplexPluginConfig =
 				context.mock(SimplexPluginConfig.class);
diff --git a/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java b/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java
index 2c3cc4348ffaaf77234df32c5ef3e0f5b711ba93..62e9a613b769ac876e8e304b58b9b3cd71984f83 100644
--- a/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java
+++ b/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java
@@ -134,7 +134,8 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
 		// Add Bob as a contact
 		Author bobAuthor = new Author(bobId, "Bob",
 				new byte[MAX_PUBLIC_KEY_LENGTH]);
-		ContactId contactId = contactManager.addContact(bobAuthor, aliceId);
+		ContactId contactId = contactManager.addContact(bobAuthor, aliceId,
+				true);
 		// Derive and store the transport keys
 		keyManager.addContact(contactId, master, timestamp, true);
 
@@ -204,7 +205,8 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
 		// Add Alice as a contact
 		Author aliceAuthor = new Author(aliceId, "Alice",
 				new byte[MAX_PUBLIC_KEY_LENGTH]);
-		ContactId contactId = contactManager.addContact(aliceAuthor, bobId);
+		ContactId contactId = contactManager.addContact(aliceAuthor, bobId,
+				true);
 		// Derive and store the transport keys
 		keyManager.addContact(contactId, master, timestamp, false);