diff --git a/api/net/sf/briar/api/transport/BatchTransportReader.java b/api/net/sf/briar/api/transport/BatchTransportReader.java
index f47d096e5608473b30ceafce4eba64f60dc70b45..4faa004740e516816e013386cbca342461b220d4 100644
--- a/api/net/sf/briar/api/transport/BatchTransportReader.java
+++ b/api/net/sf/briar/api/transport/BatchTransportReader.java
@@ -1,6 +1,5 @@
 package net.sf.briar.api.transport;
 
-import java.io.IOException;
 import java.io.InputStream;
 
 /**
@@ -16,5 +15,5 @@ public interface BatchTransportReader {
 	 * Closes the reader and disposes of any associated state. The argument
 	 * should be false if an exception was thrown while using the reader.
 	 */
-	void dispose(boolean success) throws IOException;
+	void dispose(boolean success);
 }
diff --git a/api/net/sf/briar/api/transport/BatchTransportWriter.java b/api/net/sf/briar/api/transport/BatchTransportWriter.java
index 344efa3107b181f1e2cc9e423aca326364d9ae7f..92fcf38aab9f97c62d14e16a5e684fca34cdeb6e 100644
--- a/api/net/sf/briar/api/transport/BatchTransportWriter.java
+++ b/api/net/sf/briar/api/transport/BatchTransportWriter.java
@@ -1,6 +1,5 @@
 package net.sf.briar.api.transport;
 
-import java.io.IOException;
 import java.io.OutputStream;
 
 /**
@@ -19,5 +18,5 @@ public interface BatchTransportWriter {
 	 * Closes the writer and disposes of any associated state. The argument
 	 * should be false if an exception was thrown while using the writer.
 	 */
-	void dispose(boolean success) throws IOException;
+	void dispose(boolean success);
 }
diff --git a/api/net/sf/briar/api/transport/StreamTransportConnection.java b/api/net/sf/briar/api/transport/StreamTransportConnection.java
index 5e65b76e1930a3cc29278cfa90d17121a277ab5b..f6b28c1acdf4b6b98a3ac994ab4c6fa4d4044b5a 100644
--- a/api/net/sf/briar/api/transport/StreamTransportConnection.java
+++ b/api/net/sf/briar/api/transport/StreamTransportConnection.java
@@ -21,5 +21,5 @@ public interface StreamTransportConnection {
 	 * Closes the connection and disposes of any associated state. The argument
 	 * should be false if an exception was thrown while using the connection.
 	 */
-	void dispose(boolean success) throws IOException;
+	void dispose(boolean success);
 }
diff --git a/api/net/sf/briar/api/transport/batch/BatchConnectionFactory.java b/api/net/sf/briar/api/transport/batch/BatchConnectionFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..28148f94bf93f8f2a7b23d45a68e7d77c2bdc67e
--- /dev/null
+++ b/api/net/sf/briar/api/transport/batch/BatchConnectionFactory.java
@@ -0,0 +1,15 @@
+package net.sf.briar.api.transport.batch;
+
+import net.sf.briar.api.ContactId;
+import net.sf.briar.api.TransportId;
+import net.sf.briar.api.transport.BatchTransportReader;
+import net.sf.briar.api.transport.BatchTransportWriter;
+
+public interface BatchConnectionFactory {
+
+	Runnable createOutgoingConnection(TransportId t, ContactId c,
+			BatchTransportWriter w);
+
+	Runnable createIncomingConnection(ContactId c, BatchTransportReader r,
+			byte[] encryptedIv);
+}
diff --git a/components/net/sf/briar/plugins/bluetooth/BluetoothTransportConnection.java b/components/net/sf/briar/plugins/bluetooth/BluetoothTransportConnection.java
index 87c8542455e0009334be283b7a71c1f5370f6192..1f484b4223ac9f666714087008bd2a3a1b96f07e 100644
--- a/components/net/sf/briar/plugins/bluetooth/BluetoothTransportConnection.java
+++ b/components/net/sf/briar/plugins/bluetooth/BluetoothTransportConnection.java
@@ -3,6 +3,8 @@ package net.sf.briar.plugins.bluetooth;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.microedition.io.StreamConnection;
 
