diff --git a/briar-api/src/org/briarproject/api/event/ConnectionClosedEvent.java b/briar-api/src/org/briarproject/api/event/ConnectionClosedEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..29030edcc4e382b0aa3392823fc51fe7a1ec2862
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/event/ConnectionClosedEvent.java
@@ -0,0 +1,30 @@
+package org.briarproject.api.event;
+
+import org.briarproject.api.TransportId;
+import org.briarproject.api.contact.ContactId;
+
+public class ConnectionClosedEvent extends Event {
+
+	private final ContactId contactId;
+	private final TransportId transportId;
+	private final boolean incoming;
+
+	public ConnectionClosedEvent(ContactId contactId, TransportId transportId,
+			boolean incoming) {
+		this.contactId = contactId;
+		this.transportId = transportId;
+		this.incoming = incoming;
+	}
+
+	public ContactId getContactId() {
+		return contactId;
+	}
+
+	public TransportId getTransportId() {
+		return transportId;
+	}
+
+	public boolean isIncoming() {
+		return incoming;
+	}
+}
diff --git a/briar-api/src/org/briarproject/api/event/ConnectionOpenedEvent.java b/briar-api/src/org/briarproject/api/event/ConnectionOpenedEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..2143eb37a035b4e79c7887354cc38115b891cdfb
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/event/ConnectionOpenedEvent.java
@@ -0,0 +1,30 @@
+package org.briarproject.api.event;
+
+import org.briarproject.api.TransportId;
+import org.briarproject.api.contact.ContactId;
+
+public class ConnectionOpenedEvent extends Event {
+
+	private final ContactId contactId;
+	private final TransportId transportId;
+	private final boolean incoming;
+
+	public ConnectionOpenedEvent(ContactId contactId, TransportId transportId,
+			boolean incoming) {
+		this.contactId = contactId;
+		this.transportId = transportId;
+		this.incoming = incoming;
+	}
+
+	public ContactId getContactId() {
+		return contactId;
+	}
+
+	public TransportId getTransportId() {
+		return transportId;
+	}
+
+	public boolean isIncoming() {
+		return incoming;
+	}
+}
diff --git a/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java b/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java
index dc9376195387a5da851a9aab37a67707bc470e1c..6afc50acecd28015f61e0c92502faffdaae28ad9 100644
--- a/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java
+++ b/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java
@@ -10,9 +10,9 @@ import java.util.Collection;
  */
 public interface ConnectionRegistry {
 
-	void registerConnection(ContactId c, TransportId t);
+	void registerConnection(ContactId c, TransportId t, boolean incoming);
 
-	void unregisterConnection(ContactId c, TransportId t);
+	void unregisterConnection(ContactId c, TransportId t, boolean incoming);
 
 	Collection<ContactId> getConnectedContacts(TransportId t);
 
diff --git a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java
index 3c9f5fb48ed3d792a8180ae93bd5e8cc1e04e0eb..c85c5f016d64c3af085454ca856d240943f0b93f 100644
--- a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java
+++ b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java
@@ -144,7 +144,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				return;
 			}
 			ContactId contactId = ctx.getContactId();
-			connectionRegistry.registerConnection(contactId, transportId);
+			connectionRegistry.registerConnection(contactId, transportId, true);
 			try {
 				// Create and run the incoming session
 				createIncomingSession(ctx, reader).run();
@@ -153,7 +153,8 @@ class ConnectionManagerImpl implements ConnectionManager {
 				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 				disposeReader(true, true);
 			} finally {
-				connectionRegistry.unregisterConnection(contactId, transportId);
+				connectionRegistry.unregisterConnection(contactId, transportId,
+						true);
 			}
 		}
 
@@ -194,7 +195,8 @@ class ConnectionManagerImpl implements ConnectionManager {
 				disposeWriter(true);
 				return;
 			}
-			connectionRegistry.registerConnection(contactId, transportId);
+			connectionRegistry.registerConnection(contactId, transportId,
+					false);
 			try {
 				// Create and run the outgoing session
 				createSimplexOutgoingSession(ctx, writer).run();
@@ -203,7 +205,8 @@ class ConnectionManagerImpl implements ConnectionManager {
 				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 				disposeWriter(true);
 			} finally {
-				connectionRegistry.unregisterConnection(contactId, transportId);
+				connectionRegistry.unregisterConnection(contactId, transportId,
+						false);
 			}
 		}
 
@@ -254,7 +257,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 				return;
 			}
 			contactId = ctx.getContactId();
