diff --git a/briar-android-tests/src/test/java/org/briarproject/IntroductionIntegrationTest.java b/briar-android-tests/src/test/java/org/briarproject/IntroductionIntegrationTest.java
index f049c142a86f0b4a1ac192ec10525432d51e2e46..259be1a9d4f5295d7ab36be98822125757cae24e 100644
--- a/briar-android-tests/src/test/java/org/briarproject/IntroductionIntegrationTest.java
+++ b/briar-android-tests/src/test/java/org/briarproject/IntroductionIntegrationTest.java
@@ -832,6 +832,106 @@ public class IntroductionIntegrationTest extends BriarTestCase {
 		}
 	}
 
+	@Test
+	public void testIntroduceesRemovedCleanup() 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);
+
+			// get database and local group for introducee
+			DatabaseComponent db0 = t0.getDatabaseComponent();
+			IntroductionGroupFactory groupFactory0 =
+					t0.getIntroductionGroupFactory();
+			Group group1 = groupFactory0.createLocalGroup();
+
+			// get local session state messages
+			Map<MessageId, Metadata> map;
+			Transaction txn = db0.startTransaction(false);
+			try {
+				map = db0.getMessageMetadata(txn, group1.getId());
+				txn.setComplete();
+			} finally {
+				db0.endTransaction(txn);
+			}
+			// check that we have one session state
+			assertEquals(1, map.size());
+
+			// introducer removes introducee1
+			contactManager0.removeContact(contactId1);
+
+			// get local session state messages again
+			txn = db0.startTransaction(false);
+			try {
+				map = db0.getMessageMetadata(txn, group1.getId());
+				txn.setComplete();
+			} finally {
+				db0.endTransaction(txn);
+			}
+			// make sure local state is still there
+			assertEquals(1, map.size());
+
+			// introducer removes other introducee
+			contactManager0.removeContact(contactId2);
+
+			// get local session state messages again
+			txn = db0.startTransaction(false);
+			try {
+				map = db0.getMessageMetadata(txn, group1.getId());
+				txn.setComplete();
+			} finally {
+				db0.endTransaction(txn);
+			}
+			// make sure local state is gone now
+			assertEquals(0, map.size());
+		} finally {
+			stopLifecycles();
+		}
+	}
+
 	// TODO add a test for faking responses when #256 is implemented
 
 	@After
diff --git a/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java b/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java
index 10cb986029898234208124b94e440b08e26267c0..623a1ed375adf8d37984d83208765693312e35c1 100644
--- a/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java
+++ b/briar-core/src/org/briarproject/introduction/IntroductionManagerImpl.java
@@ -9,10 +9,12 @@ import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager.AddContactHook;
 import org.briarproject.api.contact.ContactManager.RemoveContactHook;
 import org.briarproject.api.data.BdfDictionary;
+import org.briarproject.api.data.BdfEntry;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.data.MetadataParser;
 import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
+import org.briarproject.api.db.NoSuchContactException;
 import org.briarproject.api.db.NoSuchMessageException;
 import org.briarproject.api.db.Transaction;
 import org.briarproject.api.identity.AuthorId;
@@ -136,29 +138,58 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
 
 	@Override
 	public void removingContact(Transaction txn, Contact c) throws DbException {
-		// check for open sessions with that contact and abort those
-		Long id = (long) c.getId().getInt();
+		GroupId gId = introductionGroupFactory.createLocalGroup().getId();
+
+		// search for session states where c introduced us
+		BdfDictionary query = BdfDictionary.of(
+				new BdfEntry(ROLE, ROLE_INTRODUCEE),
+				new BdfEntry(CONTACT_ID_1, c.getId().getInt())
+		);
 		try {
 			Map<MessageId, BdfDictionary> map = clientHelper
-					.getMessageMetadataAsDictionary(txn,
-							introductionGroupFactory.createLocalGroup().getId());
+					.getMessageMetadataAsDictionary(txn, gId, query);
+			for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
+				// delete states if introducee removes introducer
+				deleteMessage(txn, entry.getKey());
+			}
+		} catch (FormatException e) {
+			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+		}
+
+		// check for open sessions with c and abort those,
+		// so the other introducee knows
+		query = BdfDictionary.of(
+				new BdfEntry(ROLE, ROLE_INTRODUCER)
+		);
+		try {
+			Map<MessageId, BdfDictionary> map = clientHelper
+					.getMessageMetadataAsDictionary(txn, gId, query);
 			for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
 				BdfDictionary d = entry.getValue();
-				long role = d.getLong(ROLE, -1L);
-				if (role != ROLE_INTRODUCER) {
-					if (d.getLong(CONTACT_ID_1).equals(id)) {
-						// delete states if introducee removes introducer
-						deleteMessage(txn, entry.getKey());
-					}
-				}
-				else if (d.getLong(CONTACT_ID_1).equals(id) ||
-						d.getLong(CONTACT_ID_2).equals(id)) {
+				ContactId c1 = new ContactId(d.getLong(CONTACT_ID_1).intValue());
+				ContactId c2 = new ContactId(d.getLong(CONTACT_ID_2).intValue());
 
+				if (c1.equals(c.getId()) || c2.equals(c.getId())) {
 					IntroducerProtocolState state = IntroducerProtocolState
 							.fromValue(d.getLong(STATE).intValue());
+					// abort protocol if still ongoing
 					if (IntroducerProtocolState.isOngoing(state)) {
 						introducerManager.abort(txn, d);
 					}
+					// also delete state if both contacts have been deleted
+					if (c1.equals(c.getId())) {
+						try {
+							db.getContact(txn, c2);
+						} catch (NoSuchContactException e) {
+							deleteMessage(txn, entry.getKey());
+						}
+					} else if (c2.equals(c.getId())) {
+						try {
+							db.getContact(txn, c1);
+						} catch (NoSuchContactException e) {
+							deleteMessage(txn, entry.getKey());
+						}
+					}
 				}
 			}
 		} catch (FormatException e) {