diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/droidtooth/DroidtoothPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/droidtooth/DroidtoothPlugin.java
index a5a28e005452d06230d34a04e27c25588bf7413d..12b546b468d8157963f5156e1a1484548d2f2df8 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/droidtooth/DroidtoothPlugin.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/droidtooth/DroidtoothPlugin.java
@@ -347,8 +347,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 	@Override
 	public DuplexTransportConnection createConnection(ContactId c) {
 		if (!isRunning()) return null;
-		TransportProperties p = callback.getRemoteProperties().get(c);
-		if (p == null) return null;
+		TransportProperties p = callback.getRemoteProperties(c);
 		String address = p.get(PROP_ADDRESS);
 		if (StringUtils.isNullOrEmpty(address)) return null;
 		String uuid = p.get(PROP_UUID);
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
index 9ad9f427f02148916c900f0acafe06b425d34f5c..9f736b2fd971c3b751e0d4943c82684b07a0d270 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
@@ -55,6 +55,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Scanner;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -538,16 +539,21 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 	public void poll(Collection<ContactId> connected) {
 		if (!isRunning()) return;
 		backoff.increment();
-		// TODO: Pass properties to connectAndCallBack()
-		for (ContactId c : callback.getRemoteProperties().keySet())
-			if (!connected.contains(c)) connectAndCallBack(c);
+		Map<ContactId, TransportProperties> remote =
+				callback.getRemoteProperties();
+		for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
+			ContactId c = e.getKey();
+			if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
+		}
 	}
 