-			connectionRegistry.registerConnection(contactId, transportId);
+			connectionRegistry.registerConnection(contactId, transportId, true);
 			// Start the outgoing session on another thread
 			ioExecutor.execute(new Runnable() {
 				public void run() {
@@ -270,7 +273,8 @@ class ConnectionManagerImpl implements ConnectionManager {
 				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 				disposeReader(true, true);
 			} finally {
-				connectionRegistry.unregisterConnection(contactId, transportId);
+				connectionRegistry.unregisterConnection(contactId, transportId,
+						true);
 			}
 		}
 
@@ -398,7 +402,8 @@ class ConnectionManagerImpl implements ConnectionManager {
 				disposeReader(true, true);
 				return;
 			}
-			connectionRegistry.registerConnection(contactId, transportId);
+			connectionRegistry.registerConnection(contactId, transportId,
+					false);
 			try {
 				// Create and run the incoming session
 				incomingSession = createIncomingSession(ctx, reader);
@@ -408,7 +413,8 @@ class ConnectionManagerImpl implements ConnectionManager {
 				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 				disposeReader(true, true);
 			} finally {
-				connectionRegistry.unregisterConnection(contactId, transportId);
+				connectionRegistry.unregisterConnection(contactId, transportId,
+						false);
 			}
 		}
 
diff --git a/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java b/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java
index 0fe9c3b4b4cae11e0f407d61498acd62a89b9ff9..c7f726a985c9086ebbf11d90d1efd15e8b99df93 100644
--- a/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java
+++ b/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java
@@ -2,6 +2,8 @@ package org.briarproject.plugins;
 
 import org.briarproject.api.TransportId;
 import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.event.ConnectionClosedEvent;
+import org.briarproject.api.event.ConnectionOpenedEvent;
 import org.briarproject.api.event.ContactConnectedEvent;
 import org.briarproject.api.event.ContactDisconnectedEvent;
 import org.briarproject.api.event.EventBus;
@@ -40,8 +42,12 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
 		contactCounts = new HashMap<ContactId, Integer>();
 	}
 
-	public void registerConnection(ContactId c, TransportId t) {
-		if (LOG.isLoggable(INFO)) LOG.info("Connection registered: " + t);
+	public void registerConnection(ContactId c, TransportId t,
+			boolean incoming) {
+		if (LOG.isLoggable(INFO)) {
+			if (incoming) LOG.info("Incoming connection registered: " + t);
+			else LOG.info("Outgoing connection registered: " + t);
+		}
 		boolean firstConnection = false;
 		lock.lock();
 		try {
@@ -63,14 +69,19 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
 		} finally {
 			lock.unlock();
 		}
+		eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming));
 		if (firstConnection) {
 			LOG.info("Contact connected");
 			eventBus.broadcast(new ContactConnectedEvent(c));
 		}
 	}
 
