diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java
index d349650735cbd6540378c97777b9e28e013eb8b2..68ff547a11fbf1ff573a13791fbbf57b880547c0 100644
--- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java
+++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java
@@ -68,6 +68,10 @@ class DroidtoothTransportConnection implements DuplexTransportConnection {
 			return plugin.getMaxLatency();
 		}
 
+		public long getMaxIdleTime() {
+			return plugin.getMaxIdleTime();
+		}
+
 		public long getCapacity() {
 			return Long.MAX_VALUE;
 		}
diff --git a/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java b/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java
index ab0969eae633fd8270cc35bcb33bc8e361884ebf..081bba120511addc160626995df697443eedea7e 100644
--- a/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java
+++ b/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java
@@ -67,6 +67,10 @@ class TorTransportConnection implements DuplexTransportConnection {
 			return plugin.getMaxLatency();
 		}
 
+		public long getMaxIdleTime() {
+			return plugin.getMaxIdleTime();
+		}
+
 		public long getCapacity() {
 			return Long.MAX_VALUE;
 		}
diff --git a/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java b/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java
index c5ceca842c7afbc01945b5080afc7f5ed01ab63f..d59b8ae9ab254c81ef184105a3f91cb9dd0503b6 100644
--- a/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java
+++ b/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java
@@ -11,6 +11,9 @@ public interface MessagingSessionFactory {
 	MessagingSession createIncomingSession(ContactId c, TransportId t,
 			InputStream in);
 
-	MessagingSession createOutgoingSession(ContactId c, TransportId t,
-			long maxLatency, boolean duplex, OutputStream out);
+	MessagingSession createSimplexOutgoingSession(ContactId c, TransportId t,
+			long maxLatency, OutputStream out);
+
+	MessagingSession createDuplexOutgoingSession(ContactId c, TransportId t,
+			long maxLatency, long maxIdleTime, OutputStream out);
 }