-	private void connectAndCallBack(final ContactId c) {
+	private void connectAndCallBack(final ContactId c,
+			final TransportProperties p) {
 		ioExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
-				DuplexTransportConnection d = createConnection(c);
+				if (!isRunning()) return;
+				DuplexTransportConnection d = createConnection(p);
 				if (d != null) {
 					backoff.reset();
 					callback.outgoingConnectionCreated(c, d);
@@ -559,8 +565,11 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 	@Override
 	public DuplexTransportConnection createConnection(ContactId c) {
 		if (!isRunning()) return null;
-		TransportProperties p = callback.getRemoteProperties().get(c);
-		if (p == null) return null;
+		return createConnection(callback.getRemoteProperties(c));
+	}
+
+	@Nullable
+	private DuplexTransportConnection createConnection(TransportProperties p) {
 		String onion = p.get(PROP_ONION);
 		if (StringUtils.isNullOrEmpty(onion)) return null;
 		if (!ONION.matcher(onion).matches()) {
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java
index 954db7b149b6c414c1e95af049d66bbfb4698804..3b7364c4fee854140918536197a02deece764744 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java
@@ -29,6 +29,11 @@ public interface PluginCallback {
 	 */
 	Map<ContactId, TransportProperties> getRemoteProperties();
 
+	/**
+	 * Returns the plugin's remote transport properties for the given contact.
+	 */
+	TransportProperties getRemoteProperties(ContactId c);
+
 	/**
 	 * Merges the given settings with the namespaced settings
 	 */
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/properties/TransportPropertyManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/properties/TransportPropertyManager.java
index 98e889a128a895040f03c6eb0865feb5eeee34d0..a29698873c3f016e6a19bcb436cc641a287b4969 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/properties/TransportPropertyManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/properties/TransportPropertyManager.java
@@ -49,6 +49,13 @@ public interface TransportPropertyManager {
 	Map<ContactId, TransportProperties> getRemoteProperties(TransportId t)
 			throws DbException;
 
+	/**
+	 * Returns the remote transport properties for the given contact and
+	 * transport.
+	 */
+	TransportProperties getRemoteProperties(ContactId c, TransportId t)
+			throws DbException;
+
 	/**
 	 * Merges the given properties with the existing local properties for the
 	 * given transport.
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java
index 087d943da1f966b6d60bc863901ef929cc9c26d7..60c6d61cd8913f4c00433c312d126c8afb2dc7cb 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java
@@ -283,6 +283,16 @@ class PluginManagerImpl implements PluginManager, Service {
 			}
 		}
 
+		@Override
+		public TransportProperties getRemoteProperties(ContactId c) {
+			try {
+				return transportPropertyManager.getRemoteProperties(c, id);
+			} catch (DbException e) {
+				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				return new TransportProperties();
+			}
+		}
+
 		@Override
 		public void mergeSettings(Settings s) {
 			try {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java
index 47e40f969ddc2d5fc929c6f581a9066fc732f751..baa523d4ba4cba20b8bda5c31eadc4e31e9ae92b 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java
@@ -25,6 +25,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -209,8 +210,9 @@ abstract class TcpPlugin implements DuplexPlugin {
 	public void poll(Collection<ContactId> connected) {
 		if (!isRunning()) return;
 		backoff.increment();
-		for (Entry<ContactId, TransportProperties> e :
-				callback.getRemoteProperties().entrySet()) {
+		Map<ContactId, TransportProperties> remote =
+				callback.getRemoteProperties();
+		for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
 			ContactId c = e.getKey();
 			if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
 		}
@@ -234,8 +236,7 @@ abstract class TcpPlugin implements DuplexPlugin {
 	@Override
 	public DuplexTransportConnection createConnection(ContactId c) {
 		if (!isRunning()) return null;
-		TransportProperties p = callback.getRemoteProperties().get(c);
-		return p == null ? null : createConnection(p);
+		return createConnection(callback.getRemoteProperties(c));
 	}
 
 	@Nullable
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/properties/TransportPropertyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/properties/TransportPropertyManagerImpl.java
index 1fe72cdfc10655baf714e07b66fece664aa161f5..2c3aa556aa8399e51ce3e4b54e33e65b161c3205 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/properties/TransportPropertyManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/properties/TransportPropertyManagerImpl.java
@@ -160,35 +160,52 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
 	@Override
 	public Map<ContactId, TransportProperties> getRemoteProperties(
 			TransportId t) throws DbException {
+		Map<ContactId, TransportProperties> remote =
+				new HashMap<ContactId, TransportProperties>();
+		Transaction txn = db.startTransaction(true);
 		try {
-			Map<ContactId, TransportProperties> remote =
-					new HashMap<ContactId, TransportProperties>();
-			Transaction txn = db.startTransaction(true);
-			try {
-				for (Contact c : db.getContacts(txn)) {
-					// Don't return properties for inactive contacts
-					if (!c.isActive()) continue;
-					Group g = getContactGroup(c);
-					// Find the latest remote update
-					LatestUpdate latest = findLatest(txn, g.getId(), t, false);
-					if (latest != null) {
-						// Retrieve and parse the latest remote properties
-						BdfList message = clientHelper.getMessageAsList(txn,
-								latest.messageId);
-						if (message == null) throw new DbException();
-						remote.put(c.getId(), parseProperties(message));
-					}
-				}
-				db.commitTransaction(txn);
-			} finally {
-				db.endTransaction(txn);
-			}
-			return remote;
+			for (Contact c : db.getContacts(txn))
+				remote.put(c.getId(), getRemoteProperties(txn, c, t));
+			db.commitTransaction(txn);
+		} finally {
+			db.endTransaction(txn);
+		}
+		return remote;
+	}
+
+	private TransportProperties getRemoteProperties(Transaction txn, Contact c,
+			TransportId t) throws DbException {
+		// Don't return properties for inactive contacts
+		if (!c.isActive()) return new TransportProperties();
+		Group g = getContactGroup(c);
+		try {
+			// Find the latest remote update
+			LatestUpdate latest = findLatest(txn, g.getId(), t, false);
+			if (latest == null) return new TransportProperties();
+			// Retrieve and parse the latest remote properties
+			BdfList message =
+					clientHelper.getMessageAsList(txn, latest.messageId);
+			if (message == null) throw new DbException();
+			return parseProperties(message);
 		} catch (FormatException e) {
 			throw new DbException(e);
 		}
 	}
 
+	@Override
+	public TransportProperties getRemoteProperties(ContactId c, TransportId t)
+			throws DbException {
+		TransportProperties p;
+		Transaction txn = db.startTransaction(true);
+		try {
+			p = getRemoteProperties(txn, db.getContact(txn, c), t);
+			db.commitTransaction(txn);
+		} finally {
+			db.endTransaction(txn);
+		}
+		return p;
+	}
+
 	@Override
 	public void mergeLocalProperties(TransportId t, TransportProperties p)
 			throws DbException {
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java
index a873ee829fea1eb39651ae952f2dc54d57503638..7f65a2460dfd707ee9c8e8f33f5264b47c253454 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java
@@ -311,6 +311,11 @@ public class LanTcpPluginTest extends BrambleTestCase {
 			return remote;
 		}
 
+		@Override
+		public TransportProperties getRemoteProperties(ContactId c) {
+			return remote.get(c);
+		}
+
 		@Override
 		public void mergeSettings(Settings s) {
 		}
diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java
index 426f387b33c3febae0215e82cb418130ce539d05..22783756b27fb559811ecb37f703e69ab6692a71 100644
--- a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java
+++ b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java
@@ -249,8 +249,7 @@ class BluetoothPlugin implements DuplexPlugin {
 	@Override
 	public DuplexTransportConnection createConnection(ContactId c) {
 		if (!running) return null;
-		TransportProperties p = callback.getRemoteProperties().get(c);
-		if (p == null) return null;
+		TransportProperties p = callback.getRemoteProperties(c);
 		String address = p.get(PROP_ADDRESS);
 		if (StringUtils.isNullOrEmpty(address)) return null;
 		String uuid = p.get(PROP_UUID);
diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java
index 2e9f3819c8204211895727d85544046a0266ac35..269e7feacdc23c679eb4be3ececf693dc7a33577 100644
--- a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java
+++ b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java
@@ -145,8 +145,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 		String fromIso = callback.getLocalProperties().get("iso3166");
 		if (StringUtils.isNullOrEmpty(fromIso)) return null;
 		// Get the ISO 3166 code for the callee's country
-		TransportProperties properties = callback.getRemoteProperties().get(c);
-		if (properties == null) return null;
+		TransportProperties properties = callback.getRemoteProperties(c);
 		String toIso = properties.get("iso3166");
 		if (StringUtils.isNullOrEmpty(toIso)) return null;
 		// Get the callee's phone number
diff --git a/bramble-j2se/src/test/java/org/briarproject/bramble/plugin/modem/ModemPluginTest.java b/bramble-j2se/src/test/java/org/briarproject/bramble/plugin/modem/ModemPluginTest.java
index 77db390d33985a2c109dda169c11d1c6449a80da..6f8cd501604704e5b4efb479c758f950deb2f534 100644
--- a/bramble-j2se/src/test/java/org/briarproject/bramble/plugin/modem/ModemPluginTest.java
+++ b/bramble-j2se/src/test/java/org/briarproject/bramble/plugin/modem/ModemPluginTest.java
@@ -9,8 +9,6 @@ import org.jmock.Mockery;
 import org.junit.Test;
 
 import java.io.IOException;
-import java.util.Collections;
-import java.util.Map;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -65,12 +63,10 @@ public class ModemPluginTest extends BrambleTestCase {
 		final Modem modem = context.mock(Modem.class);
 		final TransportProperties local = new TransportProperties();
 		local.put("iso3166", ISO_1336);
-		TransportProperties p = new TransportProperties();
-		p.put("iso3166", ISO_1336);
-		p.put("number", NUMBER);
-		ContactId contactId = new ContactId(234);
-		final Map<ContactId, TransportProperties> remote =
-				Collections.singletonMap(contactId, p);
+		final TransportProperties remote = new TransportProperties();
+		remote.put("iso3166", ISO_1336);
+		remote.put("number", NUMBER);
+		final ContactId contactId = new ContactId(234);
 		context.checking(new Expectations() {{
 			// start()
 			oneOf(serialPortList).getPortNames();
@@ -82,7 +78,7 @@ public class ModemPluginTest extends BrambleTestCase {
 			// createConnection()
 			oneOf(callback).getLocalProperties();
 			will(returnValue(local));
-			oneOf(callback).getRemoteProperties();
+			oneOf(callback).getRemoteProperties(contactId);
 			will(returnValue(remote));
 			oneOf(modem).dial(NUMBER);
 			will(returnValue(true));
@@ -106,12 +102,10 @@ public class ModemPluginTest extends BrambleTestCase {
 		final Modem modem = context.mock(Modem.class);
 		final TransportProperties local = new TransportProperties();
 		local.put("iso3166", ISO_1336);
-		TransportProperties p = new TransportProperties();
-		p.put("iso3166", ISO_1336);
-		p.put("number", NUMBER);
-		ContactId contactId = new ContactId(234);
-		final Map<ContactId, TransportProperties> remote =
-				Collections.singletonMap(contactId, p);
+		final TransportProperties remote = new TransportProperties();
+		remote.put("iso3166", ISO_1336);
+		remote.put("number", NUMBER);
+		final ContactId contactId = new ContactId(234);
 		context.checking(new Expectations() {{
 			// start()
 			oneOf(serialPortList).getPortNames();
@@ -123,7 +117,7 @@ public class ModemPluginTest extends BrambleTestCase {
 			// createConnection()
 			oneOf(callback).getLocalProperties();
 			will(returnValue(local));
-			oneOf(callback).getRemoteProperties();
+			oneOf(callback).getRemoteProperties(contactId);
 			will(returnValue(remote));
 			oneOf(modem).dial(NUMBER);
 			will(returnValue(false));
@@ -147,12 +141,10 @@ public class ModemPluginTest extends BrambleTestCase {
 		final Modem modem = context.mock(Modem.class);
 		final TransportProperties local = new TransportProperties();
 		local.put("iso3166", ISO_1336);
-		TransportProperties p = new TransportProperties();
-		p.put("iso3166", ISO_1336);
-		p.put("number", NUMBER);
-		ContactId contactId = new ContactId(234);
-		final Map<ContactId, TransportProperties> remote =
-				Collections.singletonMap(contactId, p);
+		final TransportProperties remote = new TransportProperties();
+		remote.put("iso3166", ISO_1336);
+		remote.put("number", NUMBER);
+		final ContactId contactId = new ContactId(234);
 		context.checking(new Expectations() {{
 			// start()
 			oneOf(serialPortList).getPortNames();
@@ -164,7 +156,7 @@ public class ModemPluginTest extends BrambleTestCase {
 			// createConnection()
 			oneOf(callback).getLocalProperties();
 			will(returnValue(local));
-			oneOf(callback).getRemoteProperties();
+			oneOf(callback).getRemoteProperties(contactId);
 			will(returnValue(remote));
 			oneOf(modem).dial(NUMBER);
 			will(throwException(new IOException()));