@@ -10,6 +12,9 @@ import net.sf.briar.api.transport.StreamTransportConnection;
 
 class BluetoothTransportConnection implements StreamTransportConnection {
 
+	private static final Logger LOG =
+		Logger.getLogger(BluetoothTransportConnection.class.getName());
+
 	private final StreamConnection stream;
 
 	BluetoothTransportConnection(StreamConnection stream) {
@@ -24,7 +29,11 @@ class BluetoothTransportConnection implements StreamTransportConnection {
 		return stream.openOutputStream();
 	}
 
-	public void dispose(boolean success) throws IOException {
-		stream.close();
+	public void dispose(boolean success) {
+		try {
+			stream.close();
+		} catch(IOException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
+		}
 	}
 }
diff --git a/components/net/sf/briar/plugins/file/FileTransportReader.java b/components/net/sf/briar/plugins/file/FileTransportReader.java
index 7ab0758392f6bed09faf17039d07634a1b58e5e4..a1996df84bae216154f0180aae20f44ad954c19c 100644
--- a/components/net/sf/briar/plugins/file/FileTransportReader.java
+++ b/components/net/sf/briar/plugins/file/FileTransportReader.java
@@ -3,11 +3,16 @@ package net.sf.briar.plugins.file;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import net.sf.briar.api.transport.BatchTransportReader;
 
 class FileTransportReader implements BatchTransportReader {
 
+	private static final Logger LOG =
+		Logger.getLogger(FileTransportReader.class.getName());
+
 	private final File file;
 	private final InputStream in;
 	private final FilePlugin plugin;
@@ -22,12 +27,15 @@ class FileTransportReader implements BatchTransportReader {
 		return in;
 	}
 
-	public void dispose(boolean success) throws IOException {
+	public void dispose(boolean success) {
 		try {
 			in.close();
-			if(success) plugin.readerFinished(file);
-		} finally {
+		} catch(IOException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
+		}
+		if(success) {
 			file.delete();
+			plugin.readerFinished(file);
 		}
 	}
 }
diff --git a/components/net/sf/briar/plugins/file/FileTransportWriter.java b/components/net/sf/briar/plugins/file/FileTransportWriter.java
index ff5a90d5a20e5d9f95c48cd674552f9d752c3638..427b708673d15f703889daf6f1c4f89400172dc4 100644
--- a/components/net/sf/briar/plugins/file/FileTransportWriter.java
+++ b/components/net/sf/briar/plugins/file/FileTransportWriter.java
@@ -3,11 +3,16 @@ package net.sf.briar.plugins.file;
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import net.sf.briar.api.transport.BatchTransportWriter;
 
 class FileTransportWriter implements BatchTransportWriter {
 
+	private static final Logger LOG =
+		Logger.getLogger(FileTransportWriter.class.getName());
+
 	private final File file;
 	private final OutputStream out;
 	private final long capacity;
@@ -29,12 +34,13 @@ class FileTransportWriter implements BatchTransportWriter {
 		return out;
 	}
 
-	public void dispose(boolean success) throws IOException {
+	public void dispose(boolean success) {
 		try {
 			out.close();
-			if(success) plugin.writerFinished(file);
-		} finally {
-			file.delete();
+		} catch(IOException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
 		}
+		if(success) plugin.writerFinished(file);
+		else file.delete();
 	}
 }
diff --git a/components/net/sf/briar/plugins/socket/SocketTransportConnection.java b/components/net/sf/briar/plugins/socket/SocketTransportConnection.java
index f5b1782979a776f73c264beee7a87a8f3ed4310e..999e6e2be57b5f7c59ff47ccd9504047af5bdf82 100644
--- a/components/net/sf/briar/plugins/socket/SocketTransportConnection.java
+++ b/components/net/sf/briar/plugins/socket/SocketTransportConnection.java
@@ -4,11 +4,16 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import net.sf.briar.api.transport.StreamTransportConnection;
 
 class SocketTransportConnection implements StreamTransportConnection {
 
+	private static final Logger LOG =
+		Logger.getLogger(SocketTransportConnection.class.getName());
+
 	private final Socket socket;
 
 	SocketTransportConnection(Socket socket) {
@@ -23,7 +28,11 @@ class SocketTransportConnection implements StreamTransportConnection {
 		return socket.getOutputStream();
 	}
 
-	public void dispose(boolean success) throws IOException {
-		socket.close();
+	public void dispose(boolean success) {
+		try {
+			socket.close();
+		} catch(IOException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
+		}
 	}
 }
diff --git a/components/net/sf/briar/transport/batch/BatchConnectionFactoryImpl.java b/components/net/sf/briar/transport/batch/BatchConnectionFactoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..598e83e2d6b52d456cc1947afe989f3b27bfc3db
--- /dev/null
+++ b/components/net/sf/briar/transport/batch/BatchConnectionFactoryImpl.java
@@ -0,0 +1,47 @@
+package net.sf.briar.transport.batch;
+
+import net.sf.briar.api.ContactId;
+import net.sf.briar.api.TransportId;
+import net.sf.briar.api.db.DatabaseComponent;
+import net.sf.briar.api.protocol.ProtocolReaderFactory;
+import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
+import net.sf.briar.api.transport.BatchTransportReader;
+import net.sf.briar.api.transport.BatchTransportWriter;
+import net.sf.briar.api.transport.ConnectionReaderFactory;
+import net.sf.briar.api.transport.ConnectionWriterFactory;
+import net.sf.briar.api.transport.batch.BatchConnectionFactory;
+
+import com.google.inject.Inject;
+
+class BatchConnectionFactoryImpl implements BatchConnectionFactory {
+
+	private final ConnectionReaderFactory connReaderFactory;
+	private final ConnectionWriterFactory connWriterFactory;
+	private final DatabaseComponent db;
+	private final ProtocolReaderFactory protoReaderFactory;
+	private final ProtocolWriterFactory protoWriterFactory;
+
+	@Inject
+	BatchConnectionFactoryImpl(ConnectionReaderFactory connReaderFactory,
+			ConnectionWriterFactory connWriterFactory, DatabaseComponent db,
+			ProtocolReaderFactory protoReaderFactory,
+			ProtocolWriterFactory protoWriterFactory) {
+		this.connReaderFactory = connReaderFactory;
+		this.connWriterFactory = connWriterFactory;
+		this.db = db;
+		this.protoReaderFactory = protoReaderFactory;
+		this.protoWriterFactory = protoWriterFactory;
+	}
+
+	public Runnable createOutgoingConnection(TransportId t, ContactId c,
+			BatchTransportWriter w) {
+		return new OutgoingBatchConnection(connWriterFactory, db,
+				protoWriterFactory, t, c, w);
+	}
+
+	public Runnable createIncomingConnection(ContactId c,
+			BatchTransportReader r, byte[] encryptedIv) {
+		return new IncomingBatchConnection(connReaderFactory, db,
+				protoReaderFactory, c, r, encryptedIv);
+	}
+}
diff --git a/components/net/sf/briar/transport/batch/IncomingBatchConnection.java b/components/net/sf/briar/transport/batch/IncomingBatchConnection.java
index 20f6f5bd3704d7978931aab2f902cfc2feb3d091..ec43c1ce5800d7e550d41d29d853b7f38a3886b8 100644
--- a/components/net/sf/briar/transport/batch/IncomingBatchConnection.java
+++ b/components/net/sf/briar/transport/batch/IncomingBatchConnection.java
@@ -1,7 +1,8 @@
 package net.sf.briar.transport.batch;
 
 import java.io.IOException;
-import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.FormatException;
@@ -13,45 +14,67 @@ import net.sf.briar.api.protocol.ProtocolReader;
 import net.sf.briar.api.protocol.ProtocolReaderFactory;
 import net.sf.briar.api.protocol.SubscriptionUpdate;
 import net.sf.briar.api.protocol.TransportUpdate;
+import net.sf.briar.api.transport.BatchTransportReader;
 import net.sf.briar.api.transport.ConnectionReader;
+import net.sf.briar.api.transport.ConnectionReaderFactory;
 
-class IncomingBatchConnection {
+class IncomingBatchConnection implements Runnable {
 
-	private final ConnectionReader conn;
+	private static final Logger LOG =
+		Logger.getLogger(IncomingBatchConnection.class.getName());
+
+	private final ConnectionReaderFactory connFactory;
 	private final DatabaseComponent db;
 	private final ProtocolReaderFactory protoFactory;
 	private final ContactId contactId;
+	private final BatchTransportReader reader;
+	private final byte[] encryptedIv;
 
-	IncomingBatchConnection(ConnectionReader conn, DatabaseComponent db,
-			ProtocolReaderFactory protoFactory, ContactId contactId) {
-		this.conn = conn;
+	IncomingBatchConnection(ConnectionReaderFactory connFactory,
+			DatabaseComponent db, ProtocolReaderFactory protoFactory,
+			ContactId contactId, BatchTransportReader reader,
+			byte[] encryptedIv) {
+		this.connFactory = connFactory;
 		this.db = db;
 		this.protoFactory = protoFactory;
 		this.contactId = contactId;
+		this.reader = reader;
+		this.encryptedIv = encryptedIv;
 	}
 
-	void read() throws DbException, IOException {
-		InputStream in = conn.getInputStream();
-		ProtocolReader proto = protoFactory.createProtocolReader(in);
-		// Read packets until EOF
-		while(!proto.eof()) {
-			if(proto.hasAck()) {
-				Ack a = proto.readAck();
-				db.receiveAck(contactId, a);
-			} else if(proto.hasBatch()) {
-				Batch b = proto.readBatch();
-				db.receiveBatch(contactId, b);
-			} else if(proto.hasSubscriptionUpdate()) {
-				SubscriptionUpdate s = proto.readSubscriptionUpdate();
-				db.receiveSubscriptionUpdate(contactId, s);
-			} else if(proto.hasTransportUpdate()) {
-				TransportUpdate t = proto.readTransportUpdate();
-				db.receiveTransportUpdate(contactId, t);
-			} else {
-				throw new FormatException();
+	public void run() {
+		try {
+			byte[] secret = db.getSharedSecret(contactId);
+			ConnectionReader conn = connFactory.createConnectionReader(
+					reader.getInputStream(), encryptedIv, secret);
+			ProtocolReader proto = protoFactory.createProtocolReader(
+					conn.getInputStream());
+			// Read packets until EOF
+			while(!proto.eof()) {
+				if(proto.hasAck()) {
+					Ack a = proto.readAck();
+					db.receiveAck(contactId, a);
+				} else if(proto.hasBatch()) {
+					Batch b = proto.readBatch();
+					db.receiveBatch(contactId, b);
+				} else if(proto.hasSubscriptionUpdate()) {
+					SubscriptionUpdate s = proto.readSubscriptionUpdate();
+					db.receiveSubscriptionUpdate(contactId, s);
+				} else if(proto.hasTransportUpdate()) {
+					TransportUpdate t = proto.readTransportUpdate();
+					db.receiveTransportUpdate(contactId, t);
+				} else {
+					throw new FormatException();
+				}
 			}
+		} catch(DbException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
+			reader.dispose(false);
+		} catch(IOException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
+			reader.dispose(false);
 		}
-		// Close the input stream
-		in.close();
+		// Success
+		reader.dispose(true);
 	}
 }
diff --git a/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java b/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java
index ab154e78fe621725151623b0924172568f079718..ad2a7e57e0541b58e62e8e0af060b741bed710f9 100644
--- a/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java
+++ b/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java
@@ -4,8 +4,11 @@ import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import net.sf.briar.api.ContactId;
+import net.sf.briar.api.TransportId;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.protocol.writers.AckWriter;
@@ -13,52 +16,77 @@ import net.sf.briar.api.protocol.writers.BatchWriter;
 import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
 import net.sf.briar.api.protocol.writers.SubscriptionWriter;
 import net.sf.briar.api.protocol.writers.TransportWriter;
+import net.sf.briar.api.transport.BatchTransportWriter;
 import net.sf.briar.api.transport.ConnectionWriter;
+import net.sf.briar.api.transport.ConnectionWriterFactory;
 
-class OutgoingBatchConnection {
+class OutgoingBatchConnection implements Runnable {
 
-	private final ConnectionWriter conn;
+	private static final Logger LOG =
+		Logger.getLogger(OutgoingBatchConnection.class.getName());
+
+	private final ConnectionWriterFactory connFactory;
 	private final DatabaseComponent db;
 	private final ProtocolWriterFactory protoFactory;
+	private final TransportId transportId;
 	private final ContactId contactId;
+	private final BatchTransportWriter writer;
 
-	OutgoingBatchConnection(ConnectionWriter conn, DatabaseComponent db,
-			ProtocolWriterFactory protoFactory, ContactId contactId) {
-		this.conn = conn;
+	OutgoingBatchConnection(ConnectionWriterFactory connFactory,
+			DatabaseComponent db, ProtocolWriterFactory protoFactory,
+			TransportId transportId, ContactId contactId,
+			BatchTransportWriter writer) {
+		this.connFactory = connFactory;
 		this.db = db;
 		this.protoFactory = protoFactory;
+		this.transportId = transportId;
 		this.contactId = contactId;
+		this.writer = writer;
 	}
 
-	void write() throws DbException, IOException {
-		OutputStream out = conn.getOutputStream();
-		// There should be enough space for a packet
-		long capacity = conn.getRemainingCapacity();
-		if(capacity < MAX_PACKET_LENGTH) throw new IOException();
-		// Write a transport update
-		TransportWriter t = protoFactory.createTransportWriter(out);
-		db.generateTransportUpdate(contactId, t);
-		// If there's space, write a subscription update
-		capacity = conn.getRemainingCapacity();
-		if(capacity >= MAX_PACKET_LENGTH) {
-			SubscriptionWriter s = protoFactory.createSubscriptionWriter(out);
-			db.generateSubscriptionUpdate(contactId, s);
-		}
-		// Write acks until you can't write acks no more
-		AckWriter a = protoFactory.createAckWriter(out);
-		do {
+	public void run() {
+		try {
+			byte[] secret = db.getSharedSecret(contactId);
+			long connection = db.getConnectionNumber(contactId, transportId);
+			ConnectionWriter conn = connFactory.createConnectionWriter(
+					writer.getOutputStream(), writer.getCapacity(), true,
+					transportId, connection, secret);
+			OutputStream out = conn.getOutputStream();
+			// There should be enough space for a packet
+			long capacity = conn.getRemainingCapacity();
+			if(capacity < MAX_PACKET_LENGTH) throw new IOException();
+			// Write a transport update
+			TransportWriter t = protoFactory.createTransportWriter(out);
+			db.generateTransportUpdate(contactId, t);
+			// If there's space, write a subscription update
 			capacity = conn.getRemainingCapacity();
-			int max = (int) Math.min(MAX_PACKET_LENGTH, capacity);
-			a.setMaxPacketLength(max);
-		} while(db.generateAck(contactId, a));
-		// Write batches until you can't write batches no more
-		BatchWriter b = protoFactory.createBatchWriter(out);
-		do {
-			capacity = conn.getRemainingCapacity();
-			int max = (int) Math.min(MAX_PACKET_LENGTH, capacity);
-			b.setMaxPacketLength(max);
-		} while(db.generateBatch(contactId, b));
-		// Close the output stream
-		out.close();
+			if(capacity >= MAX_PACKET_LENGTH) {
+				SubscriptionWriter s =
+					protoFactory.createSubscriptionWriter(out);
+				db.generateSubscriptionUpdate(contactId, s);
+			}
+			// Write acks until you can't write acks no more
+			AckWriter a = protoFactory.createAckWriter(out);
+			do {
+				capacity = conn.getRemainingCapacity();
+				int max = (int) Math.min(MAX_PACKET_LENGTH, capacity);
+				a.setMaxPacketLength(max);
+			} while(db.generateAck(contactId, a));
+			// Write batches until you can't write batches no more
+			BatchWriter b = protoFactory.createBatchWriter(out);
+			do {
+				capacity = conn.getRemainingCapacity();
+				int max = (int) Math.min(MAX_PACKET_LENGTH, capacity);
+				b.setMaxPacketLength(max);
+			} while(db.generateBatch(contactId, b));
+		} catch(DbException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
+			writer.dispose(false);
+		} catch(IOException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
+			writer.dispose(false);
+		}
+		// Success
+		writer.dispose(true);
 	}
 }
diff --git a/components/net/sf/briar/transport/batch/TransportBatchModule.java b/components/net/sf/briar/transport/batch/TransportBatchModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6e7c06d4f85eaca44061112d9b9435ca26ef02c
--- /dev/null
+++ b/components/net/sf/briar/transport/batch/TransportBatchModule.java
@@ -0,0 +1,15 @@
+package net.sf.briar.transport.batch;
+
+import net.sf.briar.api.transport.batch.BatchConnectionFactory;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Singleton;
+
+public class TransportBatchModule extends AbstractModule {
+
+	@Override
+	protected void configure() {
+		bind(BatchConnectionFactory.class).to(
+				BatchConnectionFactoryImpl.class).in(Singleton.class);
+	}
+}
diff --git a/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java b/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java
index d64f20db89355d18878a950e05aded7756e5ef47..05ec383485485d9f420a734224bd5abda6b9b11e 100644
--- a/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java
+++ b/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java
@@ -282,10 +282,11 @@ public class RemovableDrivePluginTest extends TestCase {
 		out.flush();
 		out.close();
 		assertEquals(123L, files[0].length());
-		// Disposing of the writer should delete the file
+		// Successfully disposing of the writer should not delete the file
 		writer.dispose(true);
 		files = drive1.listFiles();
-		assertTrue(files == null || files.length == 0);
+		assertEquals(1, files.length);
+		assertEquals(123L, files[0].length());
 
 		context.assertIsSatisfied();
 	}
diff --git a/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java b/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java
index 4755ba44fade325c1c23158dd5f2f1c8bd64691c..64caa91e74a913254afc73518ee4fe87996ff64a 100644
--- a/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java
+++ b/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java
@@ -5,6 +5,8 @@ import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.Collections;
 import java.util.Map;
 
@@ -20,11 +22,11 @@ import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageEncoder;
 import net.sf.briar.api.protocol.ProtocolReaderFactory;
 import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
-import net.sf.briar.api.transport.ConnectionReader;
+import net.sf.briar.api.transport.BatchTransportReader;
+import net.sf.briar.api.transport.BatchTransportWriter;
 import net.sf.briar.api.transport.ConnectionReaderFactory;
 import net.sf.briar.api.transport.ConnectionRecogniser;
 import net.sf.briar.api.transport.ConnectionRecogniserFactory;
-import net.sf.briar.api.transport.ConnectionWriter;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
 import net.sf.briar.crypto.CryptoModule;
 import net.sf.briar.db.DatabaseModule;
@@ -101,15 +103,13 @@ public class BatchConnectionReadWriteTest extends TestCase {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		ConnectionWriterFactory connFactory =
 			alice.getInstance(ConnectionWriterFactory.class);
-		long connection = db.getConnectionNumber(contactId, transportId);
-		ConnectionWriter conn = connFactory.createConnectionWriter(out,
-				Long.MAX_VALUE, true, transportId, connection, aliceSecret);
 		ProtocolWriterFactory protoFactory =
 			alice.getInstance(ProtocolWriterFactory.class);
-		OutgoingBatchConnection batchOut = new OutgoingBatchConnection(conn, db,
-				protoFactory, contactId);
+		BatchTransportWriter writer = new TestBatchTransportWriter(out);
+		OutgoingBatchConnection batchOut = new OutgoingBatchConnection(
+				connFactory, db, protoFactory, transportId, contactId, writer);
 		// Write whatever needs to be written
-		batchOut.write();
+		batchOut.run();
 		// Close Alice's database
 		db.close();
 		// Return the contents of the batch connection
@@ -139,16 +139,15 @@ public class BatchConnectionReadWriteTest extends TestCase {
 		// Create an incoming batch connection
 		ConnectionReaderFactory connFactory =
 			bob.getInstance(ConnectionReaderFactory.class);
-		ConnectionReader conn = connFactory.createConnectionReader(in,
-				encryptedIv, bobSecret);
 		ProtocolReaderFactory protoFactory =
 			bob.getInstance(ProtocolReaderFactory.class);
-		IncomingBatchConnection batchIn = new IncomingBatchConnection(conn,
-				db, protoFactory, contactId);
+		BatchTransportReader reader = new TestBatchTransportReader(in);
+		IncomingBatchConnection batchIn = new IncomingBatchConnection(
+				connFactory, db, protoFactory, contactId, reader, encryptedIv);
 		// No messages should have been added yet
 		assertFalse(listener.messagesAdded);
 		// Read whatever needs to be read
-		batchIn.read();
+		batchIn.run();
 		// The private message from Alice should have been added
 		assertTrue(listener.messagesAdded);
 		// Close Bob's database
@@ -168,4 +167,44 @@ public class BatchConnectionReadWriteTest extends TestCase {
 			if(e == Event.MESSAGES_ADDED) messagesAdded = true;
 		}
 	}
+
+	private static class TestBatchTransportWriter
+	implements BatchTransportWriter {
+
+		private final OutputStream out;
+
+		private TestBatchTransportWriter(OutputStream out) {
+			this.out = out;
+		}
+
+		public long getCapacity() {
+			return Long.MAX_VALUE;
+		}
+
+		public OutputStream getOutputStream() {
+			return out;
+		}
+
+		public void dispose(boolean success) {
+			assertTrue(success);
+		}
+	}
+
+	private static class TestBatchTransportReader
+	implements BatchTransportReader {
+
+		private final InputStream in;
+
+		private TestBatchTransportReader(InputStream in) {
+			this.in = in;
+		}
+
+		public InputStream getInputStream() {
+			return in;
+		}
+
+		public void dispose(boolean success) {
+			assertTrue(success);
+		}
+	}
 }