diff --git a/briar-api/src/org/briarproject/api/plugins/Plugin.java b/briar-api/src/org/briarproject/api/plugins/Plugin.java
index 3389a73577d9da1ce789a25d3387d2ae87fe9b16..1048f04cff3a5cc6feb73667a3e0558e684db6c6 100644
--- a/briar-api/src/org/briarproject/api/plugins/Plugin.java
+++ b/briar-api/src/org/briarproject/api/plugins/Plugin.java
@@ -17,6 +17,9 @@ public interface Plugin {
 	/** Returns the transport's maximum latency in milliseconds. */
 	long getMaxLatency();
 
+	/** Returns the transport's maximum idle time in milliseconds. */
+	long getMaxIdleTime();
+
 	/** Starts the plugin and returns true if it started successfully. */
 	boolean start() throws IOException;
 
diff --git a/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java b/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java
index 554e5de8a110e3d24c249cf896287bdd1e5336d4..4613f0e768e0ee80a0ae1b1f3d0d932a9ad49357 100644
--- a/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java
+++ b/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java
@@ -15,6 +15,9 @@ public interface TransportConnectionWriter {
 	/** Returns the maximum latency of the transport in milliseconds. */
 	long getMaxLatency();
 
+	/** Returns the maximum idle time of the transport in milliseconds. */
+	long getMaxIdleTime();
+
 	/** Returns the capacity of the transport connection in bytes. */
 	long getCapacity();
 
diff --git a/briar-api/src/org/briarproject/api/plugins/duplex/DuplexPlugin.java b/briar-api/src/org/briarproject/api/plugins/duplex/DuplexPlugin.java
index 43fcb3c82f827804e57aba34c4838a5f9ad5c047..8712d7b7920ac554ce9dc7c0b812c2092fdcb5fa 100644
--- a/briar-api/src/org/briarproject/api/plugins/duplex/DuplexPlugin.java
+++ b/briar-api/src/org/briarproject/api/plugins/duplex/DuplexPlugin.java
@@ -7,9 +7,6 @@ import org.briarproject.api.plugins.Plugin;
 /** An interface for transport plugins that support duplex communication. */
 public interface DuplexPlugin extends Plugin {
 
-	/** Returns the transport's maximum idle time in milliseconds. */
-	long getMaxIdleTime();
-
 	/**
 	 * Attempts to create and return a connection to the given contact using
 	 * the current transport and configuration properties. Returns null if a
diff --git a/briar-core/src/org/briarproject/messaging/DuplexOutgoingSession.java b/briar-core/src/org/briarproject/messaging/DuplexOutgoingSession.java
index 9a1ac406d3acd0b0213b90839c463be3d227f78b..13e8395545bf65fa0da2b9d176067f6aaff36567 100644
--- a/briar-core/src/org/briarproject/messaging/DuplexOutgoingSession.java
+++ b/briar-core/src/org/briarproject/messaging/DuplexOutgoingSession.java
@@ -55,7 +55,6 @@ import org.briarproject.api.messaging.TransportUpdate;
  */
 class DuplexOutgoingSession implements MessagingSession, EventListener {
 
-	private static final int MAX_IDLE_TIME = 30 * 1000; // Milliseconds
 	private static final Logger LOG =
 			Logger.getLogger(DuplexOutgoingSession.class.getName());
 
@@ -69,7 +68,7 @@ class DuplexOutgoingSession implements MessagingSession, EventListener {
 	private final EventBus eventBus;
 	private final ContactId contactId;
 	private final TransportId transportId;
-	private final long maxLatency;
+	private final long maxLatency, maxIdleTime;
 	private final OutputStream out;
 	private final PacketWriter packetWriter;
 	private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
@@ -79,13 +78,14 @@ class DuplexOutgoingSession implements MessagingSession, EventListener {
 	DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
 			EventBus eventBus, PacketWriterFactory packetWriterFactory,
 			ContactId contactId, TransportId transportId, long maxLatency,
-			OutputStream out) {
+			long maxIdleTime, OutputStream out) {
 		this.db = db;
 		this.dbExecutor = dbExecutor;
 		this.eventBus = eventBus;
 		this.contactId = contactId;
 		this.transportId = transportId;
 		this.maxLatency = maxLatency;
+		this.maxIdleTime = maxIdleTime;
 		this.out = out;
 		packetWriter = packetWriterFactory.createPacketWriter(out);
 		writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
@@ -110,8 +110,8 @@ class DuplexOutgoingSession implements MessagingSession, EventListener {
 				while(!interrupted) {
 					// Flush the stream if it's going to be idle
 					if(writerTasks.isEmpty()) out.flush();
-					ThrowingRunnable<IOException> task = writerTasks.poll(
-							MAX_IDLE_TIME, MILLISECONDS);
+					ThrowingRunnable<IOException> task =
+							writerTasks.poll(maxIdleTime, MILLISECONDS);
 					if(task == null) {
 						LOG.info("Idle timeout");
 						continue; // Flush and wait again
diff --git a/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java b/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java
index 04895580ccdd61b606970c60e30c672c5ffbfdb5..9ab638aa0348909bd396690f246227450957bb9a 100644
--- a/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java
+++ b/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java
@@ -49,11 +49,16 @@ class MessagingSessionFactoryImpl implements MessagingSessionFactory {
 				messageVerifier, packetReaderFactory, c, t, in);
 	}
 
-	public MessagingSession createOutgoingSession(ContactId c, TransportId t,
-			long maxLatency, boolean duplex, OutputStream out) {
-		if(duplex) return new DuplexOutgoingSession(db, dbExecutor, eventBus,
-				packetWriterFactory, c, t, maxLatency, out);
-		else return new SimplexOutgoingSession(db, dbExecutor, eventBus,
+	public MessagingSession createSimplexOutgoingSession(ContactId c,
+			TransportId t, long maxLatency, OutputStream out) {
+		return new SimplexOutgoingSession(db, dbExecutor, eventBus,
 				packetWriterFactory, c, t, maxLatency, out);
 	}
+
+	public MessagingSession createDuplexOutgoingSession(ContactId c,
+			TransportId t, long maxLatency, long maxIdleTime,
+			OutputStream out) {
+		return new DuplexOutgoingSession(db, dbExecutor, eventBus,
+				packetWriterFactory, c, t, maxLatency, maxIdleTime, out);
+	}
 }
diff --git a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java
index d94e1f80ec0a562b5b6eed312ef4120267efd4ad..9dad32f2e28a8f6f2b443d768147098528af9844 100644
--- a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java
+++ b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java
@@ -107,14 +107,27 @@ class ConnectionManagerImpl implements ConnectionManager {
 		}
 	}
 
-	private MessagingSession createOutgoingSession(StreamContext ctx,
-			TransportConnectionWriter w, boolean duplex) throws IOException {
+	private MessagingSession createSimplexOutgoingSession(StreamContext ctx,
+			TransportConnectionWriter w) throws IOException {
 		try {
 			StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
 					w.getOutputStream(), w.getMaxFrameLength(), ctx);
-			return messagingSessionFactory.createOutgoingSession(
+			return messagingSessionFactory.createSimplexOutgoingSession(
 					ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(),
-					duplex, streamWriter.getOutputStream());
+					streamWriter.getOutputStream());
+		} finally {
+			ByteUtils.erase(ctx.getSecret());
+		}
+	}
+
+	private MessagingSession createDuplexOutgoingSession(StreamContext ctx,
+			TransportConnectionWriter w) throws IOException {
+		try {
+			StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
+					w.getOutputStream(), w.getMaxFrameLength(), ctx);
+			return messagingSessionFactory.createDuplexOutgoingSession(
+					ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(),
+					w.getMaxIdleTime(), streamWriter.getOutputStream());
 		} finally {
 			ByteUtils.erase(ctx.getSecret());
 		}
@@ -199,7 +212,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			connectionRegistry.registerConnection(contactId, transportId);
 			try {
 				// Create and run the outgoing session
-				createOutgoingSession(ctx, writer, false).run();
+				createSimplexOutgoingSession(ctx, writer).run();
 				disposeWriter(false);
 			} catch(IOException e) {
 				if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
@@ -287,7 +300,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			}
 			try {
 				// Create and run the outgoing session
-				outgoingSession = createOutgoingSession(ctx, writer, true);
+				outgoingSession = createDuplexOutgoingSession(ctx, writer);
 				outgoingSession.run();
 				disposeWriter(false);
 			} catch(IOException e) {
@@ -353,7 +366,7 @@ class ConnectionManagerImpl implements ConnectionManager {
 			});
 			try {
 				// Create and run the outgoing session
-				outgoingSession = createOutgoingSession(ctx, writer, true);
+				outgoingSession = createDuplexOutgoingSession(ctx, writer);
 				outgoingSession.run();
 				disposeWriter(false);
 			} catch(IOException e) {
diff --git a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java
index 1af4215ba6afb3fdf547cebc033cb07dde57e157..9c298b02321bf07c3eabb2ca361c1ed36a0bf1c5 100644
--- a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java
+++ b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java
@@ -56,6 +56,10 @@ public abstract class FilePlugin implements SimplexPlugin {
 		return maxLatency;
 	}
 
+	public long getMaxIdleTime() {
+		return Long.MAX_VALUE; // We don't need keepalives
+	}
+
 	public boolean isRunning() {
 		return running;
 	}
diff --git a/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java b/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java
index 2ca55593fc6cdb63b0789a4e9dac14a233b335d4..ecba989ffec1f52c9e9a8bf0dfbf308e8bb9865f 100644
--- a/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java
+++ b/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java
@@ -35,6 +35,10 @@ class FileTransportWriter implements TransportConnectionWriter {
 		return plugin.getMaxLatency();
 	}
 
+	public long getMaxIdleTime() {
+		return plugin.getMaxIdleTime();
+	}
+
 	public long getCapacity() {
 		return capacity;
 	}
diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java b/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java
index 83506a211682a611ddf4a6918079336e4530eaa4..2df916265d20a5f151025becc2285948bc2c9f5c 100644
--- a/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java
+++ b/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java
@@ -67,6 +67,10 @@ class TcpTransportConnection implements DuplexTransportConnection {
 			return plugin.getMaxLatency();
 		}
 
+		public long getMaxIdleTime() {
+			return plugin.getMaxIdleTime();
+		}
+
 		public long getCapacity() {
 			return Long.MAX_VALUE;
 		}
diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java
index 8a2c7967257c223d5e34c08444eab33663ab3afd..3de6a6a105c9f4077a0d179ce0fd98987a80cba6 100644
--- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java
+++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java
@@ -68,6 +68,10 @@ class BluetoothTransportConnection implements DuplexTransportConnection {
 			return plugin.getMaxLatency();
 		}
 
+		public long getMaxIdleTime() {
+			return plugin.getMaxIdleTime();
+		}
+
 		public long getCapacity() {
 			return Long.MAX_VALUE;
 		}
diff --git a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java
index 09ffd6f663592668cc148c7ae74af1b1cc065005..bdf9d84ef9f9062ac02186fddd09d15e547c5a6a 100644
--- a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java
+++ b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java
@@ -29,8 +29,7 @@ implements RemovableDriveMonitor.Callback {
 
 	RemovableDrivePlugin(Executor ioExecutor, FileUtils fileUtils,
 			SimplexPluginCallback callback, RemovableDriveFinder finder,
-			RemovableDriveMonitor monitor, int maxFrameLength,
-			long maxLatency) {
+			RemovableDriveMonitor monitor, int maxFrameLength, long maxLatency) {
 		super(ioExecutor, fileUtils, callback, maxFrameLength, maxLatency);
 		this.finder = finder;
 		this.monitor = monitor;
diff --git a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java
index 825fc36813e7866154430440af067f14d2b2980d..98362849775b05c8405b7449322a1dc704ff2904 100644
--- a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java
+++ b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java
@@ -223,11 +223,15 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
 		private class Writer implements TransportConnectionWriter {
 
 			public int getMaxFrameLength() {
-				return maxFrameLength;
+				return getMaxFrameLength();
 			}
 
 			public long getMaxLatency() {
-				return maxLatency;
+				return getMaxLatency();
+			}
+
+			public long getMaxIdleTime() {
+				return getMaxIdleTime();
 			}
 
 			public long getCapacity() {