diff --git a/api/net/sf/briar/api/protocol/Tags.java b/api/net/sf/briar/api/protocol/Tags.java
index 59726ca5accd0c1980482312ecc60bec8c2ccdc7..3b0071d4cae947cc6d5c4a486ac6f8f12cbd30eb 100644
--- a/api/net/sf/briar/api/protocol/Tags.java
+++ b/api/net/sf/briar/api/protocol/Tags.java
@@ -16,6 +16,8 @@ public interface Tags {
 	static final int GROUP_ID = 6;
 	static final int MESSAGE = 7;
 	static final int MESSAGE_ID = 8;
-	static final int SUBSCRIPTIONS = 9;
-	static final int TRANSPORTS = 10;
+	static final int OFFER = 9;
+	static final int REQUEST = 10;
+	static final int SUBSCRIPTIONS = 11;
+	static final int TRANSPORTS = 12;
 }
diff --git a/api/net/sf/briar/api/protocol/writers/PacketWriterFactory.java b/api/net/sf/briar/api/protocol/writers/PacketWriterFactory.java
index ac4d3c292c2999127c1fe7de7fe68465cfd1a45d..71699b8ad306987d7a28f73f178e4ab778488872 100644
--- a/api/net/sf/briar/api/protocol/writers/PacketWriterFactory.java
+++ b/api/net/sf/briar/api/protocol/writers/PacketWriterFactory.java
@@ -8,6 +8,10 @@ public interface PacketWriterFactory {
 
 	BatchWriter createBatchWriter(OutputStream out);
 
+	OfferWriter createOfferWriter(OutputStream out);
+
+	RequestWriter createRequestWriter(OutputStream out);
+
 	SubscriptionWriter createSubscriptionWriter(OutputStream out);
 
 	TransportWriter createTransportWriter(OutputStream out);
diff --git a/api/net/sf/briar/api/protocol/writers/RequestWriter.java b/api/net/sf/briar/api/protocol/writers/RequestWriter.java
index 2dd52d2fbfb9ebf30227e7ba667728113763e1df..335753f6c2ddcf2f7b4625e3deb427c99a3dbc4d 100644
--- a/api/net/sf/briar/api/protocol/writers/RequestWriter.java
+++ b/api/net/sf/briar/api/protocol/writers/RequestWriter.java
@@ -7,5 +7,5 @@ import java.util.BitSet;
 public interface RequestWriter {
 
 	/** Writes the contents of the request. */
-	void writeBitmap(BitSet b) throws IOException;
+	void writeBitmap(BitSet b, int length) throws IOException;
 }
diff --git a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
index 2ccb63d1da73ea1ec87c09ad32077a04f85f538d..de0139a6f24451961f045e5a63c5a9340dab376d 100644
--- a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
+++ b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
@@ -676,11 +676,10 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				try {
 					subscriptionLock.readLock().lock();
 					try {
-						BitSet request;
+						Collection<MessageId> offered = o.getMessages();
+						BitSet request = new BitSet(offered.size());
 						Txn txn = db.startTransaction();
 						try {
-							Collection<MessageId> offered = o.getMessages();
-							request = new BitSet(offered.size());
 							Iterator<MessageId> it = offered.iterator();
 							for(int i = 0; it.hasNext(); i++) {
 								// If the message is not in the database, or if
@@ -694,7 +693,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 							db.abortTransaction(txn);
 							throw e;
 						}
-						r.writeBitmap(request);
+						r.writeBitmap(request, offered.size());
 					} finally {
 						subscriptionLock.readLock().unlock();
 					}
diff --git a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java
index c6fd5e706fb9d0dea65f9d4ce8c7935b66ad0840..079ef8254c6c4a6d810b365f4bc8defe5d7effb9 100644
--- a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java
+++ b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java
@@ -497,11 +497,10 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			synchronized(messageLock) {
 				synchronized(messageStatusLock) {
 					synchronized(subscriptionLock) {
-						BitSet request;
+						Collection<MessageId> offered = o.getMessages();
+						BitSet request = new BitSet(offered.size());
 						Txn txn = db.startTransaction();
 						try {
-							Collection<MessageId> offered = o.getMessages();
-							request = new BitSet(offered.size());
 							Iterator<MessageId> it = offered.iterator();
 							for(int i = 0; it.hasNext(); i++) {
 								// If the message is not in the database, or if
@@ -515,7 +514,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 							db.abortTransaction(txn);
 							throw e;
 						}
-						r.writeBitmap(request);
+						r.writeBitmap(request, offered.size());
 					}
 				}
 			}
diff --git a/components/net/sf/briar/protocol/AckReader.java b/components/net/sf/briar/protocol/AckReader.java
index 93c88fc8ab7277bf3d3911669a7201982df7f6d7..bffaa76c16e3cf565f26fdaee047e748aa4ccaf3 100644
--- a/components/net/sf/briar/protocol/AckReader.java
+++ b/components/net/sf/briar/protocol/AckReader.java
@@ -9,11 +9,14 @@ import net.sf.briar.api.protocol.Tags;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
+import com.google.inject.Inject;
+
 class AckReader implements ObjectReader<Ack> {
 
 	private final ObjectReader<BatchId> batchIdReader;
 	private final AckFactory ackFactory;
 
+	@Inject
 	AckReader(ObjectReader<BatchId> batchIdReader, AckFactory ackFactory) {
 		this.batchIdReader = batchIdReader;
 		this.ackFactory = ackFactory;
diff --git a/components/net/sf/briar/protocol/AuthorReader.java b/components/net/sf/briar/protocol/AuthorReader.java
index 7988f0d59f3cf5d428695811a568e12f4153c945..8a25ffc178769fd435acf5b3bf1dc4078b76bcbb 100644
--- a/components/net/sf/briar/protocol/AuthorReader.java
+++ b/components/net/sf/briar/protocol/AuthorReader.java
@@ -11,11 +11,14 @@ import net.sf.briar.api.protocol.Tags;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
+import com.google.inject.Inject;
+
 class AuthorReader implements ObjectReader<Author> {
 
 	private final MessageDigest messageDigest;
 	private final AuthorFactory authorFactory;
 
+	@Inject
 	AuthorReader(CryptoComponent crypto, AuthorFactory authorFactory) {
 		messageDigest = crypto.getMessageDigest();
 		this.authorFactory = authorFactory;
diff --git a/components/net/sf/briar/protocol/BatchReader.java b/components/net/sf/briar/protocol/BatchReader.java
index 4b467e0304ddf5819e30b6601fe0f4b3096529a4..95b3ae3446087916241410e42f6f825ef81cd700 100644
--- a/components/net/sf/briar/protocol/BatchReader.java
+++ b/components/net/sf/briar/protocol/BatchReader.java
@@ -12,12 +12,15 @@ import net.sf.briar.api.protocol.Tags;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
+import com.google.inject.Inject;
+
 class BatchReader implements ObjectReader<Batch> {
 
 	private final MessageDigest messageDigest;
 	private final ObjectReader<Message> messageReader;
 	private final BatchFactory batchFactory;
 
+	@Inject
 	BatchReader(CryptoComponent crypto, ObjectReader<Message> messageReader,
 			BatchFactory batchFactory) {
 		messageDigest = crypto.getMessageDigest();
diff --git a/components/net/sf/briar/protocol/GroupReader.java b/components/net/sf/briar/protocol/GroupReader.java
index ceadff74bd702c5fa0ff0d2c474bfd8c89f61670..ef2d4b9d739514e089f3453498640da1b662e30d 100644
--- a/components/net/sf/briar/protocol/GroupReader.java
+++ b/components/net/sf/briar/protocol/GroupReader.java
@@ -11,11 +11,14 @@ import net.sf.briar.api.protocol.Tags;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
+import com.google.inject.Inject;
+
 class GroupReader implements ObjectReader<Group> {
 
 	private final MessageDigest messageDigest;
 	private final GroupFactory groupFactory;
 
+	@Inject
 	GroupReader(CryptoComponent crypto, GroupFactory groupFactory) {
 		messageDigest = crypto.getMessageDigest();
 		this.groupFactory = groupFactory;
diff --git a/components/net/sf/briar/protocol/MessageReader.java b/components/net/sf/briar/protocol/MessageReader.java
index d80a6dfa5cee7ef5b6aabe2513acb8ee7928ab71..bc206e00b4e9f72b2957fb6fb0714ae7076b0374 100644
--- a/components/net/sf/briar/protocol/MessageReader.java
+++ b/components/net/sf/briar/protocol/MessageReader.java
@@ -19,6 +19,8 @@ import net.sf.briar.api.serial.FormatException;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
+import com.google.inject.Inject;
+
 class MessageReader implements ObjectReader<Message> {
 
 	private final ObjectReader<Group> groupReader;
@@ -27,6 +29,7 @@ class MessageReader implements ObjectReader<Message> {
 	private final Signature signature;
 	private final MessageDigest messageDigest;
 
+	@Inject
 	MessageReader(CryptoComponent crypto, ObjectReader<Group> groupReader,
 			ObjectReader<Author> authorReader) {
 		this.groupReader = groupReader;
diff --git a/components/net/sf/briar/protocol/ProtocolModule.java b/components/net/sf/briar/protocol/ProtocolModule.java
index 09c45dfb16f216cbd0b90965ce48717e89ad203d..c5ea605579b52f2e1ff580b0b50423b08bf4520a 100644
--- a/components/net/sf/briar/protocol/ProtocolModule.java
+++ b/components/net/sf/briar/protocol/ProtocolModule.java
@@ -1,10 +1,17 @@
 package net.sf.briar.protocol;
 
+import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.protocol.Author;
 import net.sf.briar.api.protocol.AuthorFactory;
+import net.sf.briar.api.protocol.BatchId;
+import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupFactory;
+import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageEncoder;
+import net.sf.briar.api.serial.ObjectReader;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
 
 public class ProtocolModule extends AbstractModule {
 
@@ -14,6 +21,32 @@ public class ProtocolModule extends AbstractModule {
 		bind(AuthorFactory.class).to(AuthorFactoryImpl.class);
 		bind(BatchFactory.class).to(BatchFactoryImpl.class);
 		bind(GroupFactory.class).to(GroupFactoryImpl.class);
+		bind(SubscriptionFactory.class).to(SubscriptionFactoryImpl.class);
+		bind(TransportFactory.class).to(TransportFactoryImpl.class);
 		bind(MessageEncoder.class).to(MessageEncoderImpl.class);
 	}
+
+	@Provides
+	ObjectReader<BatchId> getBatchIdReader() {
+		return new BatchIdReader();
+	}
+
+	@Provides
+	ObjectReader<Group> getGroupReader(CryptoComponent crypto,
+			GroupFactory groupFactory) {
+		return new GroupReader(crypto, groupFactory);
+	}
+
+	@Provides
+	ObjectReader<Author> getAuthorReader(CryptoComponent crypto,
+			AuthorFactory authorFactory) {
+		return new AuthorReader(crypto, authorFactory);
+	}
+
+	@Provides
+	ObjectReader<Message> getMessageReader(CryptoComponent crypto,
+			ObjectReader<Group> groupReader,
+			ObjectReader<Author> authorReader) {
+		return new MessageReader(crypto, groupReader, authorReader);
+	}
 }
diff --git a/components/net/sf/briar/protocol/SubscriptionReader.java b/components/net/sf/briar/protocol/SubscriptionReader.java
index e4902137111d8d240856c37d2c7888a1882d9129..88a693b26a7a95284109eb3f9ade39c94b608a8e 100644
--- a/components/net/sf/briar/protocol/SubscriptionReader.java
+++ b/components/net/sf/briar/protocol/SubscriptionReader.java
@@ -9,11 +9,14 @@ import net.sf.briar.api.protocol.Tags;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
+import com.google.inject.Inject;
+
 class SubscriptionReader implements ObjectReader<Subscriptions> {
 
 	private final ObjectReader<Group> groupReader;
 	private final SubscriptionFactory subscriptionFactory;
 
+	@Inject
 	SubscriptionReader(ObjectReader<Group> groupReader,
 			SubscriptionFactory subscriptionFactory) {
 		this.groupReader = groupReader;
diff --git a/components/net/sf/briar/protocol/TransportReader.java b/components/net/sf/briar/protocol/TransportReader.java
index 580d0ffd47d1a4289a9dd55a7d3b216b9e022611..41d5f21f6f10ed42216d79bdee6b1dbc0285f992 100644
--- a/components/net/sf/briar/protocol/TransportReader.java
+++ b/components/net/sf/briar/protocol/TransportReader.java
@@ -8,10 +8,13 @@ import net.sf.briar.api.protocol.Transports;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
+import com.google.inject.Inject;
+
 class TransportReader implements ObjectReader<Transports> {
 
 	private final TransportFactory transportFactory;
 
+	@Inject
 	TransportReader(TransportFactory transportFactory) {
 		this.transportFactory = transportFactory;
 	}
diff --git a/components/net/sf/briar/protocol/writers/AckWriterImpl.java b/components/net/sf/briar/protocol/writers/AckWriterImpl.java
index fbc854f0feb2f6d8f7f4cf858fc76b4eda42f4cd..6d3648c214a4f692aeb5cb01d82b7f33f483b6d1 100644
--- a/components/net/sf/briar/protocol/writers/AckWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/AckWriterImpl.java
@@ -15,7 +15,7 @@ class AckWriterImpl implements AckWriter {
 	private final OutputStream out;
 	private final Writer w;
 
-	private boolean started = false, finished = false;
+	private boolean started = false;
 
 	AckWriterImpl(OutputStream out, WriterFactory writerFactory) {
 		this.out = out;
@@ -23,7 +23,6 @@ class AckWriterImpl implements AckWriter {
 	}
 
 	public boolean writeBatchId(BatchId b) throws IOException {
-		if(finished) throw new IllegalStateException();
 		if(!started) {
 			w.writeUserDefinedTag(Tags.ACK);
 			w.writeListStart();
@@ -36,7 +35,6 @@ class AckWriterImpl implements AckWriter {
 	}
 
 	public void finish() throws IOException {
-		if(finished) throw new IllegalStateException();
 		if(!started) {
 			w.writeUserDefinedTag(Tags.ACK);
 			w.writeListStart();
@@ -44,6 +42,6 @@ class AckWriterImpl implements AckWriter {
 		}
 		w.writeListEnd();
 		out.flush();
-		finished = true;
+		started = false;
 	}
 }
diff --git a/components/net/sf/briar/protocol/writers/BatchWriterImpl.java b/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
index 1c026127f0f758f099d5ce89d737af20c1d25484..e0c518642062210a8dd5b87e2c519c2180f4354d 100644
--- a/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
@@ -18,7 +18,7 @@ class BatchWriterImpl implements BatchWriter {
 	private final Writer w;
 	private final MessageDigest messageDigest;
 
-	private boolean started = false, finished = false;
+	private boolean started = false;
 
 	BatchWriterImpl(OutputStream out, WriterFactory writerFactory,
 			MessageDigest messageDigest) {
@@ -32,7 +32,6 @@ class BatchWriterImpl implements BatchWriter {
 	}
 
 	public boolean writeMessage(byte[] message) throws IOException {
-		if(finished) throw new IllegalStateException();
 		if(!started) {
 			messageDigest.reset();
 			w.writeUserDefinedTag(Tags.BATCH);
@@ -47,7 +46,6 @@ class BatchWriterImpl implements BatchWriter {
 	}
 
 	public BatchId finish() throws IOException {
-		if(finished) throw new IllegalStateException();
 		if(!started) {
 			messageDigest.reset();
 			w.writeUserDefinedTag(Tags.BATCH);
@@ -56,7 +54,7 @@ class BatchWriterImpl implements BatchWriter {
 		}
 		w.writeListEnd();
 		out.flush();
-		finished = true;
+		started = false;
 		return new BatchId(messageDigest.digest());
 	}
 }
diff --git a/components/net/sf/briar/protocol/writers/OfferWriterImpl.java b/components/net/sf/briar/protocol/writers/OfferWriterImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0ec46fe94dbd01d5c6f43845fc5fbe35f5ee761
--- /dev/null
+++ b/components/net/sf/briar/protocol/writers/OfferWriterImpl.java
@@ -0,0 +1,49 @@
+package net.sf.briar.protocol.writers;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import net.sf.briar.api.protocol.MessageId;
+import net.sf.briar.api.protocol.Offer;
+import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.writers.OfferWriter;
+import net.sf.briar.api.serial.Writer;
+import net.sf.briar.api.serial.WriterFactory;
+
+class OfferWriterImpl implements OfferWriter {
+
+	private final OutputStream out;
+	private final Writer w;
+
+	private boolean started = false, finished = false;
+
+	OfferWriterImpl(OutputStream out, WriterFactory writerFactory) {
+		this.out = out;
+		w = writerFactory.createWriter(out);
+	}
+
+	public boolean writeMessageId(MessageId m) throws IOException {
+		if(finished) throw new IllegalStateException();
+		if(!started) {
+			w.writeUserDefinedTag(Tags.OFFER);
+			w.writeListStart();
+			started = true;
+		}
+		int capacity = Offer.MAX_SIZE - (int) w.getBytesWritten() - 1;
+		if(capacity < MessageId.SERIALISED_LENGTH) return false;
+		m.writeTo(w);
+		return true;
+	}
+
+	public void finish() throws IOException {
+		if(finished) throw new IllegalStateException();
+		if(!started) {
+			w.writeUserDefinedTag(Tags.OFFER);
+			w.writeListStart();
+			started = true;
+		}
+		w.writeListEnd();
+		out.flush();
+		finished = true;
+	}
+}
diff --git a/components/net/sf/briar/protocol/writers/PacketWriterFactoryImpl.java b/components/net/sf/briar/protocol/writers/PacketWriterFactoryImpl.java
index 7d0959db5460ecee61f651e1ff25f90eafe9bf3b..5daaf2361a8145817193b186bdd0172f51917d2e 100644
--- a/components/net/sf/briar/protocol/writers/PacketWriterFactoryImpl.java
+++ b/components/net/sf/briar/protocol/writers/PacketWriterFactoryImpl.java
@@ -6,7 +6,9 @@ import java.security.MessageDigest;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.protocol.writers.AckWriter;
 import net.sf.briar.api.protocol.writers.BatchWriter;
+import net.sf.briar.api.protocol.writers.OfferWriter;
 import net.sf.briar.api.protocol.writers.PacketWriterFactory;
+import net.sf.briar.api.protocol.writers.RequestWriter;
 import net.sf.briar.api.protocol.writers.SubscriptionWriter;
 import net.sf.briar.api.protocol.writers.TransportWriter;
 import net.sf.briar.api.serial.WriterFactory;
@@ -33,6 +35,14 @@ class PacketWriterFactoryImpl implements PacketWriterFactory {
 		return new BatchWriterImpl(out, writerFactory, messageDigest);
 	}
 
+	public OfferWriter createOfferWriter(OutputStream out) {
+		return new OfferWriterImpl(out, writerFactory);
+	}
+
+	public RequestWriter createRequestWriter(OutputStream out) {
+		return new RequestWriterImpl(out, writerFactory);
+	}
+
 	public SubscriptionWriter createSubscriptionWriter(OutputStream out) {
 		return new SubscriptionWriterImpl(out, writerFactory);
 	}
diff --git a/components/net/sf/briar/protocol/writers/RequestWriterImpl.java b/components/net/sf/briar/protocol/writers/RequestWriterImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..28bce226a28ac08e43d0eac325d98deffe3a613d
--- /dev/null
+++ b/components/net/sf/briar/protocol/writers/RequestWriterImpl.java
@@ -0,0 +1,38 @@
+package net.sf.briar.protocol.writers;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.BitSet;
+
+import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.writers.RequestWriter;
+import net.sf.briar.api.serial.Writer;
+import net.sf.briar.api.serial.WriterFactory;
+
+class RequestWriterImpl implements RequestWriter {
+
+	private final OutputStream out;
+	private final Writer w;
+
+	RequestWriterImpl(OutputStream out, WriterFactory writerFactory) {
+		this.out = out;
+		w = writerFactory.createWriter(out);
+	}
+
+	public void writeBitmap(BitSet b, int length) throws IOException {
+		w.writeUserDefinedTag(Tags.REQUEST);
+		// If the number of bits isn't a multiple of 8, round up to a byte
+		int bytes = length % 8 == 0 ? length / 8 : length / 8 + 1;
+		byte[] bitmap = new byte[bytes];
+		// I'm kind of surprised BitSet doesn't have a method for this
+		for(int i = 0; i < length; i++) {
+			if(b.get(i)) {
+				int offset = i / 8;
+				byte bit = (byte) (128 >> i % 8);
+				bitmap[offset] |= bit;
+			}
+		}
+		w.writeBytes(bitmap);
+		out.flush();
+	}
+}
diff --git a/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java b/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java
index ad4f6081bdeb9567fd78f386378388f77a7a1f32..67e5736c2ae0fab17a4510f53d71a2bfe77f4665 100644
--- a/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java
@@ -15,19 +15,15 @@ class SubscriptionWriterImpl implements SubscriptionWriter {
 	private final OutputStream out;
 	private final Writer w;
 
-	private boolean used = false;
-
 	SubscriptionWriterImpl(OutputStream out, WriterFactory writerFactory) {
 		this.out = out;
 		w = writerFactory.createWriter(out);
 	}
 
 	public void writeSubscriptions(Collection<Group> subs) throws IOException {
-		if(used) throw new IllegalStateException();
 		w.writeUserDefinedTag(Tags.SUBSCRIPTIONS);
 		w.writeList(subs);
 		w.writeInt64(System.currentTimeMillis());
 		out.flush();
-		used = true;
 	}
 }
diff --git a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java
index 0bcca53c541a6137a35a4f0d87538307abf63329..495e001b937d7363e7b6ef23b8eb3c9fe69f5b60 100644
--- a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java
@@ -14,8 +14,6 @@ class TransportWriterImpl implements TransportWriter {
 	private final OutputStream out;
 	private final Writer w;
 
-	private boolean used = false;
-
 	TransportWriterImpl(OutputStream out, WriterFactory writerFactory) {
 		this.out = out;
 		w = writerFactory.createWriter(out);
@@ -23,11 +21,9 @@ class TransportWriterImpl implements TransportWriter {
 
 	public void writeTransports(Map<String, String> transports)
 	throws IOException {
-		if(used) throw new IllegalStateException();
 		w.writeUserDefinedTag(Tags.TRANSPORTS);
 		w.writeMap(transports);
 		w.writeInt64(System.currentTimeMillis());
 		out.flush();
-		used = true;
 	}
 }
diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java
index 32faacbb12a198f8d64b2776ca50b4ec977f5cb0..6f843d19116ee71369242eb609e21a987c1b706e 100644
--- a/test/net/sf/briar/db/DatabaseComponentTest.java
+++ b/test/net/sf/briar/db/DatabaseComponentTest.java
@@ -948,7 +948,7 @@ public abstract class DatabaseComponentTest extends TestCase {
 			will(returnValue(true)); // Visible - do not request message # 1
 			oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId2);
 			will(returnValue(false)); // Not visible - request message # 2
-			oneOf(requestWriter).writeBitmap(expectedRequest);
+			oneOf(requestWriter).writeBitmap(expectedRequest, 3);
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, cleaner);
 
diff --git a/test/net/sf/briar/protocol/FileReadWriteTest.java b/test/net/sf/briar/protocol/FileReadWriteTest.java
index 4fe36afc2320d5f0578a213e9a8276e06ac5e4d8..695008fa5b3e9b7d15cde8a31facc7d693f19d33 100644
--- a/test/net/sf/briar/protocol/FileReadWriteTest.java
+++ b/test/net/sf/briar/protocol/FileReadWriteTest.java
@@ -34,7 +34,6 @@ import net.sf.briar.api.protocol.writers.SubscriptionWriter;
 import net.sf.briar.api.protocol.writers.TransportWriter;
 import net.sf.briar.api.serial.Reader;
 import net.sf.briar.api.serial.ReaderFactory;
-import net.sf.briar.api.serial.WriterFactory;
 import net.sf.briar.crypto.CryptoModule;
 import net.sf.briar.protocol.writers.WritersModule;
 import net.sf.briar.serial.SerialModule;
@@ -55,9 +54,12 @@ public class FileReadWriteTest extends TestCase {
 	private final long start = System.currentTimeMillis();
 
 	private final ReaderFactory readerFactory;
-	private final WriterFactory writerFactory;
 	private final PacketWriterFactory packetWriterFactory;
 	private final CryptoComponent crypto;
+	private final AckReader ackReader;
+	private final BatchReader batchReader;
+	private final SubscriptionReader subscriptionReader;
+	private final TransportReader transportReader;
 	private final Author author;
 	private final Group group, group1;
 	private final Message message, message1, message2, message3;
@@ -70,11 +72,14 @@ public class FileReadWriteTest extends TestCase {
 				new ProtocolModule(), new SerialModule(),
 				new WritersModule());
 		readerFactory = i.getInstance(ReaderFactory.class);
-		writerFactory = i.getInstance(WriterFactory.class);
 		packetWriterFactory = i.getInstance(PacketWriterFactory.class);
 		crypto = i.getInstance(CryptoComponent.class);
 		assertEquals(crypto.getMessageDigest().getDigestLength(),
 				UniqueId.LENGTH);
+		ackReader = i.getInstance(AckReader.class);
+		batchReader = i.getInstance(BatchReader.class);
+		subscriptionReader = i.getInstance(SubscriptionReader.class);
+		transportReader = i.getInstance(TransportReader.class);
 		// Create two groups: one restricted, one unrestricted
 		GroupFactory groupFactory = i.getInstance(GroupFactory.class);
 		group = groupFactory.createGroup("Unrestricted group", null);
@@ -139,21 +144,6 @@ public class FileReadWriteTest extends TestCase {
 
 		testWriteFile();
 
-		GroupReader groupReader = new GroupReader(crypto,
-				new GroupFactoryImpl(crypto, writerFactory));
-		AuthorReader authorReader = new AuthorReader(crypto,
-				new AuthorFactoryImpl(crypto, writerFactory));
-		MessageReader messageReader = new MessageReader(crypto, groupReader,
-				authorReader);
-		AckReader ackReader = new AckReader(new BatchIdReader(),
-				new AckFactoryImpl());
-		BatchReader batchReader = new BatchReader(crypto, messageReader,
-				new BatchFactoryImpl());
-		SubscriptionReader subscriptionReader =
-			new SubscriptionReader(groupReader, new SubscriptionFactoryImpl());
-		TransportReader transportReader =
-			new TransportReader(new TransportFactoryImpl());
-
 		FileInputStream in = new FileInputStream(file);
 		Reader reader = readerFactory.createReader(in);
 		reader.addObjectReader(Tags.ACK, ackReader);
diff --git a/test/net/sf/briar/protocol/writers/RequestWriterImplTest.java b/test/net/sf/briar/protocol/writers/RequestWriterImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b612f95bb14873d902898a81ebf568b7caea1226
--- /dev/null
+++ b/test/net/sf/briar/protocol/writers/RequestWriterImplTest.java
@@ -0,0 +1,70 @@
+package net.sf.briar.protocol.writers;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.BitSet;
+
+import junit.framework.TestCase;
+import net.sf.briar.api.protocol.writers.RequestWriter;
+import net.sf.briar.api.serial.WriterFactory;
+import net.sf.briar.serial.SerialModule;
+import net.sf.briar.util.StringUtils;
+
+import org.junit.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+public class RequestWriterImplTest extends TestCase {
+
+	private final WriterFactory writerFactory;
+
+	public RequestWriterImplTest() {
+		super();
+		Injector i = Guice.createInjector(new SerialModule());
+		writerFactory = i.getInstance(WriterFactory.class);
+	}
+
+	@Test
+	public void testWriteBitmapNoPadding() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		RequestWriter r = new RequestWriterImpl(out, writerFactory);
+		BitSet b = new BitSet();
+		// 11011001 = 0xD9
+		b.set(0);
+		b.set(1);
+		b.set(3);
+		b.set(4);
+		b.set(7);
+		// 01011001 = 0x59
+		b.set(9);
+		b.set(11);
+		b.set(12);
+		b.set(15);
+		r.writeBitmap(b, 16);
+		// Short user tag 10, short bytes with length 2, 0xD959
+		byte[] output = out.toByteArray();
+		assertEquals("CA" + "92" + "D959", StringUtils.toHexString(output));
+	}
+
+	@Test
+	public void testWriteBitmapWithPadding() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		RequestWriter r = new RequestWriterImpl(out, writerFactory);
+		BitSet b = new BitSet();
+		// 01011001 = 0x59
+		b.set(1);
+		b.set(3);
+		b.set(4);
+		b.set(7);
+		// 11011xxx = 0xD8, after padding
+		b.set(8);
+		b.set(9);
+		b.set(11);
+		b.set(12);
+		r.writeBitmap(b, 13);
+		// Short user tag 10, short bytes with length 2, 0x59D8
+		byte[] output = out.toByteArray();
+		assertEquals("CA" + "92" + "59D8", StringUtils.toHexString(output));
+	}
+}