diff --git a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java b/briar-android/src/org/briarproject/android/contact/ContactListActivity.java
index 02a244c23ca8cece8ea00256a670315e3e43de7d..e167eb20bac6bbbc492369d59bb9d1a225c2072b 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListActivity.java
@@ -37,7 +37,6 @@ import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.EventListener;
 import org.briarproject.api.event.MessageAddedEvent;
-import org.briarproject.api.event.MessageExpiredEvent;
 import org.briarproject.api.plugins.ConnectionRegistry;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageHeader;
@@ -73,7 +72,6 @@ EventListener {
 	private ContactListAdapter adapter = null;
 	private ListView list = null;
 	private ListLoadingProgressBar loading = null;
-	private ImageButton addContactButton = null;
 
 	// Fields that are accessed from background threads must be volatile
 	@Inject private volatile DatabaseComponent db;
@@ -115,7 +113,7 @@ EventListener {
 		footer.setGravity(CENTER);
 		Resources res = getResources();
 		footer.setBackgroundColor(res.getColor(R.color.button_bar_background));
-		addContactButton = new ImageButton(this);
+		ImageButton addContactButton = new ImageButton(this);
 		addContactButton.setBackgroundResource(0);
 		addContactButton.setImageResource(R.drawable.social_add_person);
 		addContactButton.setOnClickListener(this);
@@ -269,6 +267,7 @@ EventListener {
 
 	public void eventOccurred(Event e) {
 		if (e instanceof ContactAddedEvent) {
+			LOG.info("Contact added, reloading");
 			loadContacts();
 		} else if (e instanceof ContactConnectedEvent) {
 			setConnected(((ContactConnectedEvent) e).getContactId(), true);
@@ -282,9 +281,6 @@ EventListener {
 			ContactId source = ((MessageAddedEvent) e).getContactId();
 			if (source == null) loadContacts();
 			else reloadContact(source);
-		} else if (e instanceof MessageExpiredEvent) {
-			LOG.info("Message expired, reloading");
-			loadContacts();
 		}
 	}
 
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
index fc4fcb4d9a73423b0f317b889d62586d34b2f548..9506d616148348470ce7b8d5fff98aa03d869e73 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
@@ -36,7 +36,6 @@ import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.EventListener;
 import org.briarproject.api.event.MessageAddedEvent;
-import org.briarproject.api.event.MessageExpiredEvent;
 import org.briarproject.api.event.MessagesAckedEvent;
 import org.briarproject.api.event.MessagesSentEvent;
 import org.briarproject.api.plugins.ConnectionRegistry;
@@ -346,9 +345,6 @@ implements EventListener, OnClickListener, OnItemClickListener {
 				LOG.info("Message added, reloading");
 				loadHeaders();
 			}
-		} else if (e instanceof MessageExpiredEvent) {
-			LOG.info("Message expired, reloading");
-			loadHeaders();
 		} else if (e instanceof MessagesSentEvent) {
 			MessagesSentEvent m = (MessagesSentEvent) e;
 			if (m.getContactId().equals(contactId)) {
diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
index 3c4b5a2674b142f8db42ce6a09959aed8668ed34..26410d25dcc5e9fc973faa1af9a3bc18a04c94d9 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
@@ -27,7 +27,6 @@ import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.EventListener;
 import org.briarproject.api.event.MessageAddedEvent;
-import org.briarproject.api.event.MessageExpiredEvent;
 import org.briarproject.api.event.SubscriptionRemovedEvent;
 import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.GroupId;
@@ -317,9 +316,6 @@ OnClickListener, OnItemClickListener {
 				LOG.info("Message added, reloading");
 				loadHeaders();
 			}
-		} else if (e instanceof MessageExpiredEvent) {
-			LOG.info("Message expired, reloading");
-			loadHeaders();
 		} else if (e instanceof SubscriptionRemovedEvent) {
 			SubscriptionRemovedEvent s = (SubscriptionRemovedEvent) e;
 			if (s.getGroup().getId().equals(groupId)) {
diff --git a/briar-android/src/org/briarproject/android/forum/ForumListActivity.java b/briar-android/src/org/briarproject/android/forum/ForumListActivity.java
index 38f1d4356111c769c7de3762963b8d6582a1d760..e00e4ab2ce6a86eb2293ae55d34eb9d428b63ad0 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumListActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumListActivity.java
@@ -30,7 +30,6 @@ import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.EventListener;
 import org.briarproject.api.event.MessageAddedEvent;
-import org.briarproject.api.event.MessageExpiredEvent;
 import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
 import org.briarproject.api.event.SubscriptionAddedEvent;
 import org.briarproject.api.event.SubscriptionRemovedEvent;
@@ -259,9 +258,6 @@ OnCreateContextMenuListener {
 				LOG.info("Message added, reloading");
 				loadHeaders(g);
 			}
-		} else if (e instanceof MessageExpiredEvent) {
-			LOG.info("Message expired, reloading");
-			loadHeaders();
 		} else if (e instanceof RemoteSubscriptionsUpdatedEvent) {
 			LOG.info("Remote subscriptions changed, reloading");
 			loadAvailable();
diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
index 9aff24206c413deb01aa4c34f424048addd80444..3defbf17bc548febaf9e511f67925e62bca2b80d 100644
--- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
+++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
@@ -16,8 +16,6 @@ import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.RetentionAck;
-import org.briarproject.api.sync.RetentionUpdate;
 import org.briarproject.api.sync.SubscriptionAck;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.TransportAck;
@@ -108,20 +106,6 @@ public interface DatabaseComponent {
 	Collection<byte[]> generateRequestedBatch(ContactId c, int maxLength,
 			int maxLatency) throws DbException;
 
-	/**
-	 * Returns a retention ack for the given contact, or null if no retention
-	 * ack is due.
-	 */
-	RetentionAck generateRetentionAck(ContactId c) throws DbException;
-
-	/**
-	 * Returns a retention update for the given contact, for transmission
-	 * over a transport with the given latency. Returns null if no update is
-	 * due.
-	 */
-	RetentionUpdate generateRetentionUpdate(ContactId c, int maxLatency)
-			throws DbException;
-
 	/**
 	 * Returns a subscription ack for the given contact, or null if no
 	 * subscription ack is due.
@@ -263,13 +247,6 @@ public interface DatabaseComponent {
 	/** Processes a request from the given contact. */
 	void receiveRequest(ContactId c, Request r) throws DbException;
 
-	/** Processes a retention ack from the given contact. */
-	void receiveRetentionAck(ContactId c, RetentionAck a) throws DbException;
-
-	/** Processes a retention update from the given contact. */
-	void receiveRetentionUpdate(ContactId c, RetentionUpdate u)
-			throws DbException;
-
 	/** Processes a subscription ack from the given contact. */
 	void receiveSubscriptionAck(ContactId c, SubscriptionAck a)
 			throws DbException;
diff --git a/briar-api/src/org/briarproject/api/event/MessageExpiredEvent.java b/briar-api/src/org/briarproject/api/event/MessageExpiredEvent.java
deleted file mode 100644
index 9b708a7c44149b80373babe3ec410440cdcd4090..0000000000000000000000000000000000000000
--- a/briar-api/src/org/briarproject/api/event/MessageExpiredEvent.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.briarproject.api.event;
-
-/**
- * An event that is broadcast when one or messages expire from the database,
- * potentially changing the database's retention time.
- */
-public class MessageExpiredEvent extends Event {
-
-}
diff --git a/briar-api/src/org/briarproject/api/event/RemoteRetentionTimeUpdatedEvent.java b/briar-api/src/org/briarproject/api/event/RemoteRetentionTimeUpdatedEvent.java
deleted file mode 100644
index 2da4f893cedbcd6acfc621116da873b7ae58fe7b..0000000000000000000000000000000000000000
--- a/briar-api/src/org/briarproject/api/event/RemoteRetentionTimeUpdatedEvent.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.briarproject.api.event;
-
-import org.briarproject.api.ContactId;
-
-/**
- * An event that is broadcast when the retention time of a contact's database
- * changes.
- */
-public class RemoteRetentionTimeUpdatedEvent extends Event {
-
-	private final ContactId contactId;
-
-	public RemoteRetentionTimeUpdatedEvent(ContactId contactId) {
-		this.contactId = contactId;
-	}
-
-	public ContactId getContactId() {
-		return contactId;
-	}
-}
diff --git a/briar-api/src/org/briarproject/api/sync/PacketReader.java b/briar-api/src/org/briarproject/api/sync/PacketReader.java
index b6c6e61e0093af60cf0223eb929483ded8e5144e..8ee829e9f0b75913527cb549c225ffe11fdb126e 100644
--- a/briar-api/src/org/briarproject/api/sync/PacketReader.java
+++ b/briar-api/src/org/briarproject/api/sync/PacketReader.java
@@ -18,21 +18,15 @@ public interface PacketReader {
 	boolean hasRequest() throws IOException;
 	Request readRequest() throws IOException;
 
-	boolean hasRetentionAck() throws IOException;
-	org.briarproject.api.sync.RetentionAck readRetentionAck() throws IOException;
-
-	boolean hasRetentionUpdate() throws IOException;
-	org.briarproject.api.sync.RetentionUpdate readRetentionUpdate() throws IOException;
-
 	boolean hasSubscriptionAck() throws IOException;
-	org.briarproject.api.sync.SubscriptionAck readSubscriptionAck() throws IOException;
+	SubscriptionAck readSubscriptionAck() throws IOException;
 
 	boolean hasSubscriptionUpdate() throws IOException;
-	org.briarproject.api.sync.SubscriptionUpdate readSubscriptionUpdate() throws IOException;
+	SubscriptionUpdate readSubscriptionUpdate() throws IOException;
 
 	boolean hasTransportAck() throws IOException;
 	TransportAck readTransportAck() throws IOException;
 
 	boolean hasTransportUpdate() throws IOException;
-	org.briarproject.api.sync.TransportUpdate readTransportUpdate() throws IOException;
+	TransportUpdate readTransportUpdate() throws IOException;
 }
diff --git a/briar-api/src/org/briarproject/api/sync/PacketTypes.java b/briar-api/src/org/briarproject/api/sync/PacketTypes.java
index 595135d12596b918f7f3cf5d24a32f5cfb6d640c..873b89b3a81a93d90501b550e2834d0fbe025224 100644
--- a/briar-api/src/org/briarproject/api/sync/PacketTypes.java
+++ b/briar-api/src/org/briarproject/api/sync/PacketTypes.java
@@ -7,8 +7,6 @@ public interface PacketTypes {
 	byte MESSAGE = 1;
 	byte OFFER = 2;
 	byte REQUEST = 3;
-	byte RETENTION_ACK = 4;
-	byte RETENTION_UPDATE = 5;
 	byte SUBSCRIPTION_ACK = 6;
 	byte SUBSCRIPTION_UPDATE = 7;
 	byte TRANSPORT_ACK = 8;
diff --git a/briar-api/src/org/briarproject/api/sync/PacketWriter.java b/briar-api/src/org/briarproject/api/sync/PacketWriter.java
index 3e43eeb48de2fde32b0cb4f9b4991e0208a69015..896b661f45ec484c2edeada512e62c5ccb70bb94 100644
--- a/briar-api/src/org/briarproject/api/sync/PacketWriter.java
+++ b/briar-api/src/org/briarproject/api/sync/PacketWriter.java
@@ -14,21 +14,17 @@ public interface PacketWriter {
 
 	void writeMessage(byte[] raw) throws IOException;
 
-	void writeOffer(org.briarproject.api.sync.Offer o) throws IOException;
+	void writeOffer(Offer o) throws IOException;
 
 	void writeRequest(Request r) throws IOException;
 
-	void writeRetentionAck(org.briarproject.api.sync.RetentionAck a) throws IOException;
+	void writeSubscriptionAck(SubscriptionAck a) throws IOException;
 
-	void writeRetentionUpdate(org.briarproject.api.sync.RetentionUpdate u) throws IOException;
+	void writeSubscriptionUpdate(SubscriptionUpdate u) throws IOException;
 
-	void writeSubscriptionAck(org.briarproject.api.sync.SubscriptionAck a) throws IOException;
+	void writeTransportAck(TransportAck a) throws IOException;
 
-	void writeSubscriptionUpdate(org.briarproject.api.sync.SubscriptionUpdate u) throws IOException;
-
-	void writeTransportAck(org.briarproject.api.sync.TransportAck a) throws IOException;
-
-	void writeTransportUpdate(org.briarproject.api.sync.TransportUpdate u) throws IOException;
+	void writeTransportUpdate(TransportUpdate u) throws IOException;
 
 	void flush() throws IOException;
 }
diff --git a/briar-api/src/org/briarproject/api/sync/RetentionAck.java b/briar-api/src/org/briarproject/api/sync/RetentionAck.java
deleted file mode 100644
index 5c36a943455f01ba35d395fae6f472a4dcad4093..0000000000000000000000000000000000000000
--- a/briar-api/src/org/briarproject/api/sync/RetentionAck.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.briarproject.api.sync;
-
-/** A packet acknowledging a (@link RetentionUpdate} */
-public class RetentionAck {
-
-	private final long version;
-
-	public RetentionAck(long version) {
-		this.version = version;
-	}
-
-	/** Returns the version number of the acknowledged update. */
-	public long getVersion() {
-		return version;
-	}
-}
diff --git a/briar-api/src/org/briarproject/api/sync/RetentionUpdate.java b/briar-api/src/org/briarproject/api/sync/RetentionUpdate.java
deleted file mode 100644
index 5955a3955a1b0c022e5272788dc0067d61bbee39..0000000000000000000000000000000000000000
--- a/briar-api/src/org/briarproject/api/sync/RetentionUpdate.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.briarproject.api.sync;
-
-/**
- * A packet updating the recipient's view of the retention time of the sender's
- * database.
- */
-public class RetentionUpdate {
-
-	private final long retention, version;
-
-	public RetentionUpdate(long retention, long version) {
-		this.retention = retention;
-		this.version = version;
-	}
-
-	public long getRetentionTime() {
-		return retention;
-	}
-
-	public long getVersion() {
-		return version;
-	}
-}
diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java
index 9e0e8045c3ecbd441d31bed8f2ace426248c55d9..b8a57ccb3f06c371205019c3860e0af6d659bc1e 100644
--- a/briar-core/src/org/briarproject/db/Database.java
+++ b/briar-core/src/org/briarproject/db/Database.java
@@ -15,8 +15,6 @@ import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageHeader;
 import org.briarproject.api.sync.MessageId;
-import org.briarproject.api.sync.RetentionAck;
-import org.briarproject.api.sync.RetentionUpdate;
 import org.briarproject.api.sync.SubscriptionAck;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.TransportAck;
@@ -379,14 +377,6 @@ interface Database<T> {
 	Collection<MessageId> getMessagesToRequest(T txn, ContactId c,
 			int maxMessages) throws DbException;
 
-	/**
-	 * Returns the IDs of the oldest messages in the database, with a total
-	 * size less than or equal to the given size.
-	 * <p>
-	 * Locking: read.
-	 */
-	Collection<MessageId> getOldMessages(T txn, int size) throws DbException;
-
 	/**
 	 * Returns the parent of the given message, or null if either the message
 	 * has no parent, or the parent is absent from the database, or the parent
@@ -428,22 +418,6 @@ interface Database<T> {
 	Collection<MessageId> getRequestedMessagesToSend(T txn, ContactId c,
 			int maxLength) throws DbException;
 
-	/**
-	 * Returns a retention ack for the given contact, or null if no ack is due.
-	 * <p>
-	 * Locking: write.
-	 */
-	RetentionAck getRetentionAck(T txn, ContactId c) throws DbException;
-
-	/**
-	 * Returns a retention update for the given contact and updates its expiry
-	 * time using the given latency, or returns null if no update is due.
-	 * <p>
-	 * Locking: write.
-	 */
-	RetentionUpdate getRetentionUpdate(T txn, ContactId c, int maxLatency)
-			throws DbException;
-
 	/**
 	 * Returns all settings.
 	 * <p>
@@ -532,14 +506,6 @@ interface Database<T> {
 	void incrementStreamCounter(T txn, ContactId c, TransportId t,
 			long rotationPeriod) throws DbException;
 
-	/**
-	 * Increments the retention time versions for all contacts to indicate that
-	 * the database's retention time has changed and updates should be sent.
-	 * <p>
-	 * Locking: write.
-	 */
-	void incrementRetentionVersions(T txn) throws DbException;
-
 	/**
 	 * Marks the given messages as not needing to be acknowledged to the
 	 * given contact.
@@ -728,25 +694,6 @@ interface Database<T> {
 	boolean setRemoteProperties(T txn, ContactId c, TransportId t,
 			TransportProperties p, long version) throws DbException;
 
-	/**
-	 * Sets the retention time of the given contact's database and returns
-	 * true, unless an update with an equal or higher version number has
-	 * already been received from the contact.
-	 * <p>
-	 * Locking: write.
-	 */
-	boolean setRetentionTime(T txn, ContactId c, long retention, long version)
-			throws DbException;
-
-	/**
-	 * Records a retention ack from the given contact for the given version,
-	 * unless the contact has already acked an equal or higher version.
-	 * <p>
-	 * Locking: write.
-	 */
-	void setRetentionUpdateAcked(T txn, ContactId c, long version)
-			throws DbException;
-
 	/**
 	 * Records a subscription ack from the given contact for the given version,
 	 * unless the contact has already acked an equal or higher version.
diff --git a/briar-core/src/org/briarproject/db/DatabaseCleaner.java b/briar-core/src/org/briarproject/db/DatabaseCleaner.java
deleted file mode 100644
index fc2e1024b737891a2fe232f82c788c442b34d0e7..0000000000000000000000000000000000000000
--- a/briar-core/src/org/briarproject/db/DatabaseCleaner.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.briarproject.db;
-
-import org.briarproject.api.db.DbException;
-
-interface DatabaseCleaner {
-
-	/**
-	 * Starts a new thread to monitor the amount of free storage space
-	 * available to the database and expire old messages as necessary. The
-	 * cleaner will pause for the given number of milliseconds between sweeps.
-	 */
-	void startCleaning(Callback callback, long msBetweenSweeps);
-
-	/** Tells the cleaner thread to exit. */
-	void stopCleaning();
-
-	interface Callback {
-
-		/**
-		 * Checks how much free storage space is available to the database, and
-		 * if necessary expires old messages until the free space is at least
-		 * DatabaseConstants.MIN_FREE_SPACE. If the free space is less than
-		 * DatabaseConstants.CRITICAL_FREE_SPACE and there are no more messages
-		 * to expire, an Error will be thrown.
-		 */
-		void checkFreeSpaceAndClean() throws DbException;
-
-		/**
-		 * Returns true if the amount of free storage space available to the
-		 * database should be checked.
-		 */
-		boolean shouldCheckFreeSpace();
-	}
-}
diff --git a/briar-core/src/org/briarproject/db/DatabaseCleanerImpl.java b/briar-core/src/org/briarproject/db/DatabaseCleanerImpl.java
deleted file mode 100644
index d286ba201abe5f7476c405cd0b74aef558ee9902..0000000000000000000000000000000000000000
--- a/briar-core/src/org/briarproject/db/DatabaseCleanerImpl.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.briarproject.db;
-
-import static java.util.logging.Level.WARNING;
-
-import java.util.TimerTask;
-import java.util.logging.Logger;
-
-import javax.inject.Inject;
-
-import org.briarproject.api.db.DbClosedException;
-import org.briarproject.api.db.DbException;
-import org.briarproject.api.system.Timer;
-
-class DatabaseCleanerImpl extends TimerTask implements DatabaseCleaner {
-
-	private static final Logger LOG =
-			Logger.getLogger(DatabaseCleanerImpl.class.getName());
-
-	private final Timer timer;
-
-	private volatile Callback callback = null;
-
-	@Inject
-	DatabaseCleanerImpl(Timer timer) {
-		this.timer = timer;
-	}
-
-	public void startCleaning(Callback callback, long msBetweenSweeps) {
-		this.callback = callback;
-		timer.scheduleAtFixedRate(this, 0, msBetweenSweeps);
-	}
-
-	public void stopCleaning() {
-		timer.cancel();
-	}
-
-	public void run() {
-		if (callback == null) throw new IllegalStateException();
-		try {
-			if (callback.shouldCheckFreeSpace()) {
-				LOG.info("Checking free space");
-				callback.checkFreeSpaceAndClean();
-			}
-		} catch (DbClosedException e) {
-			LOG.info("Database closed, exiting");
-		} catch (DbException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			throw new Error(e); // Kill the application
-		} catch (RuntimeException e) {
-			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			throw new Error(e); // Kill the application
-		}
-	}
-}
diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
index 280dda05b9bc9e12cb648a08ad63fe0fe12ce1ea..5af5e56eca3c9823fcba344647467ee6c4abb0c6 100644
--- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
+++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
@@ -26,13 +26,11 @@ import org.briarproject.api.event.LocalAuthorRemovedEvent;
 import org.briarproject.api.event.LocalSubscriptionsUpdatedEvent;
 import org.briarproject.api.event.LocalTransportsUpdatedEvent;
 import org.briarproject.api.event.MessageAddedEvent;
-import org.briarproject.api.event.MessageExpiredEvent;
 import org.briarproject.api.event.MessageRequestedEvent;
 import org.briarproject.api.event.MessageToAckEvent;
 import org.briarproject.api.event.MessageToRequestEvent;
 import org.briarproject.api.event.MessagesAckedEvent;
 import org.briarproject.api.event.MessagesSentEvent;
-import org.briarproject.api.event.RemoteRetentionTimeUpdatedEvent;
 import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
 import org.briarproject.api.event.RemoteTransportsUpdatedEvent;
 import org.briarproject.api.event.SettingsUpdatedEvent;
@@ -49,8 +47,6 @@ import org.briarproject.api.sync.MessageHeader;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.RetentionAck;
-import org.briarproject.api.sync.RetentionUpdate;
 import org.briarproject.api.sync.SubscriptionAck;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.TransportAck;
@@ -71,13 +67,8 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
-import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.db.DatabaseConstants.BYTES_PER_SWEEP;
-import static org.briarproject.db.DatabaseConstants.CRITICAL_FREE_SPACE;
 import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
-import static org.briarproject.db.DatabaseConstants.MAX_TRANSACTIONS_BETWEEN_SPACE_CHECKS;
-import static org.briarproject.db.DatabaseConstants.MIN_FREE_SPACE;
 
 /**
  * An implementation of DatabaseComponent using reentrant read-write locks.
@@ -85,15 +76,12 @@ import static org.briarproject.db.DatabaseConstants.MIN_FREE_SPACE;
  * writers to starve. LockFairnessTest can be used to test whether this
  * implementation is safe on a given JVM.
  */
-class DatabaseComponentImpl<T> implements DatabaseComponent,
-		DatabaseCleaner.Callback {
+class DatabaseComponentImpl<T> implements DatabaseComponent {
 
 	private static final Logger LOG =
 			Logger.getLogger(DatabaseComponentImpl.class.getName());
-	private static final int MS_BETWEEN_SWEEPS = 10 * 1000; // 10 seconds
 
 	private final Database<T> db;
-	private final DatabaseCleaner cleaner;
 	private final EventBus eventBus;
 	private final ShutdownManager shutdown;
 
@@ -104,10 +92,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent,
 	private int shutdownHandle = -1; // Locking: lock.writeLock
 
 	@Inject
-	DatabaseComponentImpl(Database<T> db, DatabaseCleaner cleaner,
-			EventBus eventBus, ShutdownManager shutdown) {
+	DatabaseComponentImpl(Database<T> db, EventBus eventBus,
+			ShutdownManager shutdown) {
 		this.db = db;
-		this.cleaner = cleaner;
 		this.eventBus = eventBus;
 		this.shutdown = shutdown;
 	}
@@ -135,7 +122,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent,
 			if (open) throw new IllegalStateException();
 			open = true;
 			boolean reopened = db.open();
-			cleaner.startCleaning(this, MS_BETWEEN_SWEEPS);
 			shutdownHandle = shutdown.addShutdownHook(shutdownHook);
 			return reopened;
 		} finally {
@@ -150,7 +136,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent,
 			open = false;
 			if (shutdownHandle != -1)
 				shutdown.removeShutdownHook(shutdownHandle);
-			cleaner.stopCleaning();
 			db.close();
 		} finally {
 			lock.writeLock().unlock();
@@ -439,45 +424,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent,
 		return Collections.unmodifiableList(messages);
 	}
 
-	public RetentionAck generateRetentionAck(ContactId c) throws DbException {
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				RetentionAck a = db.getRetentionAck(txn, c);
-				db.commitTransaction(txn);
-				return a;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
-		}
-	}
-
-	public RetentionUpdate generateRetentionUpdate(ContactId c, int maxLatency)
-			throws DbException {
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				RetentionUpdate u = db.getRetentionUpdate(txn, c, maxLatency);
-				db.commitTransaction(txn);
-				return u;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
-		}
-	}
-
 	public SubscriptionAck generateSubscriptionAck(ContactId c)
 			throws DbException {
 		lock.writeLock().lock();
@@ -1168,47 +1114,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent,
 		if (requested) eventBus.broadcast(new MessageRequestedEvent(c));
 	}
 
-	public void receiveRetentionAck(ContactId c, RetentionAck a)
-			throws DbException {
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				db.setRetentionUpdateAcked(txn, c, a.getVersion());
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
-		}
-	}
-
-	public void receiveRetentionUpdate(ContactId c, RetentionUpdate u)
-			throws DbException {
-		boolean updated;
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				long retention = u.getRetentionTime(), version = u.getVersion();
-				updated = db.setRetentionTime(txn, c, retention, version);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
-		}
-		if (updated) eventBus.broadcast(new RemoteRetentionTimeUpdatedEvent(c));
-	}
-
 	public void receiveSubscriptionAck(ContactId c, SubscriptionAck a)
 			throws DbException {
 		lock.writeLock().lock();
@@ -1559,57 +1464,4 @@ class DatabaseComponentImpl<T> implements DatabaseComponent,
 			lock.writeLock().unlock();
 		}
 	}
-
-	public void checkFreeSpaceAndClean() throws DbException {
-		long freeSpace = db.getFreeSpace();
-		if (LOG.isLoggable(INFO)) LOG.info(freeSpace + " bytes free space");
-		while (freeSpace < MIN_FREE_SPACE) {
-			boolean expired = expireMessages(BYTES_PER_SWEEP);
-			if (freeSpace < CRITICAL_FREE_SPACE && !expired) {
-				// FIXME: Work out what to do here
-				throw new Error("Disk space is critically low");
-			}
-			Thread.yield();
-			freeSpace = db.getFreeSpace();
-		}
-	}
-
-	/**
-	 * Removes the oldest messages from the database, with a total size less
-	 * than or equal to the given size, and returns true if any messages were
-	 * removed.
-	 */
-	private boolean expireMessages(int size) throws DbException {
-		Collection<MessageId> expired;
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				expired = db.getOldMessages(txn, size);
-				if (!expired.isEmpty()) {
-					for (MessageId m : expired) db.removeMessage(txn, m);
-					db.incrementRetentionVersions(txn);
-					if (LOG.isLoggable(INFO))
-						LOG.info("Expired " + expired.size() + " messages");
-				}
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
-		}
-		if (expired.isEmpty()) return false;
-		eventBus.broadcast(new MessageExpiredEvent());
-		return true;
-	}
-
-	public boolean shouldCheckFreeSpace() {
-		if (db.getTransactionCount() > MAX_TRANSACTIONS_BETWEEN_SPACE_CHECKS) {
-			db.resetTransactionCount();
-			return true;
-		}
-		return false;
-	}
 }
diff --git a/briar-core/src/org/briarproject/db/DatabaseModule.java b/briar-core/src/org/briarproject/db/DatabaseModule.java
index bbfa1c42178b5bf575c178a5882c516f7302f6a9..d5770151db161c1294f5b36a0e6684f5f2f9c38c 100644
--- a/briar-core/src/org/briarproject/db/DatabaseModule.java
+++ b/briar-core/src/org/briarproject/db/DatabaseModule.java
@@ -40,20 +40,18 @@ public class DatabaseModule extends AbstractModule {
 
 	@Override
 	protected void configure() {
-		bind(DatabaseCleaner.class).to(DatabaseCleanerImpl.class);
+		// Nothing to bind
 	}
 
-	@Provides
+	@Provides @Singleton
 	Database<Connection> getDatabase(DatabaseConfig config) {
 		return new H2Database(config, new SystemClock());
 	}
 
 	@Provides @Singleton
 	DatabaseComponent getDatabaseComponent(Database<Connection> db,
-			DatabaseCleaner cleaner, EventBus eventBus,
-			ShutdownManager shutdown) {
-		return new DatabaseComponentImpl<Connection>(db, cleaner, eventBus,
-				shutdown);
+			EventBus eventBus, ShutdownManager shutdown) {
+		return new DatabaseComponentImpl<Connection>(db, eventBus, shutdown);
 	}
 
 	@Provides @Singleton @DatabaseExecutor
diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java
index f51f4e192ecb4140b61db23dcf22a22758a73c2e..735e05f58190d35d7e98dae1e8b5032e055f9d28 100644
--- a/briar-core/src/org/briarproject/db/JdbcDatabase.java
+++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java
@@ -18,8 +18,6 @@ import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageHeader;
 import org.briarproject.api.sync.MessageHeader.State;
 import org.briarproject.api.sync.MessageId;
-import org.briarproject.api.sync.RetentionAck;
-import org.briarproject.api.sync.RetentionUpdate;
 import org.briarproject.api.sync.SubscriptionAck;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.TransportAck;
@@ -58,7 +56,6 @@ import static org.briarproject.api.Author.Status.ANONYMOUS;
 import static org.briarproject.api.Author.Status.UNKNOWN;
 import static org.briarproject.api.Author.Status.VERIFIED;
 import static org.briarproject.api.sync.MessagingConstants.MAX_SUBSCRIPTIONS;
-import static org.briarproject.api.sync.MessagingConstants.RETENTION_GRANULARITY;
 import static org.briarproject.db.ExponentialBackoff.calculateExpiry;
 
 /**
@@ -200,21 +197,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 	private static final String INDEX_STATUSES_BY_CONTACT =
 			"CREATE INDEX statusesByContact ON statuses (contactId)";
 
-	private static final String CREATE_RETENTION_VERSIONS =
-			"CREATE TABLE retentionVersions"
-					+ " (contactId INT NOT NULL,"
-					+ " retention BIGINT NOT NULL,"
-					+ " localVersion BIGINT NOT NULL,"
-					+ " localAcked BIGINT NOT NULL,"
-					+ " remoteVersion BIGINT NOT NULL,"
-					+ " remoteAcked BOOLEAN NOT NULL,"
-					+ " expiry BIGINT NOT NULL,"
-					+ " txCount INT NOT NULL,"
-					+ " PRIMARY KEY (contactId),"
-					+ " FOREIGN KEY (contactId)"
-					+ " REFERENCES contacts (contactId)"
-					+ " ON DELETE CASCADE)";
-
 	private static final String CREATE_TRANSPORTS =
 			"CREATE TABLE transports"
 					+ " (transportId VARCHAR NOT NULL,"
@@ -416,7 +398,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 			s.executeUpdate(insertTypeNames(CREATE_STATUSES));
 			s.executeUpdate(INDEX_STATUSES_BY_MESSAGE);
 			s.executeUpdate(INDEX_STATUSES_BY_CONTACT);
-			s.executeUpdate(insertTypeNames(CREATE_RETENTION_VERSIONS));
 			s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS));
 			s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_CONFIGS));
 			s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_PROPS));
@@ -632,16 +613,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 				}
 				ps.close();
 			}
-			// Create a retention version row
-			sql = "INSERT INTO retentionVersions (contactId, retention,"
-					+ " localVersion, localAcked, remoteVersion, remoteAcked,"
-					+ " expiry, txCount)"
-					+ " VALUES (?, 0, 1, 0, 0, TRUE, 0, 0)";
-			ps = txn.prepareStatement(sql);
-			ps.setInt(1, c.getInt());
-			affected = ps.executeUpdate();
-			if (affected != 1) throw new DbStateException();
-			ps.close();
 			// Create a group version row
 			sql = "INSERT INTO groupVersions (contactId, localVersion,"
 					+ " localAcked, remoteVersion, remoteAcked, expiry,"
@@ -1712,13 +1683,10 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " JOIN groupVisibilities AS gv"
 					+ " ON m.groupId = gv.groupId"
 					+ " AND cg.contactId = gv.contactId"
-					+ " JOIN retentionVersions AS rv"
-					+ " ON cg.contactId = rv.contactId"
 					+ " JOIN statuses AS s"
 					+ " ON m.messageId = s.messageId"
 					+ " AND cg.contactId = s.contactId"
 					+ " WHERE cg.contactId = ?"
-					+ " AND timestamp >= retention"
 					+ " AND seen = FALSE AND requested = FALSE"
 					+ " AND s.expiry < ?"
 					+ " ORDER BY timestamp DESC LIMIT ?";
@@ -1775,13 +1743,10 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " JOIN groupVisibilities AS gv"
 					+ " ON m.groupId = gv.groupId"
 					+ " AND cg.contactId = gv.contactId"
-					+ " JOIN retentionVersions AS rv"
-					+ " ON cg.contactId = rv.contactId"
 					+ " JOIN statuses AS s"
 					+ " ON m.messageId = s.messageId"
 					+ " AND cg.contactId = s.contactId"
 					+ " WHERE cg.contactId = ?"
-					+ " AND timestamp >= retention"
 					+ " AND seen = FALSE"
 					+ " AND s.expiry < ?"
 					+ " ORDER BY timestamp DESC";
@@ -1807,33 +1772,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
-	public Collection<MessageId> getOldMessages(Connection txn, int capacity)
-			throws DbException {
-		PreparedStatement ps = null;
-		ResultSet rs = null;
-		try {
-			String sql = "SELECT length, messageId FROM messages"
-					+ " ORDER BY timestamp";
-			ps = txn.prepareStatement(sql);
-			rs = ps.executeQuery();
-			List<MessageId> ids = new ArrayList<MessageId>();
-			int total = 0;
-			while (rs.next()) {
-				int length = rs.getInt(1);
-				if (total + length > capacity) break;
-				ids.add(new MessageId(rs.getBytes(2)));
-				total += length;
-			}
-			rs.close();
-			ps.close();
-			return Collections.unmodifiableList(ids);
-		} catch (SQLException e) {
-			tryToClose(rs);
-			tryToClose(ps);
-			throw new DbException(e);
-		}
-	}
-
 	public MessageId getParent(Connection txn, MessageId m) throws DbException {
 		PreparedStatement ps = null;
 		ResultSet rs = null;
@@ -1954,13 +1892,10 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " JOIN groupVisibilities AS gv"
 					+ " ON m.groupId = gv.groupId"
 					+ " AND cg.contactId = gv.contactId"
-					+ " JOIN retentionVersions AS rv"
-					+ " ON cg.contactId = rv.contactId"
 					+ " JOIN statuses AS s"
 					+ " ON m.messageId = s.messageId"
 					+ " AND cg.contactId = s.contactId"
 					+ " WHERE cg.contactId = ?"
-					+ " AND timestamp >= retention"
 					+ " AND seen = FALSE AND requested = TRUE"
 					+ " AND s.expiry < ?"
 					+ " ORDER BY timestamp DESC";
@@ -1986,94 +1921,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
-	public RetentionAck getRetentionAck(Connection txn, ContactId c)
-			throws DbException {
-		PreparedStatement ps = null;
-		ResultSet rs = null;
-		try {
-			String sql = "SELECT remoteVersion FROM retentionVersions"
-					+ " WHERE contactId = ? AND remoteAcked = FALSE";
-			ps = txn.prepareStatement(sql);
-			ps.setInt(1, c.getInt());
-			rs = ps.executeQuery();
-			if (!rs.next()) {
-				rs.close();
-				ps.close();
-				return null;
-			}
-			long version = rs.getLong(1);
-			if (rs.next()) throw new DbStateException();
-			rs.close();
-			ps.close();
-			sql = "UPDATE retentionVersions SET remoteAcked = TRUE"
-					+ " WHERE contactId = ?";
-			ps = txn.prepareStatement(sql);
-			ps.setInt(1, c.getInt());
-			int affected = ps.executeUpdate();
-			if (affected != 1) throw new DbStateException();
-			ps.close();
-			return new RetentionAck(version);
-		} catch (SQLException e) {
-			tryToClose(ps);
-			tryToClose(rs);
-			throw new DbException(e);
-		}
-	}
-
-	public RetentionUpdate getRetentionUpdate(Connection txn, ContactId c,
-			int maxLatency) throws DbException {
-		long now = clock.currentTimeMillis();
-		PreparedStatement ps = null;
-		ResultSet rs = null;
-		try {
-			String sql = "SELECT localVersion, txCount"
-					+ " FROM retentionVersions"
-					+ " WHERE contactId = ?"
-					+ " AND localVersion > localAcked"
-					+ " AND expiry < ?";
-			ps = txn.prepareStatement(sql);
-			ps.setInt(1, c.getInt());
-			ps.setLong(2, now);
-			rs = ps.executeQuery();
-			if (!rs.next()) {
-				rs.close();
-				ps.close();
-				return null;
-			}
-			long version = rs.getLong(1);
-			int txCount = rs.getInt(2);
-			if (rs.next()) throw new DbStateException();
-			rs.close();
-			ps.close();
-			sql = "SELECT timestamp FROM messages AS m"
-					+ " ORDER BY timestamp LIMIT 1";
-			ps = txn.prepareStatement(sql);
-			rs = ps.executeQuery();
-			long retention = 0;
-			if (rs.next()) {
-				retention = rs.getLong(1);
-				retention -= retention % RETENTION_GRANULARITY;
-			}
-			if (rs.next()) throw new DbStateException();
-			rs.close();
-			ps.close();
-			sql = "UPDATE retentionVersions"
-					+ " SET expiry = ?, txCount = txCount + 1"
-					+ " WHERE contactId = ?";
-			ps = txn.prepareStatement(sql);
-			ps.setLong(1, calculateExpiry(now, maxLatency, txCount));
-			ps.setInt(2, c.getInt());
-			int affected = ps.executeUpdate();
-			if (affected != 1) throw new DbStateException();
-			ps.close();
-			return new RetentionUpdate(retention, version);
-		} catch (SQLException e) {
-			tryToClose(ps);
-			tryToClose(rs);
-			throw new DbException(e);
-		}
-	}
-
 	public Settings getSettings(Connection txn) throws DbException {
 		PreparedStatement ps = null;
 		ResultSet rs = null;
@@ -2474,19 +2321,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
-	public void incrementRetentionVersions(Connection txn) throws DbException {
-		PreparedStatement ps = null;
-		try {
-			String sql = "UPDATE retentionVersions"
-					+ " SET localVersion = localVersion + 1, expiry = 0";
-			ps = txn.prepareStatement(sql);
-			ps.executeUpdate();
-		} catch (SQLException e) {
-			tryToClose(ps);
-			throw new DbException(e);
-		}
-	}
-
 	public void lowerAckFlag(Connection txn, ContactId c,
 			Collection<MessageId> acked) throws DbException {
 		PreparedStatement ps = null;
@@ -3195,49 +3029,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
-	public boolean setRetentionTime(Connection txn, ContactId c, long retention,
-			long version) throws DbException {
-		PreparedStatement ps = null;
-		try {
-			String sql = "UPDATE retentionVersions SET retention = ?,"
-					+ " remoteVersion = ?, remoteAcked = FALSE"
-					+ " WHERE contactId = ? AND remoteVersion < ?";
-			ps = txn.prepareStatement(sql);
-			ps.setLong(1, retention);
-			ps.setLong(2, version);
-			ps.setInt(3, c.getInt());
-			ps.setLong(4, version);
-			int affected = ps.executeUpdate();
-			if (affected < 0 || affected > 1) throw new DbStateException();
-			ps.close();
-			return affected == 1;
-		} catch (SQLException e) {
-			tryToClose(ps);
-			throw new DbException(e);
-		}
-	}
-
-	public void setRetentionUpdateAcked(Connection txn, ContactId c,
-			long version) throws DbException {
-		PreparedStatement ps = null;
-		try {
-			String sql = "UPDATE retentionVersions SET localAcked = ?"
-					+ " WHERE contactId = ?"
-					+ " AND localAcked < ? AND localVersion >= ?";
-			ps = txn.prepareStatement(sql);
-			ps.setLong(1, version);
-			ps.setInt(2, c.getInt());
-			ps.setLong(3, version);
-			ps.setLong(4, version);
-			int affected = ps.executeUpdate();
-			if (affected < 0 || affected > 1) throw new DbStateException();
-			ps.close();
-		} catch (SQLException e) {
-			tryToClose(ps);
-			throw new DbException(e);
-		}
-	}
-
 	public void setSubscriptionUpdateAcked(Connection txn, ContactId c,
 			long version) throws DbException {
 		PreparedStatement ps = null;
diff --git a/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java b/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java
index ad7cea37582f50d02ebbbe6aadad355a024ee5b6..2153a0ea83133e4ada3d59b8a5727cd68c21fcaa 100644
--- a/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java
+++ b/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java
@@ -11,11 +11,9 @@ import org.briarproject.api.event.EventListener;
 import org.briarproject.api.event.LocalSubscriptionsUpdatedEvent;
 import org.briarproject.api.event.LocalTransportsUpdatedEvent;
 import org.briarproject.api.event.MessageAddedEvent;
-import org.briarproject.api.event.MessageExpiredEvent;
 import org.briarproject.api.event.MessageRequestedEvent;
 import org.briarproject.api.event.MessageToAckEvent;
 import org.briarproject.api.event.MessageToRequestEvent;
-import org.briarproject.api.event.RemoteRetentionTimeUpdatedEvent;
 import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
 import org.briarproject.api.event.RemoteTransportsUpdatedEvent;
 import org.briarproject.api.event.ShutdownEvent;
@@ -25,8 +23,6 @@ import org.briarproject.api.sync.MessagingSession;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.PacketWriter;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.RetentionAck;
-import org.briarproject.api.sync.RetentionUpdate;
 import org.briarproject.api.sync.SubscriptionAck;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.TransportAck;
@@ -46,7 +42,7 @@ import static java.util.logging.Level.WARNING;
 import static org.briarproject.api.sync.MessagingConstants.MAX_PAYLOAD_LENGTH;
 
 /**
- * An outgoing {@link MessagingSession
+ * An outgoing {@link org.briarproject.api.sync.MessagingSession
  * MessagingSession} suitable for duplex transports. The session offers
  * messages before sending them, keeps its output stream open when there are no
  * packets to send, and reacts to events that make packets available to send.
@@ -73,10 +69,6 @@ class DuplexOutgoingSession implements MessagingSession, EventListener {
 	private final PacketWriter packetWriter;
 	private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
 
-	// The following must only be accessed on the writer thread
-	private long nextKeepalive = 0, nextRetxQuery = 0;
-	private boolean dataToFlush = true;
-
 	private volatile boolean interrupted = false;
 
 	DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
@@ -103,15 +95,14 @@ class DuplexOutgoingSession implements MessagingSession, EventListener {
 			dbExecutor.execute(new GenerateTransportUpdates());
 			dbExecutor.execute(new GenerateSubscriptionAck());
 			dbExecutor.execute(new GenerateSubscriptionUpdate());
-			dbExecutor.execute(new GenerateRetentionAck());
-			dbExecutor.execute(new GenerateRetentionUpdate());
 			dbExecutor.execute(new GenerateAck());
 			dbExecutor.execute(new GenerateBatch());
 			dbExecutor.execute(new GenerateOffer());
 			dbExecutor.execute(new GenerateRequest());
 			long now = clock.currentTimeMillis();
-			nextKeepalive = now + maxIdleTime;
-			nextRetxQuery = now + RETX_QUERY_INTERVAL;
+			long nextKeepalive = now + maxIdleTime;
+			long nextRetxQuery = now + RETX_QUERY_INTERVAL;
+			boolean dataToFlush = true;
 			// Write packets until interrupted
 			try {
 				while (!interrupted) {
@@ -134,7 +125,6 @@ class DuplexOutgoingSession implements MessagingSession, EventListener {
 							// Check for retransmittable packets
 							dbExecutor.execute(new GenerateTransportUpdates());
 							dbExecutor.execute(new GenerateSubscriptionUpdate());
-							dbExecutor.execute(new GenerateRetentionUpdate());
 							dbExecutor.execute(new GenerateBatch());
 							dbExecutor.execute(new GenerateOffer());
 							nextRetxQuery = now + RETX_QUERY_INTERVAL;
@@ -173,8 +163,6 @@ class DuplexOutgoingSession implements MessagingSession, EventListener {
 			if (c.getContactId().equals(contactId)) interrupt();
 		} else if (e instanceof MessageAddedEvent) {
 			dbExecutor.execute(new GenerateOffer());
-		} else if (e instanceof MessageExpiredEvent) {
-			dbExecutor.execute(new GenerateRetentionUpdate());
 		} else if (e instanceof LocalSubscriptionsUpdatedEvent) {
 			LocalSubscriptionsUpdatedEvent l =
 					(LocalSubscriptionsUpdatedEvent) e;
@@ -193,11 +181,6 @@ class DuplexOutgoingSession implements MessagingSession, EventListener {
 		} else if (e instanceof MessageToRequestEvent) {
 			if (((MessageToRequestEvent) e).getContactId().equals(contactId))
 				dbExecutor.execute(new GenerateRequest());
-		} else if (e instanceof RemoteRetentionTimeUpdatedEvent) {
-			RemoteRetentionTimeUpdatedEvent r =
-					(RemoteRetentionTimeUpdatedEvent) e;
-			if (r.getContactId().equals(contactId))
-				dbExecutor.execute(new GenerateRetentionAck());
 		} else if (e instanceof RemoteSubscriptionsUpdatedEvent) {
 			RemoteSubscriptionsUpdatedEvent r =
 					(RemoteSubscriptionsUpdatedEvent) e;
@@ -360,77 +343,6 @@ class DuplexOutgoingSession implements MessagingSession, EventListener {
 		}
 	}
 
-	// This task runs on the database thread
-	private class GenerateRetentionAck implements Runnable {
-
-		public void run() {
-			if (interrupted) return;
-			try {
-				RetentionAck a = db.generateRetentionAck(contactId);
-				if (LOG.isLoggable(INFO))
-					LOG.info("Generated retention ack: " + (a != null));
-				if (a != null) writerTasks.add(new WriteRetentionAck(a));
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
-	// This tasks runs on the writer thread
-	private class WriteRetentionAck implements ThrowingRunnable<IOException> {
-
-		private final RetentionAck ack;
-
-		private WriteRetentionAck(RetentionAck ack) {
-			this.ack = ack;
-		}
-
-
-		public void run() throws IOException {
-			if (interrupted) return;
-			packetWriter.writeRetentionAck(ack);
-			LOG.info("Sent retention ack");
-			dbExecutor.execute(new GenerateRetentionAck());
-		}
-	}
-
-	// This task runs on the database thread
-	private class GenerateRetentionUpdate implements Runnable {
-
-		public void run() {
-			if (interrupted) return;
-			try {
-				RetentionUpdate u =
-						db.generateRetentionUpdate(contactId, maxLatency);
-				if (LOG.isLoggable(INFO))
-					LOG.info("Generated retention update: " + (u != null));
-				if (u != null) writerTasks.add(new WriteRetentionUpdate(u));
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
-	// This task runs on the writer thread
-	private class WriteRetentionUpdate
-	implements ThrowingRunnable<IOException> {
-
-		private final RetentionUpdate update;
-
-		private WriteRetentionUpdate(RetentionUpdate update) {
-			this.update = update;
-		}
-
-		public void run() throws IOException {
-			if (interrupted) return;
-			packetWriter.writeRetentionUpdate(update);
-			LOG.info("Sent retention update");
-			dbExecutor.execute(new GenerateRetentionUpdate());
-		}
-	}
-
 	// This task runs on the database thread
 	private class GenerateSubscriptionAck implements Runnable {
 
diff --git a/briar-core/src/org/briarproject/sync/IncomingSession.java b/briar-core/src/org/briarproject/sync/IncomingSession.java
index 092cf507944b91f16b8e29a4f85a75e7420b1169..44d36bc7019106304031626d896ff7a0350dfd23 100644
--- a/briar-core/src/org/briarproject/sync/IncomingSession.java
+++ b/briar-core/src/org/briarproject/sync/IncomingSession.java
@@ -18,8 +18,6 @@ import org.briarproject.api.sync.MessagingSession;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.PacketReader;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.RetentionAck;
-import org.briarproject.api.sync.RetentionUpdate;
 import org.briarproject.api.sync.SubscriptionAck;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.TransportAck;
@@ -34,7 +32,7 @@ import java.util.logging.Logger;
 import static java.util.logging.Level.WARNING;
 
 /**
- * An incoming {@link MessagingSession
+ * An incoming {@link org.briarproject.api.sync.MessagingSession
  * MessagingSession}.
  */
 class IncomingSession implements MessagingSession, EventListener {
@@ -83,12 +81,6 @@ class IncomingSession implements MessagingSession, EventListener {
 				} else if (packetReader.hasRequest()) {
 					Request r = packetReader.readRequest();
 					dbExecutor.execute(new ReceiveRequest(r));
-				} else if (packetReader.hasRetentionAck()) {
-					RetentionAck a = packetReader.readRetentionAck();
-					dbExecutor.execute(new ReceiveRetentionAck(a));
-				} else if (packetReader.hasRetentionUpdate()) {
-					RetentionUpdate u = packetReader.readRetentionUpdate();
-					dbExecutor.execute(new ReceiveRetentionUpdate(u));
 				} else if (packetReader.hasSubscriptionAck()) {
 					SubscriptionAck a = packetReader.readSubscriptionAck();
 					dbExecutor.execute(new ReceiveSubscriptionAck(a));
@@ -218,42 +210,6 @@ class IncomingSession implements MessagingSession, EventListener {
 		}
 	}
 
-	private class ReceiveRetentionAck implements Runnable {
-
-		private final RetentionAck ack;
-
-		private ReceiveRetentionAck(RetentionAck ack) {
-			this.ack = ack;
-		}
-
-		public void run() {
-			try {
-				db.receiveRetentionAck(contactId, ack);
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
-	private class ReceiveRetentionUpdate implements Runnable {
-
-		private final RetentionUpdate update;
-
-		private ReceiveRetentionUpdate(RetentionUpdate update) {
-			this.update = update;
-		}
-
-		public void run() {
-			try {
-				db.receiveRetentionUpdate(contactId, update);
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
 	private class ReceiveSubscriptionAck implements Runnable {
 
 		private final SubscriptionAck ack;
diff --git a/briar-core/src/org/briarproject/sync/PacketReaderImpl.java b/briar-core/src/org/briarproject/sync/PacketReaderImpl.java
index c5c8e5a09b02f27091218121dd8ad323684324e3..9527c6897b21d5bb1ab30a1ec5351189f2349d1a 100644
--- a/briar-core/src/org/briarproject/sync/PacketReaderImpl.java
+++ b/briar-core/src/org/briarproject/sync/PacketReaderImpl.java
@@ -12,8 +12,6 @@ import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.PacketReader;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.RetentionAck;
-import org.briarproject.api.sync.RetentionUpdate;
 import org.briarproject.api.sync.SubscriptionAck;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.TransportAck;
@@ -40,8 +38,6 @@ import static org.briarproject.api.sync.PacketTypes.ACK;
 import static org.briarproject.api.sync.PacketTypes.MESSAGE;
 import static org.briarproject.api.sync.PacketTypes.OFFER;
 import static org.briarproject.api.sync.PacketTypes.REQUEST;
-import static org.briarproject.api.sync.PacketTypes.RETENTION_ACK;
-import static org.briarproject.api.sync.PacketTypes.RETENTION_UPDATE;
 import static org.briarproject.api.sync.PacketTypes.SUBSCRIPTION_ACK;
 import static org.briarproject.api.sync.PacketTypes.SUBSCRIPTION_UPDATE;
 import static org.briarproject.api.sync.PacketTypes.TRANSPORT_ACK;
@@ -213,52 +209,6 @@ class PacketReaderImpl implements PacketReader {
 		return new Request(Collections.unmodifiableList(requested));
 	}
 
-	public boolean hasRetentionAck() throws IOException {
-		return !eof() && header[1] == RETENTION_ACK;
-	}
-
-	public RetentionAck readRetentionAck() throws IOException {
-		if (!hasRetentionAck()) throw new FormatException();
-		// Set up the reader
-		InputStream bais = new ByteArrayInputStream(payload, 0, payloadLength);
-		Reader r = readerFactory.createReader(bais);
-		// Read the start of the payload
-		r.readListStart();
-		// Read the version
-		long version = r.readInteger();
-		if (version < 0) throw new FormatException();
-		// Read the end of the payload
-		r.readListEnd();
-		if (!r.eof()) throw new FormatException();
-		state = State.BUFFER_EMPTY;
-		// Build and return the retention ack
-		return new RetentionAck(version);
-	}
-
-	public boolean hasRetentionUpdate() throws IOException {
-		return !eof() && header[1] == RETENTION_UPDATE;
-	}
-
-	public RetentionUpdate readRetentionUpdate() throws IOException {
-		if (!hasRetentionUpdate()) throw new FormatException();
-		// Set up the reader
-		InputStream bais = new ByteArrayInputStream(payload, 0, payloadLength);
-		Reader r = readerFactory.createReader(bais);
-		// Read the start of the payload
-		r.readListStart();
-		// Read the retention time and version
-		long retention = r.readInteger();
-		if (retention < 0) throw new FormatException();
-		long version = r.readInteger();
-		if (version < 0) throw new FormatException();
-		// Read the end of the payload
-		r.readListEnd();
-		if (!r.eof()) throw new FormatException();
-		state = State.BUFFER_EMPTY;
-		// Build and return the retention update
-		return new RetentionUpdate(retention, version);
-	}
-
 	public boolean hasSubscriptionAck() throws IOException {
 		return !eof() && header[1] == SUBSCRIPTION_ACK;
 	}
diff --git a/briar-core/src/org/briarproject/sync/PacketWriterImpl.java b/briar-core/src/org/briarproject/sync/PacketWriterImpl.java
index 62263d181e023d9f643db73b1fc520ab472060f1..b30f046155b754aa1387a602093454edfa0ceacf 100644
--- a/briar-core/src/org/briarproject/sync/PacketWriterImpl.java
+++ b/briar-core/src/org/briarproject/sync/PacketWriterImpl.java
@@ -9,8 +9,6 @@ import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.PacketTypes;
 import org.briarproject.api.sync.PacketWriter;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.RetentionAck;
-import org.briarproject.api.sync.RetentionUpdate;
 import org.briarproject.api.sync.SubscriptionAck;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.TransportAck;
@@ -30,8 +28,6 @@ import static org.briarproject.api.sync.MessagingConstants.PROTOCOL_VERSION;
 import static org.briarproject.api.sync.PacketTypes.ACK;
 import static org.briarproject.api.sync.PacketTypes.OFFER;
 import static org.briarproject.api.sync.PacketTypes.REQUEST;
-import static org.briarproject.api.sync.PacketTypes.RETENTION_ACK;
-import static org.briarproject.api.sync.PacketTypes.RETENTION_UPDATE;
 import static org.briarproject.api.sync.PacketTypes.SUBSCRIPTION_ACK;
 import static org.briarproject.api.sync.PacketTypes.SUBSCRIPTION_UPDATE;
 import static org.briarproject.api.sync.PacketTypes.TRANSPORT_ACK;
@@ -120,25 +116,6 @@ class PacketWriterImpl implements PacketWriter {
 		writePacket(REQUEST);
 	}
 
-	public void writeRetentionAck(RetentionAck a) throws IOException {
-		assert payload.size() == 0;
-		Writer w = writerFactory.createWriter(payload);
-		w.writeListStart();
-		w.writeInteger(a.getVersion());
-		w.writeListEnd();
-		writePacket(RETENTION_ACK);
-	}
-
-	public void writeRetentionUpdate(RetentionUpdate u) throws IOException {
-		assert payload.size() == 0;
-		Writer w = writerFactory.createWriter(payload);
-		w.writeListStart();
-		w.writeInteger(u.getRetentionTime());
-		w.writeInteger(u.getVersion());
-		w.writeListEnd();
-		writePacket(RETENTION_UPDATE);
-	}
-
 	public void writeSubscriptionAck(SubscriptionAck a) throws IOException {
 		assert payload.size() == 0;
 		Writer w = writerFactory.createWriter(payload);
diff --git a/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java b/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java
index b82ff02012fcc9999d8e3d9aea9da6d1675d62a8..cfa7c6899c7cfa420371e15674e5a82665418d28 100644
--- a/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java
+++ b/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java
@@ -13,8 +13,6 @@ import org.briarproject.api.event.TransportRemovedEvent;
 import org.briarproject.api.sync.Ack;
 import org.briarproject.api.sync.MessagingSession;
 import org.briarproject.api.sync.PacketWriter;
-import org.briarproject.api.sync.RetentionAck;
-import org.briarproject.api.sync.RetentionUpdate;
 import org.briarproject.api.sync.SubscriptionAck;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.TransportAck;
@@ -33,7 +31,7 @@ import static java.util.logging.Level.WARNING;
 import static org.briarproject.api.sync.MessagingConstants.MAX_PAYLOAD_LENGTH;
 
 /**
- * An outgoing {@link MessagingSession
+ * An outgoing {@link org.briarproject.api.sync.MessagingSession
  * MessagingSession} suitable for simplex transports. The session sends
  * messages without offering them, and closes its output stream when there are
  * no more packets to send.
@@ -70,7 +68,7 @@ class SimplexOutgoingSession implements MessagingSession, EventListener {
 		this.transportId = transportId;
 		this.maxLatency = maxLatency;
 		this.packetWriter = packetWriter;
-		outstandingQueries = new AtomicInteger(8); // One per type of packet
+		outstandingQueries = new AtomicInteger(6); // One per type of packet
 		writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
 	}
 
@@ -82,8 +80,6 @@ class SimplexOutgoingSession implements MessagingSession, EventListener {
 			dbExecutor.execute(new GenerateTransportUpdates());
 			dbExecutor.execute(new GenerateSubscriptionAck());
 			dbExecutor.execute(new GenerateSubscriptionUpdate());
-			dbExecutor.execute(new GenerateRetentionAck());
-			dbExecutor.execute(new GenerateRetentionUpdate());
 			dbExecutor.execute(new GenerateAck());
 			dbExecutor.execute(new GenerateBatch());
 			// Write packets until interrupted or no more packets to write
@@ -196,79 +192,6 @@ class SimplexOutgoingSession implements MessagingSession, EventListener {
 		}
 	}
 
-	// This task runs on the database thread
-	private class GenerateRetentionAck implements Runnable {
-
-		public void run() {
-			if (interrupted) return;
-			try {
-				RetentionAck a = db.generateRetentionAck(contactId);
-				if (LOG.isLoggable(INFO))
-					LOG.info("Generated retention ack: " + (a != null));
-				if (a == null) decrementOutstandingQueries();
-				else writerTasks.add(new WriteRetentionAck(a));
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
-	// This tasks runs on the writer thread
-	private class WriteRetentionAck implements ThrowingRunnable<IOException> {
-
-		private final RetentionAck ack;
-
-		private WriteRetentionAck(RetentionAck ack) {
-			this.ack = ack;
-		}
-
-
-		public void run() throws IOException {
-			if (interrupted) return;
-			packetWriter.writeRetentionAck(ack);
-			LOG.info("Sent retention ack");
-			dbExecutor.execute(new GenerateRetentionAck());
-		}
-	}
-
-	// This task runs on the database thread
-	private class GenerateRetentionUpdate implements Runnable {
-
-		public void run() {
-			if (interrupted) return;
-			try {
-				RetentionUpdate u =
-						db.generateRetentionUpdate(contactId, maxLatency);
-				if (LOG.isLoggable(INFO))
-					LOG.info("Generated retention update: " + (u != null));
-				if (u == null) decrementOutstandingQueries();
-				else writerTasks.add(new WriteRetentionUpdate(u));
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
-	// This task runs on the writer thread
-	private class WriteRetentionUpdate
-	implements ThrowingRunnable<IOException> {
-
-		private final RetentionUpdate update;
-
-		private WriteRetentionUpdate(RetentionUpdate update) {
-			this.update = update;
-		}
-
-		public void run() throws IOException {
-			if (interrupted) return;
-			packetWriter.writeRetentionUpdate(update);
-			LOG.info("Sent retention update");
-			dbExecutor.execute(new GenerateRetentionUpdate());
-		}
-	}
-
 	// This task runs on the database thread
 	private class GenerateSubscriptionAck implements Runnable {
 
diff --git a/briar-tests/build.xml b/briar-tests/build.xml
index 338812a5ddf9e248b2ad000f1b753768625c98c2..72b05fd9b0703b97f7cf7cb158d55f2c5bac2974 100644
--- a/briar-tests/build.xml
+++ b/briar-tests/build.xml
@@ -106,16 +106,15 @@
 			<test name='org.briarproject.data.ReaderImplTest'/>
 			<test name='org.briarproject.data.WriterImplTest'/>
 			<test name='org.briarproject.db.BasicH2Test'/>
-			<test name='org.briarproject.db.DatabaseCleanerImplTest'/>
-			<test name='org.briarproject.db.DatabaseComponentImplTest'/>
+			<test name='org.briarproject.db.DatabaseComponentTest'/>
 			<test name='org.briarproject.db.ExponentialBackoffTest'/>
 			<test name='org.briarproject.lifecycle.ShutdownManagerImplTest'/>
 			<test name='org.briarproject.lifecycle.WindowsShutdownManagerImplTest'/>
-			<test name='org.briarproject.messaging.ConstantsTest'/>
-			<test name='org.briarproject.messaging.ConsumersTest'/>
-			<test name='org.briarproject.messaging.PacketReaderImplTest'/>
-			<test name='org.briarproject.messaging.SimplexMessagingIntegrationTest'/>
-			<test name='org.briarproject.messaging.SimplexOutgoingSessionTest'/>
+			<test name='org.briarproject.sync.ConstantsTest'/>
+			<test name='org.briarproject.sync.ConsumersTest'/>
+			<test name='org.briarproject.sync.PacketReaderImplTest'/>
+			<test name='org.briarproject.sync.SimplexMessagingIntegrationTest'/>
+			<test name='org.briarproject.sync.SimplexOutgoingSessionTest'/>
 			<test name='org.briarproject.plugins.ConnectionRegistryImplTest'/>
 			<test name='org.briarproject.plugins.PluginManagerImplTest'/>
 			<test name='org.briarproject.plugins.file.LinuxRemovableDriveFinderTest'/>
diff --git a/briar-tests/src/org/briarproject/db/DatabaseCleanerImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseCleanerImplTest.java
deleted file mode 100644
index 2e9bf43e224604049971058c5b45b3fae7888cc7..0000000000000000000000000000000000000000
--- a/briar-tests/src/org/briarproject/db/DatabaseCleanerImplTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.briarproject.db;
-
-import org.briarproject.BriarTestCase;
-import org.briarproject.api.db.DbException;
-import org.briarproject.api.system.Timer;
-import org.briarproject.db.DatabaseCleaner.Callback;
-import org.jmock.Expectations;
-import org.jmock.Mockery;
-import org.junit.Test;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-import static org.junit.Assert.assertEquals;
-
-public class DatabaseCleanerImplTest extends BriarTestCase {
-
-	@Test
-	public void testCleanerRunsPeriodically() throws Exception {
-		final AtomicInteger cleans = new AtomicInteger(0);
-		Callback callback = new Callback() {
-
-			boolean check = true;
-
-			public void checkFreeSpaceAndClean() throws DbException {
-				cleans.incrementAndGet();
-			}
-
-			public boolean shouldCheckFreeSpace() {
-				// Alternate between true and false
-				check = !check;
-				return !check;
-			}
-		};
-		Mockery context = new Mockery();
-		final Timer timer = context.mock(Timer.class);
-		final DatabaseCleanerImpl cleaner = new DatabaseCleanerImpl(timer);
-		context.checking(new Expectations() {{
-			oneOf(timer).scheduleAtFixedRate(cleaner, 0, 10);
-			oneOf(timer).cancel();
-		}});
-		// Start the cleaner - it should schedule itself with the timer
-		cleaner.startCleaning(callback, 10);
-		// Call the cleaner's run method six times
-		for (int i = 0; i < 6; i++) cleaner.run();
-		// Stop the cleaner - it should cancel the timer
-		cleaner.stopCleaning();
-		// The database should have been cleaned three times
-		assertEquals(3, cleans.get());
-		context.assertIsSatisfied();
-	}
-}
diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
deleted file mode 100644
index 79e79fd2dba7a3eb4a746b5f77e413cf7e4df46a..0000000000000000000000000000000000000000
--- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.briarproject.db;
-
-import org.briarproject.api.db.DatabaseComponent;
-import org.briarproject.api.db.DbException;
-import org.briarproject.api.event.EventBus;
-import org.briarproject.api.lifecycle.ShutdownManager;
-import org.briarproject.db.DatabaseCleaner.Callback;
-import org.jmock.Expectations;
-import org.jmock.Mockery;
-import org.junit.Test;
-
-import java.util.Collections;
-
-import static org.briarproject.db.DatabaseConstants.BYTES_PER_SWEEP;
-import static org.briarproject.db.DatabaseConstants.MIN_FREE_SPACE;
-
-/**
- * Tests that use the DatabaseCleaner.Callback interface of
- * DatabaseComponentImpl.
- */
-public class DatabaseComponentImplTest extends DatabaseComponentTest {
-
-	@Test
-	public void testNotCleanedIfEnoughFreeSpace() throws DbException {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
-		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final EventBus eventBus = context.mock(EventBus.class);
-		context.checking(new Expectations() {{
-			oneOf(database).getFreeSpace();
-			will(returnValue(MIN_FREE_SPACE));
-		}});
-		Callback db = createDatabaseComponentImpl(database, cleaner, eventBus,
-				shutdown);
-
-		db.checkFreeSpaceAndClean();
-
-		context.assertIsSatisfied();
-	}
-
-	@Test
-	public void testCleanedIfNotEnoughFreeSpace() throws DbException {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
-		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final EventBus eventBus = context.mock(EventBus.class);
-		context.checking(new Expectations() {{
-			oneOf(database).getFreeSpace();
-			will(returnValue(MIN_FREE_SPACE - 1));
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP);
-			will(returnValue(Collections.emptyList()));
-			oneOf(database).commitTransaction(txn);
-			// As if by magic, some free space has appeared
-			oneOf(database).getFreeSpace();
-			will(returnValue(MIN_FREE_SPACE));
-		}});
-		Callback db = createDatabaseComponentImpl(database, cleaner, eventBus,
-				shutdown);
-
-		db.checkFreeSpaceAndClean();
-
-		context.assertIsSatisfied();
-	}
-
-	@Override
-	protected <T> DatabaseComponent createDatabaseComponent(
-			Database<T> database, DatabaseCleaner cleaner, EventBus eventBus,
-			ShutdownManager shutdown) {
-		return createDatabaseComponentImpl(database, cleaner, eventBus,
-				shutdown);
-	}
-
-	private <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
-			Database<T> database, DatabaseCleaner cleaner, EventBus eventBus,
-			ShutdownManager shutdown) {
-		return new DatabaseComponentImpl<T>(database, cleaner, eventBus,
-				shutdown);
-	}
-}
diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentTest.java
index 01dd33cb9f11297d1fb19196e0c2e2c1ab6b350c..f2967c2bc059689033c1d216c990610416de8a99 100644
--- a/briar-tests/src/org/briarproject/db/DatabaseComponentTest.java
+++ b/briar-tests/src/org/briarproject/db/DatabaseComponentTest.java
@@ -40,8 +40,6 @@ import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.RetentionAck;
-import org.briarproject.api.sync.RetentionUpdate;
 import org.briarproject.api.sync.SubscriptionAck;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.TransportAck;
@@ -66,7 +64,7 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
-public abstract class DatabaseComponentTest extends BriarTestCase {
+public class DatabaseComponentTest extends BriarTestCase {
 
 	protected final Object txn = new Object();
 	protected final GroupId groupId;
@@ -114,9 +112,10 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		contact = new Contact(contactId, author, localAuthorId);
 	}
 
-	protected abstract <T> DatabaseComponent createDatabaseComponent(
-			Database<T> database, DatabaseCleaner cleaner, EventBus eventBus,
-			ShutdownManager shutdown);
+	private <T> DatabaseComponent createDatabaseComponent(Database<T> database,
+			EventBus eventBus, ShutdownManager shutdown) {
+		return new DatabaseComponentImpl<T>(database, eventBus, shutdown);
+	}
 
 	@Test
 	@SuppressWarnings("unchecked")
@@ -124,7 +123,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final int shutdownHandle = 12345;
 		Mockery context = new Mockery();
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -134,9 +132,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			// open()
 			oneOf(database).open();
 			will(returnValue(false));
-			oneOf(cleaner).startCleaning(
-					with(any(DatabaseCleaner.Callback.class)),
-					with(any(long.class)));
 			oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
 			will(returnValue(shutdownHandle));
 			// addLocalAuthor()
@@ -201,11 +196,10 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(eventBus).broadcast(with(any(LocalAuthorRemovedEvent.class)));
 			// close()
 			oneOf(shutdown).removeShutdownHook(shutdownHandle);
-			oneOf(cleaner).stopCleaning();
 			oneOf(database).close();
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		assertFalse(db.open());
 		db.addLocalAuthor(localAuthor);
@@ -230,7 +224,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -242,8 +235,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			will(returnValue(true));
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.addLocalMessage(message);
 
@@ -256,7 +249,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -268,8 +260,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			will(returnValue(false));
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.addLocalMessage(message);
 
@@ -281,7 +273,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -304,8 +295,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			// The message was added, so the listener should be called
 			oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.addLocalMessage(message);
 
@@ -318,19 +309,18 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
 			// Check whether the contact is in the DB (which it's not)
-			exactly(25).of(database).startTransaction();
+			exactly(21).of(database).startTransaction();
 			will(returnValue(txn));
-			exactly(25).of(database).containsContact(txn, contactId);
+			exactly(21).of(database).containsContact(txn, contactId);
 			will(returnValue(false));
-			exactly(25).of(database).abortTransaction(txn);
+			exactly(21).of(database).abortTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		try {
 			db.addTransportKeys(contactId, createTransportKeys());
@@ -360,20 +350,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			// Expected
 		}
 
-		try {
-			db.generateRetentionAck(contactId);
-			fail();
-		} catch (NoSuchContactException expected) {
-			// Expected
-		}
-
-		try {
-			db.generateRetentionUpdate(contactId, 123);
-			fail();
-		} catch (NoSuchContactException expected) {
-			// Expected
-		}
-
 		try {
 			db.generateSubscriptionAck(contactId);
 			fail();
@@ -446,22 +422,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			// Expected
 		}
 
-		try {
-			RetentionAck a = new RetentionAck(0);
-			db.receiveRetentionAck(contactId, a);
-			fail();
-		} catch (NoSuchContactException expected) {
-			// Expected
-		}
-
-		try {
-			RetentionUpdate u = new RetentionUpdate(0, 1);
-			db.receiveRetentionUpdate(contactId, u);
-			fail();
-		} catch (NoSuchContactException expected) {
-			// Expected
-		}
-
 		try {
 			SubscriptionAck a = new SubscriptionAck(0);
 			db.receiveSubscriptionAck(contactId, a);
@@ -526,7 +486,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -540,8 +499,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			exactly(1).of(database).containsContact(txn, authorId);
 			will(returnValue(false));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		try {
 			db.addContact(author, localAuthorId);
@@ -573,7 +532,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -584,8 +542,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			will(returnValue(false));
 			exactly(5).of(database).abortTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		try {
 			db.getGroup(groupId);
@@ -631,7 +589,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -663,8 +620,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			will(returnValue(false));
 			exactly(8).of(database).abortTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.addLocalAuthor(localAuthor);
 		assertEquals(contactId, db.addContact(author, localAuthorId));
@@ -735,7 +692,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -748,8 +704,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).lowerAckFlag(txn, contactId, messagesToAck);
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		Ack a = db.generateAck(contactId, 123);
 		assertEquals(messagesToAck, a.getMessageIds());
@@ -765,7 +721,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -787,8 +742,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).commitTransaction(txn);
 			oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		assertEquals(messages, db.generateBatch(contactId, size * 2,
 				maxLatency));
@@ -803,7 +758,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -819,8 +773,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 					maxLatency);
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		Offer o = db.generateOffer(contactId, 123, maxLatency);
 		assertEquals(ids, o.getMessageIds());
@@ -835,7 +789,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -848,8 +801,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).removeOfferedMessages(txn, contactId, ids);
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		Request r = db.generateRequest(contactId, 123);
 		assertEquals(ids, r.getMessageIds());
@@ -865,7 +818,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -888,8 +840,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).commitTransaction(txn);
 			oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		assertEquals(messages, db.generateRequestedBatch(contactId, size * 2,
 				maxLatency));
@@ -897,64 +849,11 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		context.assertIsSatisfied();
 	}
 
-	@Test
-	public void testGenerateRetentionUpdateNoUpdateDue() throws Exception {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
-		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final EventBus eventBus = context.mock(EventBus.class);
-		context.checking(new Expectations() {{
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).containsContact(txn, contactId);
-			will(returnValue(true));
-			oneOf(database).getRetentionUpdate(txn, contactId, maxLatency);
-			will(returnValue(null));
-			oneOf(database).commitTransaction(txn);
-		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
-
-		assertNull(db.generateRetentionUpdate(contactId, maxLatency));
-
-		context.assertIsSatisfied();
-	}
-
-	@Test
-	public void testGenerateRetentionUpdate() throws Exception {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
-		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final EventBus eventBus = context.mock(EventBus.class);
-		context.checking(new Expectations() {{
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).containsContact(txn, contactId);
-			will(returnValue(true));
-			oneOf(database).getRetentionUpdate(txn, contactId, maxLatency);
-			will(returnValue(new RetentionUpdate(0, 1)));
-			oneOf(database).commitTransaction(txn);
-		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
-
-		RetentionUpdate u = db.generateRetentionUpdate(contactId, maxLatency);
-		assertEquals(0, u.getRetentionTime());
-		assertEquals(1, u.getVersion());
-
-		context.assertIsSatisfied();
-	}
-
 	@Test
 	public void testGenerateSubscriptionUpdateNoUpdateDue() throws Exception {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -966,8 +865,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			will(returnValue(null));
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		assertNull(db.generateSubscriptionUpdate(contactId, maxLatency));
 
@@ -979,7 +878,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -992,8 +890,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 					Collections.singletonList(group), 1)));
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		SubscriptionUpdate u = db.generateSubscriptionUpdate(contactId,
 				maxLatency);
@@ -1008,7 +906,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1020,8 +917,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			will(returnValue(null));
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		assertNull(db.generateTransportUpdates(contactId, maxLatency));
 
@@ -1033,7 +930,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1046,8 +942,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 					transportId, transportProperties, 1))));
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		Collection<TransportUpdate> updates =
 				db.generateTransportUpdates(contactId, maxLatency);
@@ -1066,7 +962,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1080,8 +975,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).commitTransaction(txn);
 			oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.receiveAck(contactId, new Ack(Collections.singletonList(messageId)));
 
@@ -1093,7 +988,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1119,8 +1013,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class)));
 			oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.receiveMessage(contactId, message);
 
@@ -1132,7 +1026,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1150,8 +1043,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			// The message was received but not added
 			oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.receiveMessage(contactId, message);
 
@@ -1163,7 +1056,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1177,8 +1069,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			will(returnValue(false));
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.receiveMessage(contactId, message);
 
@@ -1193,7 +1085,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1224,8 +1115,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class)));
 			oneOf(eventBus).broadcast(with(any(MessageToRequestEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		Offer o = new Offer(Arrays.asList(messageId, messageId1, messageId2,
 				messageId3));
@@ -1238,7 +1129,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1253,8 +1143,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).commitTransaction(txn);
 			oneOf(eventBus).broadcast(with(any(MessageRequestedEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.receiveRequest(contactId, new Request(Collections.singletonList(
 				messageId)));
@@ -1262,37 +1152,11 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		context.assertIsSatisfied();
 	}
 
-	@Test
-	public void testReceiveRetentionAck() throws Exception {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
-		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final EventBus eventBus = context.mock(EventBus.class);
-		context.checking(new Expectations() {{
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).containsContact(txn, contactId);
-			will(returnValue(true));
-			oneOf(database).setRetentionUpdateAcked(txn, contactId, 1);
-			oneOf(database).commitTransaction(txn);
-		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
-
-		RetentionAck a = new RetentionAck(1);
-		db.receiveRetentionAck(contactId, a);
-
-		context.assertIsSatisfied();
-	}
-
 	@Test
 	public void testReceiveSubscriptionAck() throws Exception {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1303,8 +1167,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).setSubscriptionUpdateAcked(txn, contactId, 1);
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		SubscriptionAck a = new SubscriptionAck(1);
 		db.receiveSubscriptionAck(contactId, a);
@@ -1317,7 +1181,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1329,8 +1192,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 					Collections.singletonList(group), 1);
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		SubscriptionUpdate u = new SubscriptionUpdate(
 				Collections.singletonList(group), 1);
@@ -1344,7 +1207,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1358,8 +1220,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 					transportId, 1);
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		TransportAck a = new TransportAck(transportId, 1);
 		db.receiveTransportAck(contactId, a);
@@ -1372,7 +1234,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1384,8 +1245,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 					transportProperties, 1);
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		TransportUpdate u = new TransportUpdate(transportId,
 				transportProperties, 1);
@@ -1402,7 +1263,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1417,8 +1277,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(eventBus).broadcast(with(any(
 					LocalTransportsUpdatedEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.mergeLocalProperties(transportId, properties);
 
@@ -1433,7 +1293,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1445,8 +1304,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			will(returnValue(properties));
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.mergeLocalProperties(transportId, properties);
 
@@ -1460,7 +1319,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1478,8 +1336,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(eventBus).broadcast(with(any(
 					LocalSubscriptionsUpdatedEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.setVisibility(groupId, Collections.singletonList(contactId));
 
@@ -1494,7 +1352,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1509,8 +1366,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).setVisibleToAll(txn, groupId, false);
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.setVisibility(groupId, both);
 
@@ -1525,7 +1382,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1558,8 +1414,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(eventBus).broadcast(with(any(
 					LocalSubscriptionsUpdatedEvent.class)));
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.setVisibility(groupId, Collections.singletonList(contactId));
 		db.setVisibleToAll(groupId, true);
@@ -1573,7 +1429,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
 		final Database<Object> database = context.mock(Database.class);
-		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
@@ -1596,8 +1451,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			will(returnValue(Collections.singletonMap(contactId, keys)));
 			oneOf(database).commitTransaction(txn);
 		}});
-		DatabaseComponent db = createDatabaseComponent(database, cleaner,
-				eventBus, shutdown);
+		DatabaseComponent db = createDatabaseComponent(database, eventBus,
+				shutdown);
 
 		db.updateTransportKeys(Collections.singletonMap(contactId, keys));
 		assertEquals(Collections.singletonMap(contactId, keys),
diff --git a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
index 686789f687f62e0089f88f0cd80db7dc0d7b9453..2a42f5ea5f01e7a84d4baed6f4a2c2c35d775721 100644
--- a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
+++ b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
@@ -32,7 +32,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -404,36 +403,6 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.close();
 	}
 
-	@Test
-	public void testGetOldMessages() throws Exception {
-		MessageId messageId1 = new MessageId(TestUtils.getRandomId());
-		Message message1 = new TestMessage(messageId1, null, group, author,
-				contentType, subject, timestamp + 1000, raw);
-		Database<Connection> db = open(false);
-		Connection txn = db.startTransaction();
-
-		// Subscribe to a group and store two messages
-		db.addGroup(txn, group);
-		db.addMessage(txn, message, true);
-		db.addMessage(txn, message1, true);
-
-		// Allowing enough capacity for one message should return the older one
-		Iterator<MessageId> it = db.getOldMessages(txn, size).iterator();
-		assertTrue(it.hasNext());
-		assertEquals(messageId, it.next());
-		assertFalse(it.hasNext());
-
-		// Allowing enough capacity for both messages should return both
-		Collection<MessageId> ids = new HashSet<MessageId>();
-		for (MessageId id : db.getOldMessages(txn, size * 2)) ids.add(id);
-		assertEquals(2, ids.size());
-		assertTrue(ids.contains(messageId));
-		assertTrue(ids.contains(messageId1));
-
-		db.commitTransaction(txn);
-		db.close();
-	}
-
 	@Test
 	public void testGetFreeSpace() throws Exception {
 		byte[] largeBody = new byte[ONE_MEGABYTE];
diff --git a/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java b/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java
index f3b27bf85bd8bfce53d7811c4b818f19453e872e..714e02430244786098eaa98bc0bcd637f245df78 100644
--- a/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java
+++ b/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java
@@ -65,12 +65,6 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
 			// No subscription update to send
 			oneOf(db).generateSubscriptionUpdate(contactId, maxLatency);
 			will(returnValue(null));
-			// No retention ack to send
-			oneOf(db).generateRetentionAck(contactId);
-			will(returnValue(null));
-			// No retention update to send
-			oneOf(db).generateRetentionUpdate(contactId, maxLatency);
-			will(returnValue(null));
 			// No acks to send
 			oneOf(packetWriter).getMaxMessagesForAck(with(any(long.class)));
 			will(returnValue(MAX_MESSAGES_PER_ACK));
@@ -112,12 +106,6 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
 			// No subscription update to send
 			oneOf(db).generateSubscriptionUpdate(contactId, maxLatency);
 			will(returnValue(null));
-			// No retention ack to send
-			oneOf(db).generateRetentionAck(contactId);
-			will(returnValue(null));
-			// No retention update to send
-			oneOf(db).generateRetentionUpdate(contactId, maxLatency);
-			will(returnValue(null));
 			// One ack to send
 			oneOf(packetWriter).getMaxMessagesForAck(with(any(long.class)));
 			will(returnValue(MAX_MESSAGES_PER_ACK));