-	public void unregisterConnection(ContactId c, TransportId t) {
-		if (LOG.isLoggable(INFO)) LOG.info("Connection unregistered: " + t);
+	public void unregisterConnection(ContactId c, TransportId t,
+			boolean incoming) {
+		if (LOG.isLoggable(INFO)) {
+			if (incoming) LOG.info("Incoming connection unregistered: " + t);
+			else LOG.info("Outgoing connection unregistered: " + t);
+		}
 		boolean lastConnection = false;
 		lock.lock();
 		try {
@@ -94,6 +105,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
 		} finally {
 			lock.unlock();
 		}
+		eventBus.broadcast(new ConnectionClosedEvent(c, t, incoming));
 		if (lastConnection) {
 			LOG.info("Contact disconnected");
 			eventBus.broadcast(new ContactDisconnectedEvent(c));
diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java
index 4eb8da75b93907cb3e5ef1a03fbea07f9e74d21c..a03c23dbb547c845f3ea9b26b19dffdc29e6a9a8 100644
--- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java
+++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java
@@ -3,6 +3,7 @@ package org.briarproject.plugins;
 import org.briarproject.api.TransportId;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.db.DbException;
+import org.briarproject.api.event.ConnectionClosedEvent;
 import org.briarproject.api.event.ContactStatusChangedEvent;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
@@ -163,7 +164,16 @@ class PluginManagerImpl implements PluginManager, Service, EventListener {
 	public void eventOccurred(Event e) {
 		if (e instanceof ContactStatusChangedEvent) {
 			ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
-			if (c.isActive()) connectToContact(c.getContactId());
+			if (c.isActive()) {
+				// Connect to the newly activated contact
+				connectToContact(c.getContactId());
+			}
+		} else if (e instanceof ConnectionClosedEvent) {
+			ConnectionClosedEvent c = (ConnectionClosedEvent) e;
+			if (!c.isIncoming()) {
+				// Connect to the disconnected contact
+				connectToContact(c.getContactId(), c.getTransportId());
+			}
 		}
 	}
 
@@ -174,6 +184,14 @@ class PluginManagerImpl implements PluginManager, Service, EventListener {
 			if (d.shouldPoll()) connectToContact(c, d);
 	}
 
+	private void connectToContact(ContactId c, TransportId t) {
+		Plugin p = plugins.get(t);
+		if (p instanceof SimplexPlugin && p.shouldPoll())
+			connectToContact(c, (SimplexPlugin) p);
+		else if (p instanceof DuplexPlugin && p.shouldPoll())
+			connectToContact(c, (DuplexPlugin) p);
+	}
+
 	private void connectToContact(final ContactId c, final SimplexPlugin p) {
 		ioExecutor.execute(new Runnable() {
 			public void run() {
diff --git a/briar-tests/src/org/briarproject/plugins/ConnectionRegistryImplTest.java b/briar-tests/src/org/briarproject/plugins/ConnectionRegistryImplTest.java
index 8ea235249bca696de2b2edc2323c5d9ecad2a3cc..b18634e0aa7db117d6ce634cf4d0f411bb5370bd 100644
--- a/briar-tests/src/org/briarproject/plugins/ConnectionRegistryImplTest.java
+++ b/briar-tests/src/org/briarproject/plugins/ConnectionRegistryImplTest.java
@@ -3,6 +3,8 @@ package org.briarproject.plugins;
 import org.briarproject.BriarTestCase;
 import org.briarproject.api.TransportId;
 import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.event.ConnectionClosedEvent;
+import org.briarproject.api.event.ConnectionOpenedEvent;
 import org.briarproject.api.event.ContactConnectedEvent;
 import org.briarproject.api.event.ContactDisconnectedEvent;
 import org.briarproject.api.event.EventBus;
@@ -35,6 +37,10 @@ public class ConnectionRegistryImplTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
+			exactly(5).of(eventBus).broadcast(with(any(
+					ConnectionOpenedEvent.class)));
+			exactly(2).of(eventBus).broadcast(with(any(
+					ConnectionClosedEvent.class)));
 			exactly(3).of(eventBus).broadcast(with(any(
 					ContactConnectedEvent.class)));
 			oneOf(eventBus).broadcast(with(any(
@@ -49,43 +55,46 @@ public class ConnectionRegistryImplTest extends BriarTestCase {
 		assertEquals(Collections.emptyList(),
 				c.getConnectedContacts(transportId1));
 		// Check that a registered connection shows up - this should
-		// broadcast a ContactConnectedEvent
-		c.registerConnection(contactId, transportId);
+		// broadcast a ConnectionOpenedEvent and a ContactConnectedEvent
+		c.registerConnection(contactId, transportId, true);
 		assertEquals(Collections.singletonList(contactId),
 				c.getConnectedContacts(transportId));
 		assertEquals(Collections.emptyList(),
 				c.getConnectedContacts(transportId1));
-		// Register an identical connection - lookup should be unaffected
-		c.registerConnection(contactId, transportId);
+		// Register an identical connection - this should broadcast a
+		// ConnectionOpenedEvent and lookup should be unaffected
+		c.registerConnection(contactId, transportId, true);
 		assertEquals(Collections.singletonList(contactId),
 				c.getConnectedContacts(transportId));
 		assertEquals(Collections.emptyList(),
 				c.getConnectedContacts(transportId1));
-		// Unregister one of the connections - lookup should be unaffected
-		c.unregisterConnection(contactId, transportId);
+		// Unregister one of the connections - this should broadcast a
+		// ConnectionClosedEvent and lookup should be unaffected
+		c.unregisterConnection(contactId, transportId, true);
 		assertEquals(Collections.singletonList(contactId),
 				c.getConnectedContacts(transportId));
 		assertEquals(Collections.emptyList(),
 				c.getConnectedContacts(transportId1));
-		// Unregister the other connection - lookup should be affected -
-		// this should broadcast a ContactDisconnectedEvent
-		c.unregisterConnection(contactId, transportId);
+		// Unregister the other connection - this should broadcast a
+		// ConnectionClosedEvent and a ContactDisconnectedEvent
+		c.unregisterConnection(contactId, transportId, true);
 		assertEquals(Collections.emptyList(),
 				c.getConnectedContacts(transportId));
 		assertEquals(Collections.emptyList(),
 				c.getConnectedContacts(transportId1));
 		// Try to unregister the connection again - exception should be thrown
 		try {
-			c.unregisterConnection(contactId, transportId);
+			c.unregisterConnection(contactId, transportId, true);
 			fail();
 		} catch (IllegalArgumentException expected) {
 			// Expected
 		}
 		// Register both contacts with one transport, one contact with both -
-		// this should broadcast two ContactConnectedEvents
-		c.registerConnection(contactId, transportId);
-		c.registerConnection(contactId1, transportId);
-		c.registerConnection(contactId1, transportId1);
+		// this should broadcast three ConnectionOpenedEvents and two
+		// ContactConnectedEvents
+		c.registerConnection(contactId, transportId, true);
+		c.registerConnection(contactId1, transportId, true);
+		c.registerConnection(contactId1, transportId1, true);
 		Collection<ContactId> connected = c.getConnectedContacts(transportId);
 		assertEquals(2, connected.size());
 		assertTrue(connected.contains(contactId));