diff --git a/briar-android/src/net/sf/briar/android/AndroidFileUtils.java b/briar-android/src/net/sf/briar/android/AndroidFileUtils.java
index 9d52ae01e81a9b40505f0f7c98f64d05c6853173..a7c286e04c9922f878752f62551845550ce2c18b 100644
--- a/briar-android/src/net/sf/briar/android/AndroidFileUtils.java
+++ b/briar-android/src/net/sf/briar/android/AndroidFileUtils.java
@@ -3,7 +3,7 @@ package net.sf.briar.android;
 import java.io.File;
 import java.io.IOException;
 
-import net.sf.briar.api.os.FileUtils;
+import net.sf.briar.api.system.FileUtils;
 import android.os.Build;
 import android.os.StatFs;
 
diff --git a/briar-android/src/net/sf/briar/android/AndroidModule.java b/briar-android/src/net/sf/briar/android/AndroidModule.java
index 1a584579a5fc31804ceedec446fdc56494f594fe..a25cf96dc29f9fe9012ad004b9be721ed53aeb22 100644
--- a/briar-android/src/net/sf/briar/android/AndroidModule.java
+++ b/briar-android/src/net/sf/briar/android/AndroidModule.java
@@ -23,12 +23,12 @@ import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.db.DatabaseConfig;
 import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.lifecycle.ShutdownManager;
-import net.sf.briar.api.os.FileUtils;
 import net.sf.briar.api.plugins.PluginExecutor;
 import net.sf.briar.api.plugins.duplex.DuplexPluginConfig;
 import net.sf.briar.api.plugins.duplex.DuplexPluginFactory;
 import net.sf.briar.api.plugins.simplex.SimplexPluginConfig;
 import net.sf.briar.api.plugins.simplex.SimplexPluginFactory;
+import net.sf.briar.api.system.FileUtils;
 import net.sf.briar.api.ui.UiCallback;
 import net.sf.briar.plugins.droidtooth.DroidtoothPluginFactory;
 import net.sf.briar.plugins.tcp.DroidLanTcpPluginFactory;
diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
index 1b64b76352c5f4c594380e9ae5bf17dcfdcebef7..2fb503234b9aa4ac26da24dd88f1b3ba087e828e 100644
--- a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
+++ b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
@@ -29,12 +29,12 @@ import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.MessageHeader;
 import net.sf.briar.api.db.NoSuchContactException;
-import net.sf.briar.api.db.event.ContactAddedEvent;
-import net.sf.briar.api.db.event.ContactRemovedEvent;
-import net.sf.briar.api.db.event.DatabaseEvent;
-import net.sf.briar.api.db.event.DatabaseListener;
-import net.sf.briar.api.db.event.MessageAddedEvent;
-import net.sf.briar.api.db.event.MessageExpiredEvent;
+import net.sf.briar.api.event.ContactAddedEvent;
+import net.sf.briar.api.event.ContactRemovedEvent;
+import net.sf.briar.api.event.Event;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.event.MessageAddedEvent;
+import net.sf.briar.api.event.MessageExpiredEvent;
 import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.messaging.GroupId;
 import net.sf.briar.api.transport.ConnectionListener;
@@ -51,7 +51,7 @@ import android.widget.LinearLayout;
 import android.widget.ListView;
 
 public class ContactListActivity extends RoboActivity
-implements OnClickListener, OnItemClickListener, DatabaseListener,
+implements OnClickListener, OnItemClickListener, EventListener,
 ConnectionListener {
 
 	private static final Logger LOG =
@@ -219,7 +219,7 @@ ConnectionListener {
 		startActivity(i);
 	}
 
-	public void eventOccurred(DatabaseEvent e) {
+	public void eventOccurred(Event e) {
 		if(e instanceof ContactAddedEvent) {
 			loadContacts();
 		} else if(e instanceof ContactRemovedEvent) {
diff --git a/briar-android/src/net/sf/briar/android/contact/ConversationActivity.java b/briar-android/src/net/sf/briar/android/contact/ConversationActivity.java
index 29d32232870ae4763186e8a59e914a024edd8ad8..d4ae17170c33e2d2306cc0fcbcfa81b94aef2fb0 100644
--- a/briar-android/src/net/sf/briar/android/contact/ConversationActivity.java
+++ b/briar-android/src/net/sf/briar/android/contact/ConversationActivity.java
@@ -25,11 +25,11 @@ import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.MessageHeader;
 import net.sf.briar.api.db.NoSuchContactException;
-import net.sf.briar.api.db.event.ContactRemovedEvent;
-import net.sf.briar.api.db.event.DatabaseEvent;
-import net.sf.briar.api.db.event.DatabaseListener;
-import net.sf.briar.api.db.event.MessageAddedEvent;
-import net.sf.briar.api.db.event.MessageExpiredEvent;
+import net.sf.briar.api.event.ContactRemovedEvent;
+import net.sf.briar.api.event.Event;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.event.MessageAddedEvent;
+import net.sf.briar.api.event.MessageExpiredEvent;
 import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.messaging.GroupId;
 import roboguice.activity.RoboActivity;
@@ -44,7 +44,7 @@ import android.widget.LinearLayout;
 import android.widget.ListView;
 
 public class ConversationActivity extends RoboActivity
-implements DatabaseListener, OnClickListener, OnItemClickListener {
+implements EventListener, OnClickListener, OnItemClickListener {
 
 	private static final Logger LOG =
 			Logger.getLogger(ConversationActivity.class.getName());
@@ -196,7 +196,7 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
 		db.removeListener(this);
 	}
 
-	public void eventOccurred(DatabaseEvent e) {
+	public void eventOccurred(Event e) {
 		if(e instanceof ContactRemovedEvent) {
 			ContactRemovedEvent c = (ContactRemovedEvent) e;
 			if(c.getContactId().equals(contactId)) {
diff --git a/briar-android/src/net/sf/briar/android/groups/GroupActivity.java b/briar-android/src/net/sf/briar/android/groups/GroupActivity.java
index fef29b665a1bfdd72ad1314faf4638cf83bf0766..d42d33e254df13455057a5fcd688a4ed230b5944 100644
--- a/briar-android/src/net/sf/briar/android/groups/GroupActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/GroupActivity.java
@@ -27,11 +27,11 @@ import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.MessageHeader;
 import net.sf.briar.api.db.NoSuchSubscriptionException;
-import net.sf.briar.api.db.event.DatabaseEvent;
-import net.sf.briar.api.db.event.DatabaseListener;
-import net.sf.briar.api.db.event.MessageAddedEvent;
-import net.sf.briar.api.db.event.MessageExpiredEvent;
-import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
+import net.sf.briar.api.event.Event;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.event.MessageAddedEvent;
+import net.sf.briar.api.event.MessageExpiredEvent;
+import net.sf.briar.api.event.SubscriptionRemovedEvent;
 import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.messaging.GroupId;
 import roboguice.activity.RoboActivity;
@@ -45,7 +45,7 @@ import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 
-public class GroupActivity extends RoboActivity implements DatabaseListener,
+public class GroupActivity extends RoboActivity implements EventListener,
 OnClickListener, OnItemClickListener {
 
 	private static final Logger LOG =
@@ -186,7 +186,7 @@ OnClickListener, OnItemClickListener {
 		db.removeListener(this);
 	}
 
-	public void eventOccurred(DatabaseEvent e) {
+	public void eventOccurred(Event e) {
 		if(e instanceof MessageAddedEvent) {
 			if(((MessageAddedEvent) e).getGroup().getId().equals(groupId)) {
 				if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
diff --git a/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java b/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java
index 0689eba6d0e7d842fa01b22a64d68790bacc35ea..8ff3f80e93235beaab860f02cee97c7ebfbd3e94 100644
--- a/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java
@@ -31,13 +31,13 @@ import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.MessageHeader;
 import net.sf.briar.api.db.NoSuchSubscriptionException;
-import net.sf.briar.api.db.event.DatabaseEvent;
-import net.sf.briar.api.db.event.DatabaseListener;
-import net.sf.briar.api.db.event.MessageAddedEvent;
-import net.sf.briar.api.db.event.MessageExpiredEvent;
-import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
-import net.sf.briar.api.db.event.SubscriptionAddedEvent;
-import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
+import net.sf.briar.api.event.Event;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.event.MessageAddedEvent;
+import net.sf.briar.api.event.MessageExpiredEvent;
+import net.sf.briar.api.event.RemoteSubscriptionsUpdatedEvent;
+import net.sf.briar.api.event.SubscriptionAddedEvent;
+import net.sf.briar.api.event.SubscriptionRemovedEvent;
 import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.messaging.Group;
 import net.sf.briar.api.messaging.GroupId;
@@ -54,7 +54,7 @@ import android.widget.LinearLayout;
 import android.widget.ListView;
 
 public class GroupListActivity extends RoboFragmentActivity
-implements DatabaseListener, OnClickListener, OnItemClickListener {
+implements EventListener, OnClickListener, OnItemClickListener {
 
 	private static final Logger LOG =
 			Logger.getLogger(GroupListActivity.class.getName());
@@ -238,7 +238,7 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
 		db.removeListener(this);
 	}
 
-	public void eventOccurred(DatabaseEvent e) {
+	public void eventOccurred(Event e) {
 		if(e instanceof MessageAddedEvent) {
 			Group g = ((MessageAddedEvent) e).getGroup();
 			if(groups.containsKey(g.getId())) {
diff --git a/briar-android/src/net/sf/briar/android/groups/ManageGroupsActivity.java b/briar-android/src/net/sf/briar/android/groups/ManageGroupsActivity.java
index 282fa6d29f40a35f1676a6efd362483261844e9f..9c265d573d617a4a044d12703371b2cd453ed162 100644
--- a/briar-android/src/net/sf/briar/android/groups/ManageGroupsActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/ManageGroupsActivity.java
@@ -16,11 +16,11 @@ import net.sf.briar.android.util.ListLoadingProgressBar;
 import net.sf.briar.api.android.DatabaseUiExecutor;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
-import net.sf.briar.api.db.event.DatabaseEvent;
-import net.sf.briar.api.db.event.DatabaseListener;
-import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
-import net.sf.briar.api.db.event.SubscriptionAddedEvent;
-import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
+import net.sf.briar.api.event.Event;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.event.RemoteSubscriptionsUpdatedEvent;
+import net.sf.briar.api.event.SubscriptionAddedEvent;
+import net.sf.briar.api.event.SubscriptionRemovedEvent;
 import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.messaging.Group;
 import net.sf.briar.api.messaging.GroupStatus;
@@ -33,7 +33,7 @@ import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ListView;
 
 public class ManageGroupsActivity extends RoboFragmentActivity
-implements DatabaseListener, OnItemClickListener {
+implements EventListener, OnItemClickListener {
 
 	private static final Logger LOG =
 			Logger.getLogger(ManageGroupsActivity.class.getName());
@@ -111,7 +111,7 @@ implements DatabaseListener, OnItemClickListener {
 		db.removeListener(this);
 	}
 
-	public void eventOccurred(DatabaseEvent e) {
+	public void eventOccurred(Event e) {
 		if(e instanceof RemoteSubscriptionsUpdatedEvent) {
 			if(LOG.isLoggable(INFO))
 				LOG.info("Remote subscriptions changed, reloading");
diff --git a/briar-android/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java b/briar-android/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java
index 407daedd68a1c9a13d065705cdc144583b634072..417f7c931e748fe919bd4585f9ec99166e67ce12 100644
--- a/briar-android/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java
+++ b/briar-android/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java
@@ -28,11 +28,11 @@ import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.android.AndroidExecutor;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.PseudoRandom;
 import net.sf.briar.api.plugins.duplex.DuplexPlugin;
 import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
 import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.util.LatchedReference;
 import net.sf.briar.util.StringUtils;
 import android.bluetooth.BluetoothAdapter;
diff --git a/briar-android/src/net/sf/briar/plugins/droidtooth/DroidtoothPluginFactory.java b/briar-android/src/net/sf/briar/plugins/droidtooth/DroidtoothPluginFactory.java
index 6599601c318015f9fe0b08526d044bb6020d0fbb..384f129b62782584c6c6a4063b97f28c94f8c911 100644
--- a/briar-android/src/net/sf/briar/plugins/droidtooth/DroidtoothPluginFactory.java
+++ b/briar-android/src/net/sf/briar/plugins/droidtooth/DroidtoothPluginFactory.java
@@ -5,11 +5,11 @@ import java.util.concurrent.Executor;
 
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.android.AndroidExecutor;
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.plugins.duplex.DuplexPlugin;
 import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
 import net.sf.briar.api.plugins.duplex.DuplexPluginFactory;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.SystemClock;
 import android.content.Context;
 
 public class DroidtoothPluginFactory implements DuplexPluginFactory {
diff --git a/briar-android/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java b/briar-android/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java
index 9853c23aa6e4acd153429ec622161f03da8b3464..20bcd3867d28ed74e0e11f3b87c9232c72265147 100644
--- a/briar-android/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java
+++ b/briar-android/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java
@@ -4,10 +4,10 @@ import static android.content.Context.WIFI_SERVICE;
 
 import java.util.concurrent.Executor;
 
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.PseudoRandom;
 import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
 import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
+import net.sf.briar.api.system.Clock;
 import android.content.Context;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.MulticastLock;
diff --git a/briar-android/src/net/sf/briar/plugins/tcp/DroidLanTcpPluginFactory.java b/briar-android/src/net/sf/briar/plugins/tcp/DroidLanTcpPluginFactory.java
index 32ca3261a7b0ac32b6e3824cec22eb0cc46c4d70..ad7b2986876b8a300ce5c6f953ae5862f1cc3864 100644
--- a/briar-android/src/net/sf/briar/plugins/tcp/DroidLanTcpPluginFactory.java
+++ b/briar-android/src/net/sf/briar/plugins/tcp/DroidLanTcpPluginFactory.java
@@ -3,11 +3,11 @@ package net.sf.briar.plugins.tcp;
 import java.util.concurrent.Executor;
 
 import net.sf.briar.api.TransportId;
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.plugins.duplex.DuplexPlugin;
 import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
 import net.sf.briar.api.plugins.duplex.DuplexPluginFactory;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.SystemClock;
 import android.content.Context;
 
 public class DroidLanTcpPluginFactory implements DuplexPluginFactory {
diff --git a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java
index 4d3ab1808037f10a1e0c0efb6c8db2110ddf7084..43e1d387694d841750b10ba83390b1f7b39758f6 100644
--- a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java
+++ b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java
@@ -12,7 +12,7 @@ import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.db.event.DatabaseListener;
+import net.sf.briar.api.event.EventListener;
 import net.sf.briar.api.messaging.Ack;
 import net.sf.briar.api.messaging.Group;
 import net.sf.briar.api.messaging.GroupId;
@@ -43,10 +43,10 @@ public interface DatabaseComponent {
 	void close() throws DbException, IOException;
 
 	/** Adds a listener to be notified when database events occur. */
-	void addListener(DatabaseListener d);
+	void addListener(EventListener d);
 
 	/** Removes a listener. */
-	void removeListener(DatabaseListener d);
+	void removeListener(EventListener d);
 
 	/**
 	 * Stores a contact associated with the given local and remote pseudonyms,
diff --git a/briar-api/src/net/sf/briar/api/db/event/DatabaseEvent.java b/briar-api/src/net/sf/briar/api/db/event/DatabaseEvent.java
deleted file mode 100644
index 2316bdef350f98cd8ebe2e63f1000d2b03be5560..0000000000000000000000000000000000000000
--- a/briar-api/src/net/sf/briar/api/db/event/DatabaseEvent.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package net.sf.briar.api.db.event;
-
-/** An abstract superclass for database events. */
-public abstract class DatabaseEvent {
-
-}
diff --git a/briar-api/src/net/sf/briar/api/db/event/DatabaseListener.java b/briar-api/src/net/sf/briar/api/db/event/DatabaseListener.java
deleted file mode 100644
index 58605bee125550652d113bbb480b915772763f38..0000000000000000000000000000000000000000
--- a/briar-api/src/net/sf/briar/api/db/event/DatabaseListener.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.sf.briar.api.db.event;
-
-/** An interface for receiving notifications when database events occur. */
-public interface DatabaseListener {
-
-	void eventOccurred(DatabaseEvent e);
-}
diff --git a/briar-api/src/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java b/briar-api/src/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java
deleted file mode 100644
index c60c23b83b1000df6d6b7d933bbb8f2822abfb2e..0000000000000000000000000000000000000000
--- a/briar-api/src/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package net.sf.briar.api.db.event;
-
-/**
- * An event that is broadcast when the local transport properties are
- * updated.
- */
-public class LocalTransportsUpdatedEvent extends DatabaseEvent {
-
-}
diff --git a/briar-api/src/net/sf/briar/api/db/event/ContactAddedEvent.java b/briar-api/src/net/sf/briar/api/event/ContactAddedEvent.java
similarity index 75%
rename from briar-api/src/net/sf/briar/api/db/event/ContactAddedEvent.java
rename to briar-api/src/net/sf/briar/api/event/ContactAddedEvent.java
index 84cc5f298865d7bde35ca176283ba5081ebd7519..9e1cc4dfc1843869643e2073efe273509a4232e7 100644
--- a/briar-api/src/net/sf/briar/api/db/event/ContactAddedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/ContactAddedEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.ContactId;
 
 /** An event that is broadcast when a contact is added. */
-public class ContactAddedEvent extends DatabaseEvent {
+public class ContactAddedEvent extends Event {
 
 	private final ContactId contactId;
 
diff --git a/briar-api/src/net/sf/briar/api/db/event/ContactRemovedEvent.java b/briar-api/src/net/sf/briar/api/event/ContactRemovedEvent.java
similarity index 75%
rename from briar-api/src/net/sf/briar/api/db/event/ContactRemovedEvent.java
rename to briar-api/src/net/sf/briar/api/event/ContactRemovedEvent.java
index 74eed602994e691912f2f385d6d8a666b65c97bb..e4ad51c5a4377e5e648d94be69333f5e9f44a3eb 100644
--- a/briar-api/src/net/sf/briar/api/db/event/ContactRemovedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/ContactRemovedEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.ContactId;
 
 /** An event that is broadcast when a contact is removed. */
-public class ContactRemovedEvent extends DatabaseEvent {
+public class ContactRemovedEvent extends Event {
 
 	private final ContactId contactId;
 
diff --git a/briar-api/src/net/sf/briar/api/event/Event.java b/briar-api/src/net/sf/briar/api/event/Event.java
new file mode 100644
index 0000000000000000000000000000000000000000..610194452496d784107b6805092f4a95eedae15d
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/event/Event.java
@@ -0,0 +1,6 @@
+package net.sf.briar.api.event;
+
+/** An abstract superclass for events. */
+public abstract class Event {
+
+}
diff --git a/briar-api/src/net/sf/briar/api/event/EventListener.java b/briar-api/src/net/sf/briar/api/event/EventListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..9db96a3d5e08532d84a94240ed72d0df503101e4
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/event/EventListener.java
@@ -0,0 +1,7 @@
+package net.sf.briar.api.event;
+
+/** An interface for receiving notifications when events occur. */
+public interface EventListener {
+
+	void eventOccurred(Event e);
+}
diff --git a/briar-api/src/net/sf/briar/api/db/event/LocalAuthorAddedEvent.java b/briar-api/src/net/sf/briar/api/event/LocalAuthorAddedEvent.java
similarity index 74%
rename from briar-api/src/net/sf/briar/api/db/event/LocalAuthorAddedEvent.java
rename to briar-api/src/net/sf/briar/api/event/LocalAuthorAddedEvent.java
index 1d9c3054379a38a7a966925699c913005904a7dd..2bd6c4fe273d085b152f01138b0b653a3df63dc2 100644
--- a/briar-api/src/net/sf/briar/api/db/event/LocalAuthorAddedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/LocalAuthorAddedEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.AuthorId;
 
 /** An event that is broadcast when a local pseudonym is added. */
-public class LocalAuthorAddedEvent extends DatabaseEvent {
+public class LocalAuthorAddedEvent extends Event {
 
 	private final AuthorId authorId;
 
diff --git a/briar-api/src/net/sf/briar/api/db/event/LocalAuthorRemovedEvent.java b/briar-api/src/net/sf/briar/api/event/LocalAuthorRemovedEvent.java
similarity index 74%
rename from briar-api/src/net/sf/briar/api/db/event/LocalAuthorRemovedEvent.java
rename to briar-api/src/net/sf/briar/api/event/LocalAuthorRemovedEvent.java
index cf4e18e75880f9c0424ccb6c8ed6a4b1dd013ba7..11666979368e1a778d2fa950c6c93176725671ce 100644
--- a/briar-api/src/net/sf/briar/api/db/event/LocalAuthorRemovedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/LocalAuthorRemovedEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.AuthorId;
 
 /** An event that is broadcast when a local pseudonym is removed. */
-public class LocalAuthorRemovedEvent extends DatabaseEvent {
+public class LocalAuthorRemovedEvent extends Event {
 
 	private final AuthorId authorId;
 
diff --git a/briar-api/src/net/sf/briar/api/db/event/LocalSubscriptionsUpdatedEvent.java b/briar-api/src/net/sf/briar/api/event/LocalSubscriptionsUpdatedEvent.java
similarity index 81%
rename from briar-api/src/net/sf/briar/api/db/event/LocalSubscriptionsUpdatedEvent.java
rename to briar-api/src/net/sf/briar/api/event/LocalSubscriptionsUpdatedEvent.java
index fcd2ddeb6677ebf739096d7671385a0a3fdea546..934eee61e1c792fb3a013927e11aa056353936f8 100644
--- a/briar-api/src/net/sf/briar/api/db/event/LocalSubscriptionsUpdatedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/LocalSubscriptionsUpdatedEvent.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import java.util.Collection;
 
@@ -8,7 +8,7 @@ import net.sf.briar.api.ContactId;
  * An event that is broadcast when the set of subscriptions visible to one or
  * more contacts is updated.
  */
-public class LocalSubscriptionsUpdatedEvent extends DatabaseEvent {
+public class LocalSubscriptionsUpdatedEvent extends Event {
 
 	private final Collection<ContactId> affected;
 
diff --git a/briar-api/src/net/sf/briar/api/event/LocalTransportsUpdatedEvent.java b/briar-api/src/net/sf/briar/api/event/LocalTransportsUpdatedEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3be88940f79da7bd6c1ed72e5cbfe9ac2788bc0
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/event/LocalTransportsUpdatedEvent.java
@@ -0,0 +1,9 @@
+package net.sf.briar.api.event;
+
+/**
+ * An event that is broadcast when the local transport properties are
+ * updated.
+ */
+public class LocalTransportsUpdatedEvent extends Event {
+
+}
diff --git a/briar-api/src/net/sf/briar/api/db/event/MessageAddedEvent.java b/briar-api/src/net/sf/briar/api/event/MessageAddedEvent.java
similarity index 87%
rename from briar-api/src/net/sf/briar/api/db/event/MessageAddedEvent.java
rename to briar-api/src/net/sf/briar/api/event/MessageAddedEvent.java
index 8f04e5d141143475bde31b444d454aa8411dcb70..ba7e54c1e0dbe03a0dbedb84ba718a2a8d4d1378 100644
--- a/briar-api/src/net/sf/briar/api/db/event/MessageAddedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/MessageAddedEvent.java
@@ -1,10 +1,10 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.messaging.Group;
 
 /** An event that is broadcast when a message is added to the database. */
-public class MessageAddedEvent extends DatabaseEvent {
+public class MessageAddedEvent extends Event {
 
 	private final Group group;
 	private final ContactId contactId;
diff --git a/briar-api/src/net/sf/briar/api/db/event/MessageExpiredEvent.java b/briar-api/src/net/sf/briar/api/event/MessageExpiredEvent.java
similarity index 61%
rename from briar-api/src/net/sf/briar/api/db/event/MessageExpiredEvent.java
rename to briar-api/src/net/sf/briar/api/event/MessageExpiredEvent.java
index 5f130f78a7f5fb00f80c0197549168c2d69c2e02..e7252d08c5a3fbfa3ba9bc2d8842e3a858e7d10b 100644
--- a/briar-api/src/net/sf/briar/api/db/event/MessageExpiredEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/MessageExpiredEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.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 DatabaseEvent {
+public class MessageExpiredEvent extends Event {
 
 }
diff --git a/briar-api/src/net/sf/briar/api/db/event/MessageRequestedEvent.java b/briar-api/src/net/sf/briar/api/event/MessageRequestedEvent.java
similarity index 76%
rename from briar-api/src/net/sf/briar/api/db/event/MessageRequestedEvent.java
rename to briar-api/src/net/sf/briar/api/event/MessageRequestedEvent.java
index f735994036e86218c02f7e4888356be0a666988f..3aef09fc42d186a74c10135c2be1d8966e9692d6 100644
--- a/briar-api/src/net/sf/briar/api/db/event/MessageRequestedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/MessageRequestedEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.ContactId;
 
 /** An event that is broadcast when a message is requested by a contact. */
-public class MessageRequestedEvent extends DatabaseEvent {
+public class MessageRequestedEvent extends Event {
 
 	private final ContactId contactId;
 
diff --git a/briar-api/src/net/sf/briar/api/db/event/MessageToAckEvent.java b/briar-api/src/net/sf/briar/api/event/MessageToAckEvent.java
similarity index 61%
rename from briar-api/src/net/sf/briar/api/db/event/MessageToAckEvent.java
rename to briar-api/src/net/sf/briar/api/event/MessageToAckEvent.java
index 6050acd881839602f2d0d99f6e11d89634350bb2..b0edcb03e40209ca99a45d1752f6cc31dc922b8d 100644
--- a/briar-api/src/net/sf/briar/api/db/event/MessageToAckEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/MessageToAckEvent.java
@@ -1,12 +1,12 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.ContactId;
 
 /**
- * An event that is broadcast when a message is received or offered from a
+ * An event that is broadcast when a message is received from or offered by a
  * contact and needs to be acknowledged.
  */
-public class MessageToAckEvent extends DatabaseEvent {
+public class MessageToAckEvent extends Event {
 
 	private final ContactId contactId;
 
diff --git a/briar-api/src/net/sf/briar/api/db/event/MessageToRequestEvent.java b/briar-api/src/net/sf/briar/api/event/MessageToRequestEvent.java
similarity index 77%
rename from briar-api/src/net/sf/briar/api/db/event/MessageToRequestEvent.java
rename to briar-api/src/net/sf/briar/api/event/MessageToRequestEvent.java
index 196ff5b714b8f1082f0d853dbc7fd42ec0b6e3f6..da4cf6e5d1e62e335a7ecfc27cf4a2ab67281c4f 100644
--- a/briar-api/src/net/sf/briar/api/db/event/MessageToRequestEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/MessageToRequestEvent.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.ContactId;
 
@@ -6,7 +6,7 @@ import net.sf.briar.api.ContactId;
  * An event that is broadcast when a message is offered by a contact and needs
  * to be requested.
  */
-public class MessageToRequestEvent extends DatabaseEvent {
+public class MessageToRequestEvent extends Event {
 
 	private final ContactId contactId;
 
diff --git a/briar-api/src/net/sf/briar/api/db/event/RemoteRetentionTimeUpdatedEvent.java b/briar-api/src/net/sf/briar/api/event/RemoteRetentionTimeUpdatedEvent.java
similarity index 76%
rename from briar-api/src/net/sf/briar/api/db/event/RemoteRetentionTimeUpdatedEvent.java
rename to briar-api/src/net/sf/briar/api/event/RemoteRetentionTimeUpdatedEvent.java
index 35beb8e4d128ba9e081b67c2d20e5ac73d1af2fd..3fd53d51ea8d7e1bfd6419e7b011e41050ee2a50 100644
--- a/briar-api/src/net/sf/briar/api/db/event/RemoteRetentionTimeUpdatedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/RemoteRetentionTimeUpdatedEvent.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.ContactId;
 
@@ -6,7 +6,7 @@ import net.sf.briar.api.ContactId;
  * An event that is broadcast when the retention time of a contact's database
  * changes.
  */
-public class RemoteRetentionTimeUpdatedEvent extends DatabaseEvent {
+public class RemoteRetentionTimeUpdatedEvent extends Event {
 
 	private final ContactId contactId;
 
diff --git a/briar-api/src/net/sf/briar/api/db/event/RemoteSubscriptionsUpdatedEvent.java b/briar-api/src/net/sf/briar/api/event/RemoteSubscriptionsUpdatedEvent.java
similarity index 74%
rename from briar-api/src/net/sf/briar/api/db/event/RemoteSubscriptionsUpdatedEvent.java
rename to briar-api/src/net/sf/briar/api/event/RemoteSubscriptionsUpdatedEvent.java
index 54436e52e4d0e15a3daf07b4a78bea4bf6c15a1a..945fd2829dee91f51a2ae1351d9ce57c4739ed94 100644
--- a/briar-api/src/net/sf/briar/api/db/event/RemoteSubscriptionsUpdatedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/RemoteSubscriptionsUpdatedEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.ContactId;
 
 /**  An event that is broadcast when a contact's subscriptions are updated. */
-public class RemoteSubscriptionsUpdatedEvent extends DatabaseEvent {
+public class RemoteSubscriptionsUpdatedEvent extends Event {
 
 	private final ContactId contactId;
 
diff --git a/briar-api/src/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java b/briar-api/src/net/sf/briar/api/event/RemoteTransportsUpdatedEvent.java
similarity index 84%
rename from briar-api/src/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java
rename to briar-api/src/net/sf/briar/api/event/RemoteTransportsUpdatedEvent.java
index b2f66008a95a9f6b79a83d9531299b6f0107487f..843686e1f3103e007a11dde9768ab44743016eb6 100644
--- a/briar-api/src/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/RemoteTransportsUpdatedEvent.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
@@ -7,7 +7,7 @@ import net.sf.briar.api.TransportId;
  * An event that is broadcast when a contact's remote transport properties
  * are updated.
  */
-public class RemoteTransportsUpdatedEvent extends DatabaseEvent {
+public class RemoteTransportsUpdatedEvent extends Event {
 
 	private final ContactId contactId;
 	private final TransportId transportId;
diff --git a/briar-api/src/net/sf/briar/api/db/event/SubscriptionAddedEvent.java b/briar-api/src/net/sf/briar/api/event/SubscriptionAddedEvent.java
similarity index 73%
rename from briar-api/src/net/sf/briar/api/db/event/SubscriptionAddedEvent.java
rename to briar-api/src/net/sf/briar/api/event/SubscriptionAddedEvent.java
index 1955d8f9dfc4f9889fc1e1de2301c5240f85efb8..0ca97fdf7187e1b88cbe0ab955b00f99a3f2ff38 100644
--- a/briar-api/src/net/sf/briar/api/db/event/SubscriptionAddedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/SubscriptionAddedEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.messaging.Group;
 
 /** An event that is broadcast when the user subscribes to a group. */
-public class SubscriptionAddedEvent extends DatabaseEvent {
+public class SubscriptionAddedEvent extends Event {
 
 	private final Group group;
 
diff --git a/briar-api/src/net/sf/briar/api/db/event/SubscriptionRemovedEvent.java b/briar-api/src/net/sf/briar/api/event/SubscriptionRemovedEvent.java
similarity index 73%
rename from briar-api/src/net/sf/briar/api/db/event/SubscriptionRemovedEvent.java
rename to briar-api/src/net/sf/briar/api/event/SubscriptionRemovedEvent.java
index 2957304efcab710e6085e4a4fdb0104bea936f77..a1afb7b3d78a70334331d6b48eddf2c574414bac 100644
--- a/briar-api/src/net/sf/briar/api/db/event/SubscriptionRemovedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/SubscriptionRemovedEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.messaging.Group;
 
 /** An event that is broadcast when the user unsubscribes from a group. */
-public class SubscriptionRemovedEvent extends DatabaseEvent {
+public class SubscriptionRemovedEvent extends Event {
 
 	private final Group group;
 
diff --git a/briar-api/src/net/sf/briar/api/db/event/TransportAddedEvent.java b/briar-api/src/net/sf/briar/api/event/TransportAddedEvent.java
similarity index 69%
rename from briar-api/src/net/sf/briar/api/db/event/TransportAddedEvent.java
rename to briar-api/src/net/sf/briar/api/event/TransportAddedEvent.java
index f1e7dc62c9825386d0efae7a8bf22a30eeb67b6a..bd4d18d41f0bcba64c343baf34c6dc1ab1462b5e 100644
--- a/briar-api/src/net/sf/briar/api/db/event/TransportAddedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/TransportAddedEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.TransportId;
 
-/** An event that is broadcast when a transport is added to the database. */
-public class TransportAddedEvent extends DatabaseEvent {
+/** An event that is broadcast when a transport is added. */
+public class TransportAddedEvent extends Event {
 
 	private final TransportId transportId;
 	private final long maxLatency;
diff --git a/briar-api/src/net/sf/briar/api/db/event/TransportRemovedEvent.java b/briar-api/src/net/sf/briar/api/event/TransportRemovedEvent.java
similarity index 76%
rename from briar-api/src/net/sf/briar/api/db/event/TransportRemovedEvent.java
rename to briar-api/src/net/sf/briar/api/event/TransportRemovedEvent.java
index b5e4ee0f0e46d9bfc968cacb4fc1c7919cb6e378..4b16b8ccfa403ab203bfffd718d40ab81285e10d 100644
--- a/briar-api/src/net/sf/briar/api/db/event/TransportRemovedEvent.java
+++ b/briar-api/src/net/sf/briar/api/event/TransportRemovedEvent.java
@@ -1,9 +1,9 @@
-package net.sf.briar.api.db.event;
+package net.sf.briar.api.event;
 
 import net.sf.briar.api.TransportId;
 
 /** An event that is broadcast when a transport is removed. */
-public class TransportRemovedEvent extends DatabaseEvent {
+public class TransportRemovedEvent extends Event {
 
 	private final TransportId transportId;
 
diff --git a/briar-api/src/net/sf/briar/api/clock/Clock.java b/briar-api/src/net/sf/briar/api/system/Clock.java
similarity index 90%
rename from briar-api/src/net/sf/briar/api/clock/Clock.java
rename to briar-api/src/net/sf/briar/api/system/Clock.java
index f78c60dc47daff86d522f137af6f4f143d066758..805d017c6621ce6df5a90109002dc307963e79af 100644
--- a/briar-api/src/net/sf/briar/api/clock/Clock.java
+++ b/briar-api/src/net/sf/briar/api/system/Clock.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.clock;
+package net.sf.briar.api.system;
 
 /**
  * An interface for time-related system functions that allows them to be
diff --git a/briar-api/src/net/sf/briar/api/os/FileUtils.java b/briar-api/src/net/sf/briar/api/system/FileUtils.java
similarity index 79%
rename from briar-api/src/net/sf/briar/api/os/FileUtils.java
rename to briar-api/src/net/sf/briar/api/system/FileUtils.java
index d52d4ca6eb4813986da551a3c8949e04a11e22f1..63bd408eeb75827b15b74e60cabc365f3ec23967 100644
--- a/briar-api/src/net/sf/briar/api/os/FileUtils.java
+++ b/briar-api/src/net/sf/briar/api/system/FileUtils.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.os;
+package net.sf.briar.api.system;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/briar-api/src/net/sf/briar/api/clock/SystemClock.java b/briar-api/src/net/sf/briar/api/system/SystemClock.java
similarity index 88%
rename from briar-api/src/net/sf/briar/api/clock/SystemClock.java
rename to briar-api/src/net/sf/briar/api/system/SystemClock.java
index d6fb2cdb070b167ac3153ee4f5d88267bce9267a..94100b342ad83b19b6d7a99a406076f5dd403d3b 100644
--- a/briar-api/src/net/sf/briar/api/clock/SystemClock.java
+++ b/briar-api/src/net/sf/briar/api/system/SystemClock.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.clock;
+package net.sf.briar.api.system;
 
 /** Default clock implementation. */
 public class SystemClock implements Clock {
diff --git a/briar-api/src/net/sf/briar/api/clock/SystemTimer.java b/briar-api/src/net/sf/briar/api/system/SystemTimer.java
similarity index 94%
rename from briar-api/src/net/sf/briar/api/clock/SystemTimer.java
rename to briar-api/src/net/sf/briar/api/system/SystemTimer.java
index b220e3026b2b4a5acca196138899e3b7f38344bf..6a068698d00b1ab3c98ce510625a46f872e8b26c 100644
--- a/briar-api/src/net/sf/briar/api/clock/SystemTimer.java
+++ b/briar-api/src/net/sf/briar/api/system/SystemTimer.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.clock;
+package net.sf.briar.api.system;
 
 import java.util.TimerTask;
 
diff --git a/briar-api/src/net/sf/briar/api/clock/Timer.java b/briar-api/src/net/sf/briar/api/system/Timer.java
similarity index 95%
rename from briar-api/src/net/sf/briar/api/clock/Timer.java
rename to briar-api/src/net/sf/briar/api/system/Timer.java
index 9e5cae3213e26c904b583916e4e7bc5a6e899ac7..d5ad5620b72af61b249b90668b5e9a556d39ed53 100644
--- a/briar-api/src/net/sf/briar/api/clock/Timer.java
+++ b/briar-api/src/net/sf/briar/api/system/Timer.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.clock;
+package net.sf.briar.api.system;
 
 import java.util.TimerTask;
 
diff --git a/briar-core/src/net/sf/briar/db/DatabaseCleanerImpl.java b/briar-core/src/net/sf/briar/db/DatabaseCleanerImpl.java
index 10a1a554bda74602596aaf57bcbf2289cde3a2aa..573adc6144355f82923d6fef9422f2c916b2265b 100644
--- a/briar-core/src/net/sf/briar/db/DatabaseCleanerImpl.java
+++ b/briar-core/src/net/sf/briar/db/DatabaseCleanerImpl.java
@@ -8,9 +8,9 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
-import net.sf.briar.api.clock.Timer;
 import net.sf.briar.api.db.DbClosedException;
 import net.sf.briar.api.db.DbException;
+import net.sf.briar.api.system.Timer;
 
 class DatabaseCleanerImpl extends TimerTask implements DatabaseCleaner {
 
diff --git a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
index 79f39e86c900f23765a4e5300f8ecbcdbb4d7f14..b5c5c623b13dc0f5eeeb86fa5c92540acfd55861 100644
--- a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
@@ -30,7 +30,6 @@ import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.db.ContactExistsException;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
@@ -41,26 +40,26 @@ import net.sf.briar.api.db.NoSuchLocalAuthorException;
 import net.sf.briar.api.db.NoSuchMessageException;
 import net.sf.briar.api.db.NoSuchSubscriptionException;
 import net.sf.briar.api.db.NoSuchTransportException;
-import net.sf.briar.api.db.event.ContactAddedEvent;
-import net.sf.briar.api.db.event.ContactRemovedEvent;
-import net.sf.briar.api.db.event.DatabaseEvent;
-import net.sf.briar.api.db.event.DatabaseListener;
-import net.sf.briar.api.db.event.LocalAuthorAddedEvent;
-import net.sf.briar.api.db.event.LocalAuthorRemovedEvent;
-import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent;
-import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent;
-import net.sf.briar.api.db.event.MessageAddedEvent;
-import net.sf.briar.api.db.event.MessageExpiredEvent;
-import net.sf.briar.api.db.event.MessageRequestedEvent;
-import net.sf.briar.api.db.event.MessageToAckEvent;
-import net.sf.briar.api.db.event.MessageToRequestEvent;
-import net.sf.briar.api.db.event.RemoteRetentionTimeUpdatedEvent;
-import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
-import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
-import net.sf.briar.api.db.event.SubscriptionAddedEvent;
-import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
-import net.sf.briar.api.db.event.TransportAddedEvent;
-import net.sf.briar.api.db.event.TransportRemovedEvent;
+import net.sf.briar.api.event.ContactAddedEvent;
+import net.sf.briar.api.event.ContactRemovedEvent;
+import net.sf.briar.api.event.Event;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.event.LocalAuthorAddedEvent;
+import net.sf.briar.api.event.LocalAuthorRemovedEvent;
+import net.sf.briar.api.event.LocalSubscriptionsUpdatedEvent;
+import net.sf.briar.api.event.LocalTransportsUpdatedEvent;
+import net.sf.briar.api.event.MessageAddedEvent;
+import net.sf.briar.api.event.MessageExpiredEvent;
+import net.sf.briar.api.event.MessageRequestedEvent;
+import net.sf.briar.api.event.MessageToAckEvent;
+import net.sf.briar.api.event.MessageToRequestEvent;
+import net.sf.briar.api.event.RemoteRetentionTimeUpdatedEvent;
+import net.sf.briar.api.event.RemoteSubscriptionsUpdatedEvent;
+import net.sf.briar.api.event.RemoteTransportsUpdatedEvent;
+import net.sf.briar.api.event.SubscriptionAddedEvent;
+import net.sf.briar.api.event.SubscriptionRemovedEvent;
+import net.sf.briar.api.event.TransportAddedEvent;
+import net.sf.briar.api.event.TransportRemovedEvent;
 import net.sf.briar.api.lifecycle.ShutdownManager;
 import net.sf.briar.api.messaging.Ack;
 import net.sf.briar.api.messaging.Group;
@@ -76,6 +75,7 @@ import net.sf.briar.api.messaging.SubscriptionAck;
 import net.sf.briar.api.messaging.SubscriptionUpdate;
 import net.sf.briar.api.messaging.TransportAck;
 import net.sf.briar.api.messaging.TransportUpdate;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.api.transport.Endpoint;
 import net.sf.briar.api.transport.TemporarySecret;
 
@@ -117,8 +117,8 @@ DatabaseCleaner.Callback {
 	private final ShutdownManager shutdown;
 	private final Clock clock;
 
-	private final Collection<DatabaseListener> listeners =
-			new CopyOnWriteArrayList<DatabaseListener>();
+	private final Collection<EventListener> listeners =
+			new CopyOnWriteArrayList<EventListener>();
 
 	private final Object spaceLock = new Object();
 	private long bytesStoredSinceLastCheck = 0; // Locking: spaceLock
@@ -174,11 +174,11 @@ DatabaseCleaner.Callback {
 		}
 	}
 
-	public void addListener(DatabaseListener d) {
+	public void addListener(EventListener d) {
 		listeners.add(d);
 	}
 
-	public void removeListener(DatabaseListener d) {
+	public void removeListener(EventListener d) {
 		listeners.remove(d);
 	}
 
@@ -237,8 +237,8 @@ DatabaseCleaner.Callback {
 	}
 
 	/** Notifies all listeners of a database event. */
-	private void callListeners(DatabaseEvent e) {
-		for(DatabaseListener d : listeners) d.eventOccurred(e);
+	private void callListeners(Event e) {
+		for(EventListener d : listeners) d.eventOccurred(e);
 	}
 
 	public void addEndpoint(Endpoint ep) throws DbException {
diff --git a/briar-core/src/net/sf/briar/db/DatabaseModule.java b/briar-core/src/net/sf/briar/db/DatabaseModule.java
index 165f986e8f29443f40edd5851fc26a73a251e785..921845cf54055c8a400fff49dad0099ad2d85ad5 100644
--- a/briar-core/src/net/sf/briar/db/DatabaseModule.java
+++ b/briar-core/src/net/sf/briar/db/DatabaseModule.java
@@ -12,14 +12,14 @@ import java.util.concurrent.ThreadPoolExecutor;
 
 import javax.inject.Singleton;
 
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DatabaseConfig;
 import net.sf.briar.api.db.DatabaseExecutor;
 import net.sf.briar.api.lifecycle.LifecycleManager;
 import net.sf.briar.api.lifecycle.ShutdownManager;
-import net.sf.briar.api.os.FileUtils;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.FileUtils;
+import net.sf.briar.api.system.SystemClock;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
diff --git a/briar-core/src/net/sf/briar/db/H2Database.java b/briar-core/src/net/sf/briar/db/H2Database.java
index 2255e8e74e75f7857a091c98c962e8dfc6cd832b..0f6b67f9737fdb9ca05e19fc35a4f68ac1d61443 100644
--- a/briar-core/src/net/sf/briar/db/H2Database.java
+++ b/briar-core/src/net/sf/briar/db/H2Database.java
@@ -10,10 +10,10 @@ import java.util.Properties;
 
 import javax.inject.Inject;
 
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.db.DatabaseConfig;
 import net.sf.briar.api.db.DbException;
-import net.sf.briar.api.os.FileUtils;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.FileUtils;
 import net.sf.briar.util.StringUtils;
 
 /** Contains all the H2-specific code for the database. */
diff --git a/briar-core/src/net/sf/briar/db/JdbcDatabase.java b/briar-core/src/net/sf/briar/db/JdbcDatabase.java
index a896416c9895ccdd9b5a84d11f5bf046d30e5438..5e7e008eec2ee544fa5a9f79250bab77739cb83b 100644
--- a/briar-core/src/net/sf/briar/db/JdbcDatabase.java
+++ b/briar-core/src/net/sf/briar/db/JdbcDatabase.java
@@ -32,7 +32,6 @@ import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.db.DbClosedException;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.MessageHeader;
@@ -47,6 +46,7 @@ import net.sf.briar.api.messaging.SubscriptionAck;
 import net.sf.briar.api.messaging.SubscriptionUpdate;
 import net.sf.briar.api.messaging.TransportAck;
 import net.sf.briar.api.messaging.TransportUpdate;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.api.transport.Endpoint;
 import net.sf.briar.api.transport.TemporarySecret;
 
diff --git a/briar-core/src/net/sf/briar/invitation/AliceConnector.java b/briar-core/src/net/sf/briar/invitation/AliceConnector.java
index 3fe1c54917163b39f2f5f4861a90691787dadd2a..d33787b88b3d38566a5035714ac368510e5dc857 100644
--- a/briar-core/src/net/sf/briar/invitation/AliceConnector.java
+++ b/briar-core/src/net/sf/briar/invitation/AliceConnector.java
@@ -15,7 +15,6 @@ import net.sf.briar.api.AuthorFactory;
 import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyManager;
 import net.sf.briar.api.crypto.PseudoRandom;
@@ -28,6 +27,7 @@ import net.sf.briar.api.serial.Reader;
 import net.sf.briar.api.serial.ReaderFactory;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.api.transport.ConnectionDispatcher;
 import net.sf.briar.api.transport.ConnectionReader;
 import net.sf.briar.api.transport.ConnectionReaderFactory;
diff --git a/briar-core/src/net/sf/briar/invitation/BobConnector.java b/briar-core/src/net/sf/briar/invitation/BobConnector.java
index 4d05f92f7c0ca5e46d71c41a1b2c2ec6e46af188..ebd8fbfddd371dd810455f80ad21511488913e6f 100644
--- a/briar-core/src/net/sf/briar/invitation/BobConnector.java
+++ b/briar-core/src/net/sf/briar/invitation/BobConnector.java
@@ -15,7 +15,6 @@ import net.sf.briar.api.AuthorFactory;
 import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyManager;
 import net.sf.briar.api.crypto.PseudoRandom;
@@ -28,6 +27,7 @@ import net.sf.briar.api.serial.Reader;
 import net.sf.briar.api.serial.ReaderFactory;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.api.transport.ConnectionDispatcher;
 import net.sf.briar.api.transport.ConnectionReader;
 import net.sf.briar.api.transport.ConnectionReaderFactory;
diff --git a/briar-core/src/net/sf/briar/invitation/Connector.java b/briar-core/src/net/sf/briar/invitation/Connector.java
index a1c7fe27fd47f7b1dc3ade0223b2305be6d236b6..a2486ba14629d9da18b1f6cb879ef4d957aace3a 100644
--- a/briar-core/src/net/sf/briar/invitation/Connector.java
+++ b/briar-core/src/net/sf/briar/invitation/Connector.java
@@ -30,7 +30,6 @@ import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.UniqueId;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyManager;
 import net.sf.briar.api.crypto.KeyPair;
@@ -50,6 +49,7 @@ import net.sf.briar.api.serial.Reader;
 import net.sf.briar.api.serial.ReaderFactory;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.api.transport.ConnectionDispatcher;
 import net.sf.briar.api.transport.ConnectionReaderFactory;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
diff --git a/briar-core/src/net/sf/briar/invitation/ConnectorGroup.java b/briar-core/src/net/sf/briar/invitation/ConnectorGroup.java
index c1965975243638542482a1e91870f29cc69e15b0..0dc6a39bc4a00af52dad2d89aa24db9d16456e39 100644
--- a/briar-core/src/net/sf/briar/invitation/ConnectorGroup.java
+++ b/briar-core/src/net/sf/briar/invitation/ConnectorGroup.java
@@ -18,7 +18,6 @@ import net.sf.briar.api.AuthorId;
 import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyManager;
 import net.sf.briar.api.crypto.PseudoRandom;
@@ -32,6 +31,7 @@ import net.sf.briar.api.plugins.PluginManager;
 import net.sf.briar.api.plugins.duplex.DuplexPlugin;
 import net.sf.briar.api.serial.ReaderFactory;
 import net.sf.briar.api.serial.WriterFactory;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.api.transport.ConnectionDispatcher;
 import net.sf.briar.api.transport.ConnectionReaderFactory;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
diff --git a/briar-core/src/net/sf/briar/invitation/InvitationTaskFactoryImpl.java b/briar-core/src/net/sf/briar/invitation/InvitationTaskFactoryImpl.java
index b798adc75e9736d3e9c2032e509a7feaaca9766f..8adad86d8d07f2e1332e65f52c4df592ebb3d7ae 100644
--- a/briar-core/src/net/sf/briar/invitation/InvitationTaskFactoryImpl.java
+++ b/briar-core/src/net/sf/briar/invitation/InvitationTaskFactoryImpl.java
@@ -4,7 +4,6 @@ import javax.inject.Inject;
 
 import net.sf.briar.api.AuthorFactory;
 import net.sf.briar.api.AuthorId;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyManager;
 import net.sf.briar.api.db.DatabaseComponent;
@@ -14,6 +13,7 @@ import net.sf.briar.api.messaging.GroupFactory;
 import net.sf.briar.api.plugins.PluginManager;
 import net.sf.briar.api.serial.ReaderFactory;
 import net.sf.briar.api.serial.WriterFactory;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.api.transport.ConnectionDispatcher;
 import net.sf.briar.api.transport.ConnectionReaderFactory;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
diff --git a/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java b/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java
index cf9f755fdb42426257d2240a67dbe3e9127b8385..e68995aa7eb6f70547cfbaf12611550436ccb06e 100644
--- a/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java
+++ b/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java
@@ -7,7 +7,6 @@ import java.security.GeneralSecurityException;
 import javax.inject.Inject;
 
 import net.sf.briar.api.Author;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyParser;
 import net.sf.briar.api.crypto.MessageDigest;
@@ -17,6 +16,7 @@ import net.sf.briar.api.messaging.Message;
 import net.sf.briar.api.messaging.MessageId;
 import net.sf.briar.api.messaging.MessageVerifier;
 import net.sf.briar.api.messaging.UnverifiedMessage;
+import net.sf.briar.api.system.Clock;
 
 class MessageVerifierImpl implements MessageVerifier {
 
diff --git a/briar-core/src/net/sf/briar/messaging/duplex/DuplexConnection.java b/briar-core/src/net/sf/briar/messaging/duplex/DuplexConnection.java
index be616e9652308075efdec080efef2a23e4b09a8b..2b20048fcaa676297c2c2e690f9287b1c205b3c1 100644
--- a/briar-core/src/net/sf/briar/messaging/duplex/DuplexConnection.java
+++ b/briar-core/src/net/sf/briar/messaging/duplex/DuplexConnection.java
@@ -20,19 +20,19 @@ import net.sf.briar.api.FormatException;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
-import net.sf.briar.api.db.event.ContactRemovedEvent;
-import net.sf.briar.api.db.event.DatabaseEvent;
-import net.sf.briar.api.db.event.DatabaseListener;
-import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent;
-import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent;
-import net.sf.briar.api.db.event.MessageAddedEvent;
-import net.sf.briar.api.db.event.MessageExpiredEvent;
-import net.sf.briar.api.db.event.MessageRequestedEvent;
-import net.sf.briar.api.db.event.MessageToAckEvent;
-import net.sf.briar.api.db.event.MessageToRequestEvent;
-import net.sf.briar.api.db.event.RemoteRetentionTimeUpdatedEvent;
-import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
-import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
+import net.sf.briar.api.event.ContactRemovedEvent;
+import net.sf.briar.api.event.Event;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.event.LocalSubscriptionsUpdatedEvent;
+import net.sf.briar.api.event.LocalTransportsUpdatedEvent;
+import net.sf.briar.api.event.MessageAddedEvent;
+import net.sf.briar.api.event.MessageExpiredEvent;
+import net.sf.briar.api.event.MessageRequestedEvent;
+import net.sf.briar.api.event.MessageToAckEvent;
+import net.sf.briar.api.event.MessageToRequestEvent;
+import net.sf.briar.api.event.RemoteRetentionTimeUpdatedEvent;
+import net.sf.briar.api.event.RemoteSubscriptionsUpdatedEvent;
+import net.sf.briar.api.event.RemoteTransportsUpdatedEvent;
 import net.sf.briar.api.messaging.Ack;
 import net.sf.briar.api.messaging.Message;
 import net.sf.briar.api.messaging.MessageVerifier;
@@ -58,7 +58,7 @@ import net.sf.briar.api.transport.ConnectionWriter;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
 import net.sf.briar.util.ByteUtils;
 
-abstract class DuplexConnection implements DatabaseListener {
+abstract class DuplexConnection implements EventListener {
 
 	private static final Logger LOG =
 			Logger.getLogger(DuplexConnection.class.getName());
@@ -122,7 +122,7 @@ abstract class DuplexConnection implements DatabaseListener {
 	protected abstract ConnectionWriter createConnectionWriter()
 			throws IOException;
 
-	public void eventOccurred(DatabaseEvent e) {
+	public void eventOccurred(Event e) {
 		if(e instanceof ContactRemovedEvent) {
 			ContactRemovedEvent c = (ContactRemovedEvent) e;
 			if(contactId.equals(c.getContactId())) writerTasks.add(CLOSE);
diff --git a/briar-core/src/net/sf/briar/plugins/PollerImpl.java b/briar-core/src/net/sf/briar/plugins/PollerImpl.java
index 8ae80eed4b009ceb77b6fc525682c7b556b33ad3..1a9bc6333873d05b7cea33f906dd8c82ca9a216c 100644
--- a/briar-core/src/net/sf/briar/plugins/PollerImpl.java
+++ b/briar-core/src/net/sf/briar/plugins/PollerImpl.java
@@ -11,9 +11,9 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import net.sf.briar.api.ContactId;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.plugins.Plugin;
 import net.sf.briar.api.plugins.PluginExecutor;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.api.transport.ConnectionRegistry;
 
 class PollerImpl implements Poller, Runnable {
diff --git a/briar-core/src/net/sf/briar/plugins/file/FilePlugin.java b/briar-core/src/net/sf/briar/plugins/file/FilePlugin.java
index e2c7af542020c78329cc8bc50af4a4887ca95ce7..f770b78a14aacacc6c2581267b8d9bfe04879dff 100644
--- a/briar-core/src/net/sf/briar/plugins/file/FilePlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/file/FilePlugin.java
@@ -13,11 +13,11 @@ import java.util.concurrent.Executor;
 import java.util.logging.Logger;
 
 import net.sf.briar.api.ContactId;
-import net.sf.briar.api.os.FileUtils;
 import net.sf.briar.api.plugins.simplex.SimplexPlugin;
 import net.sf.briar.api.plugins.simplex.SimplexPluginCallback;
 import net.sf.briar.api.plugins.simplex.SimplexTransportReader;
 import net.sf.briar.api.plugins.simplex.SimplexTransportWriter;
+import net.sf.briar.api.system.FileUtils;
 
 public abstract class FilePlugin implements SimplexPlugin {
 
diff --git a/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java b/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
index a01a5f4344c89128d658d5b7ad5e49cefa40a238..90f91a122e54a2636a0cc7bf66633d46c6df87a7 100644
--- a/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
@@ -23,10 +23,10 @@ import java.util.logging.Logger;
 
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.PseudoRandom;
 import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
 import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.util.ByteUtils;
 import net.sf.briar.util.LatchedReference;
 import net.sf.briar.util.StringUtils;
diff --git a/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPluginFactory.java b/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPluginFactory.java
index d3339b3b3f3809dea623f94531b053989f81bca1..475e4fbf83f395259342b359ee0b7572597e04e0 100644
--- a/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPluginFactory.java
+++ b/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPluginFactory.java
@@ -3,11 +3,11 @@ package net.sf.briar.plugins.tcp;
 import java.util.concurrent.Executor;
 
 import net.sf.briar.api.TransportId;
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.plugins.duplex.DuplexPlugin;
 import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
 import net.sf.briar.api.plugins.duplex.DuplexPluginFactory;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.SystemClock;
 
 public class LanTcpPluginFactory implements DuplexPluginFactory {
 
diff --git a/briar-core/src/net/sf/briar/reliability/Receiver.java b/briar-core/src/net/sf/briar/reliability/Receiver.java
index 807a0a9f3840179c64316adb3bac1601c4c42f22..7874d4920890ccfae9b1b62b7b3c3e248bf7bbb9 100644
--- a/briar-core/src/net/sf/briar/reliability/Receiver.java
+++ b/briar-core/src/net/sf/briar/reliability/Receiver.java
@@ -6,8 +6,8 @@ import java.util.Iterator;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.reliability.ReadHandler;
+import net.sf.briar.api.system.Clock;
 
 class Receiver implements ReadHandler {
 
diff --git a/briar-core/src/net/sf/briar/reliability/ReliabilityLayerFactoryImpl.java b/briar-core/src/net/sf/briar/reliability/ReliabilityLayerFactoryImpl.java
index 406436dba8a843e794791c5b5b7770d17b48964e..6554536c51154aafec8cface395157eb266a8b7a 100644
--- a/briar-core/src/net/sf/briar/reliability/ReliabilityLayerFactoryImpl.java
+++ b/briar-core/src/net/sf/briar/reliability/ReliabilityLayerFactoryImpl.java
@@ -4,12 +4,12 @@ import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.reliability.ReliabilityExecutor;
 import net.sf.briar.api.reliability.ReliabilityLayer;
 import net.sf.briar.api.reliability.ReliabilityLayerFactory;
 import net.sf.briar.api.reliability.WriteHandler;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.SystemClock;
 
 class ReliabilityLayerFactoryImpl implements ReliabilityLayerFactory {
 
diff --git a/briar-core/src/net/sf/briar/reliability/ReliabilityLayerImpl.java b/briar-core/src/net/sf/briar/reliability/ReliabilityLayerImpl.java
index ed1df1a1a7e596fb964bf636ff63f672582119e1..518a1c85383f31b65d9b8285a5b53da0ceb3916d 100644
--- a/briar-core/src/net/sf/briar/reliability/ReliabilityLayerImpl.java
+++ b/briar-core/src/net/sf/briar/reliability/ReliabilityLayerImpl.java
@@ -11,9 +11,9 @@ import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.logging.Logger;
 
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.reliability.ReliabilityLayer;
 import net.sf.briar.api.reliability.WriteHandler;
+import net.sf.briar.api.system.Clock;
 
 class ReliabilityLayerImpl implements ReliabilityLayer, WriteHandler {
 
diff --git a/briar-core/src/net/sf/briar/reliability/Sender.java b/briar-core/src/net/sf/briar/reliability/Sender.java
index 3208ea0fea443d0e24b03eea9eb4acebfa46f294..7d767f7b161ac006ba98b593a2f283943ed530fb 100644
--- a/briar-core/src/net/sf/briar/reliability/Sender.java
+++ b/briar-core/src/net/sf/briar/reliability/Sender.java
@@ -6,8 +6,8 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.reliability.WriteHandler;
+import net.sf.briar.api.system.Clock;
 
 class Sender {
 
diff --git a/briar-core/src/net/sf/briar/clock/ClockModule.java b/briar-core/src/net/sf/briar/system/ClockModule.java
similarity index 52%
rename from briar-core/src/net/sf/briar/clock/ClockModule.java
rename to briar-core/src/net/sf/briar/system/ClockModule.java
index f3c1d921b692215b9500e769495d4666ca453361..663bfe815f7e2040090735174bc6ed245bbfa876 100644
--- a/briar-core/src/net/sf/briar/clock/ClockModule.java
+++ b/briar-core/src/net/sf/briar/system/ClockModule.java
@@ -1,9 +1,9 @@
-package net.sf.briar.clock;
+package net.sf.briar.system;
 
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
-import net.sf.briar.api.clock.SystemTimer;
-import net.sf.briar.api.clock.Timer;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.SystemClock;
+import net.sf.briar.api.system.SystemTimer;
+import net.sf.briar.api.system.Timer;
 
 import com.google.inject.AbstractModule;
 
diff --git a/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java b/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java
index 109df1e9bf5f3303d9c684f20e62d9b22720b040..f14f7ca906db888dc8e6cf2f5f76511efdc935b0 100644
--- a/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java
+++ b/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java
@@ -18,17 +18,17 @@ import javax.inject.Inject;
 
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.Timer;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyManager;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
-import net.sf.briar.api.db.event.ContactRemovedEvent;
-import net.sf.briar.api.db.event.DatabaseEvent;
-import net.sf.briar.api.db.event.DatabaseListener;
-import net.sf.briar.api.db.event.TransportAddedEvent;
-import net.sf.briar.api.db.event.TransportRemovedEvent;
+import net.sf.briar.api.event.ContactRemovedEvent;
+import net.sf.briar.api.event.Event;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.event.TransportAddedEvent;
+import net.sf.briar.api.event.TransportRemovedEvent;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.Timer;
 import net.sf.briar.api.transport.ConnectionContext;
 import net.sf.briar.api.transport.ConnectionRecogniser;
 import net.sf.briar.api.transport.Endpoint;
@@ -36,7 +36,7 @@ import net.sf.briar.api.transport.TemporarySecret;
 import net.sf.briar.util.ByteUtils;
 
 // FIXME: Don't make alien calls with a lock held
-class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
+class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
 
 	private static final int MS_BETWEEN_CHECKS = 60 * 1000;
 
@@ -324,7 +324,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
 		}
 	}
 
-	public void eventOccurred(DatabaseEvent e) {
+	public void eventOccurred(Event e) {
 		if(e instanceof ContactRemovedEvent) {
 			ContactRemovedEvent c = (ContactRemovedEvent) e;
 			timer.schedule(new ContactRemovedTask(c), 0);
diff --git a/briar-desktop/src/net/sf/briar/plugins/DesktopPluginsModule.java b/briar-desktop/src/net/sf/briar/plugins/DesktopPluginsModule.java
index 76ad61ea5961476976d378386fee8dfcff25349b..cb733c89adcf156f89918110c415b7605f2e9651 100644
--- a/briar-desktop/src/net/sf/briar/plugins/DesktopPluginsModule.java
+++ b/briar-desktop/src/net/sf/briar/plugins/DesktopPluginsModule.java
@@ -6,13 +6,13 @@ import java.util.concurrent.Executor;
 
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.lifecycle.ShutdownManager;
-import net.sf.briar.api.os.FileUtils;
 import net.sf.briar.api.plugins.PluginExecutor;
 import net.sf.briar.api.plugins.duplex.DuplexPluginConfig;
 import net.sf.briar.api.plugins.duplex.DuplexPluginFactory;
 import net.sf.briar.api.plugins.simplex.SimplexPluginConfig;
 import net.sf.briar.api.plugins.simplex.SimplexPluginFactory;
 import net.sf.briar.api.reliability.ReliabilityLayerFactory;
+import net.sf.briar.api.system.FileUtils;
 import net.sf.briar.plugins.bluetooth.BluetoothPluginFactory;
 import net.sf.briar.plugins.file.RemovableDrivePluginFactory;
 import net.sf.briar.plugins.modem.ModemPluginFactory;
diff --git a/briar-desktop/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java b/briar-desktop/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
index f522fea0b2ac40941b93f078349654fc5d1a7fc9..1b61f1d5b6b01f54462f6e3dd033e8adf5486460 100644
--- a/briar-desktop/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
+++ b/briar-desktop/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
@@ -24,11 +24,11 @@ import javax.microedition.io.StreamConnectionNotifier;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.PseudoRandom;
 import net.sf.briar.api.plugins.duplex.DuplexPlugin;
 import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
 import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
+import net.sf.briar.api.system.Clock;
 import net.sf.briar.util.LatchedReference;
 import net.sf.briar.util.OsUtils;
 import net.sf.briar.util.StringUtils;
diff --git a/briar-desktop/src/net/sf/briar/plugins/bluetooth/BluetoothPluginFactory.java b/briar-desktop/src/net/sf/briar/plugins/bluetooth/BluetoothPluginFactory.java
index 88ede376efa1abdbb56e8c08b9e6448aad3428f0..75dbcbdc37a565f169edf5fc3fb158fe4ea360a8 100644
--- a/briar-desktop/src/net/sf/briar/plugins/bluetooth/BluetoothPluginFactory.java
+++ b/briar-desktop/src/net/sf/briar/plugins/bluetooth/BluetoothPluginFactory.java
@@ -4,11 +4,11 @@ import java.security.SecureRandom;
 import java.util.concurrent.Executor;
 
 import net.sf.briar.api.TransportId;
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.plugins.duplex.DuplexPlugin;
 import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
 import net.sf.briar.api.plugins.duplex.DuplexPluginFactory;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.SystemClock;
 
 public class BluetoothPluginFactory implements DuplexPluginFactory {
 
diff --git a/briar-desktop/src/net/sf/briar/plugins/file/RemovableDrivePlugin.java b/briar-desktop/src/net/sf/briar/plugins/file/RemovableDrivePlugin.java
index 05c981c6d8a3f02fb9f7a705159ad7161670fa91..cdb8862b157c00821ab3b4662bd213f851fe4771 100644
--- a/briar-desktop/src/net/sf/briar/plugins/file/RemovableDrivePlugin.java
+++ b/briar-desktop/src/net/sf/briar/plugins/file/RemovableDrivePlugin.java
@@ -13,8 +13,8 @@ import java.util.logging.Logger;
 
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
-import net.sf.briar.api.os.FileUtils;
 import net.sf.briar.api.plugins.simplex.SimplexPluginCallback;
+import net.sf.briar.api.system.FileUtils;
 import net.sf.briar.util.StringUtils;
 
 class RemovableDrivePlugin extends FilePlugin
diff --git a/briar-desktop/src/net/sf/briar/plugins/file/RemovableDrivePluginFactory.java b/briar-desktop/src/net/sf/briar/plugins/file/RemovableDrivePluginFactory.java
index 6af65eda85b9fc786259ce51c72ccab17180213b..fb1ae045ee29a67554c469d333190c6baf894b7c 100644
--- a/briar-desktop/src/net/sf/briar/plugins/file/RemovableDrivePluginFactory.java
+++ b/briar-desktop/src/net/sf/briar/plugins/file/RemovableDrivePluginFactory.java
@@ -5,10 +5,10 @@ import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
 import java.util.concurrent.Executor;
 
 import net.sf.briar.api.TransportId;
-import net.sf.briar.api.os.FileUtils;
 import net.sf.briar.api.plugins.simplex.SimplexPlugin;
 import net.sf.briar.api.plugins.simplex.SimplexPluginCallback;
 import net.sf.briar.api.plugins.simplex.SimplexPluginFactory;
+import net.sf.briar.api.system.FileUtils;
 import net.sf.briar.util.OsUtils;
 
 public class RemovableDrivePluginFactory implements SimplexPluginFactory {
diff --git a/briar-desktop/src/net/sf/briar/plugins/modem/ModemFactoryImpl.java b/briar-desktop/src/net/sf/briar/plugins/modem/ModemFactoryImpl.java
index c6129d6a1f7069d20ee0d373ed02e4d877eec718..858590a337c53391d11e40b719df9c7bc3ec0bb8 100644
--- a/briar-desktop/src/net/sf/briar/plugins/modem/ModemFactoryImpl.java
+++ b/briar-desktop/src/net/sf/briar/plugins/modem/ModemFactoryImpl.java
@@ -2,9 +2,9 @@ package net.sf.briar.plugins.modem;
 
 import java.util.concurrent.Executor;
 
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.reliability.ReliabilityLayerFactory;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.SystemClock;
 
 class ModemFactoryImpl implements ModemFactory {
 
diff --git a/briar-desktop/src/net/sf/briar/plugins/modem/ModemImpl.java b/briar-desktop/src/net/sf/briar/plugins/modem/ModemImpl.java
index 387051051cdae10a957777c08294eb75012c86ef..d7922d1c09b8f3f3571da3aed8101df25710452b 100644
--- a/briar-desktop/src/net/sf/briar/plugins/modem/ModemImpl.java
+++ b/briar-desktop/src/net/sf/briar/plugins/modem/ModemImpl.java
@@ -14,10 +14,10 @@ import java.util.logging.Logger;
 
 import jssc.SerialPortEvent;
 import jssc.SerialPortEventListener;
-import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.reliability.ReliabilityLayer;
 import net.sf.briar.api.reliability.ReliabilityLayerFactory;
 import net.sf.briar.api.reliability.WriteHandler;
+import net.sf.briar.api.system.Clock;
 
 class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 
diff --git a/briar-desktop/src/net/sf/briar/os/DesktopOsModule.java b/briar-desktop/src/net/sf/briar/system/DesktopOsModule.java
similarity index 73%
rename from briar-desktop/src/net/sf/briar/os/DesktopOsModule.java
rename to briar-desktop/src/net/sf/briar/system/DesktopOsModule.java
index b0a8a087b436ecd7e6b83205bda6ee244d56716d..fb1e5cf3a2798b840b65138d5a62c4d7dbe72c84 100644
--- a/briar-desktop/src/net/sf/briar/os/DesktopOsModule.java
+++ b/briar-desktop/src/net/sf/briar/system/DesktopOsModule.java
@@ -1,6 +1,6 @@
-package net.sf.briar.os;
+package net.sf.briar.system;
 
-import net.sf.briar.api.os.FileUtils;
+import net.sf.briar.api.system.FileUtils;
 
 import com.google.inject.AbstractModule;
 
diff --git a/briar-desktop/src/net/sf/briar/os/FileUtilsImpl.java b/briar-desktop/src/net/sf/briar/system/FileUtilsImpl.java
similarity index 72%
rename from briar-desktop/src/net/sf/briar/os/FileUtilsImpl.java
rename to briar-desktop/src/net/sf/briar/system/FileUtilsImpl.java
index a86824272f2145d5451293dbffbe4b576415b248..0773bdbbf1c40f82dc5744556e00891ddbfdb647 100644
--- a/briar-desktop/src/net/sf/briar/os/FileUtilsImpl.java
+++ b/briar-desktop/src/net/sf/briar/system/FileUtilsImpl.java
@@ -1,9 +1,9 @@
-package net.sf.briar.os;
+package net.sf.briar.system;
 
 import java.io.File;
 import java.io.IOException;
 
-import net.sf.briar.api.os.FileUtils;
+import net.sf.briar.api.system.FileUtils;
 
 class FileUtilsImpl implements FileUtils {
 
diff --git a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
index 1a4b1c6590d79122443860e81997d2f0549faa74..d67802a3cc21680c784ee95a46473adf22f7f727 100644
--- a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
+++ b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
@@ -41,7 +41,6 @@ import net.sf.briar.api.transport.ConnectionReader;
 import net.sf.briar.api.transport.ConnectionReaderFactory;
 import net.sf.briar.api.transport.ConnectionWriter;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
-import net.sf.briar.clock.ClockModule;
 import net.sf.briar.crypto.CryptoModule;
 import net.sf.briar.db.DatabaseModule;
 import net.sf.briar.messaging.MessagingModule;
@@ -49,6 +48,7 @@ import net.sf.briar.messaging.duplex.DuplexMessagingModule;
 import net.sf.briar.messaging.simplex.SimplexMessagingModule;
 import net.sf.briar.reliability.ReliabilityModule;
 import net.sf.briar.serial.SerialModule;
+import net.sf.briar.system.ClockModule;
 import net.sf.briar.transport.TransportModule;
 
 import org.junit.Test;
diff --git a/briar-tests/src/net/sf/briar/TestDatabaseModule.java b/briar-tests/src/net/sf/briar/TestDatabaseModule.java
index 8ea2390ce7cb8190afc9d0b7a3e695e8fcd326ff..26ccfb2dda255337619d624622f6a822b2de50ec 100644
--- a/briar-tests/src/net/sf/briar/TestDatabaseModule.java
+++ b/briar-tests/src/net/sf/briar/TestDatabaseModule.java
@@ -3,7 +3,7 @@ package net.sf.briar;
 import java.io.File;
 
 import net.sf.briar.api.db.DatabaseConfig;
-import net.sf.briar.api.os.FileUtils;
+import net.sf.briar.api.system.FileUtils;
 
 import com.google.inject.AbstractModule;
 
diff --git a/briar-tests/src/net/sf/briar/TestFileUtils.java b/briar-tests/src/net/sf/briar/TestFileUtils.java
index ca4828f91482c2fe732a211b62f4f799d0977486..470bd1c0ec35a3badf38f583e52e6bed1dc70e28 100644
--- a/briar-tests/src/net/sf/briar/TestFileUtils.java
+++ b/briar-tests/src/net/sf/briar/TestFileUtils.java
@@ -3,7 +3,7 @@ package net.sf.briar;
 import java.io.File;
 import java.io.IOException;
 
-import net.sf.briar.api.os.FileUtils;
+import net.sf.briar.api.system.FileUtils;
 
 public class TestFileUtils implements FileUtils {
 
diff --git a/briar-tests/src/net/sf/briar/db/DatabaseCleanerImplTest.java b/briar-tests/src/net/sf/briar/db/DatabaseCleanerImplTest.java
index a2c761e016ea02ddbded6ce294ff781d3b72dace..ea1797f46488f42fe6072340f205efd2abac6cdc 100644
--- a/briar-tests/src/net/sf/briar/db/DatabaseCleanerImplTest.java
+++ b/briar-tests/src/net/sf/briar/db/DatabaseCleanerImplTest.java
@@ -5,9 +5,9 @@ import static java.util.concurrent.TimeUnit.SECONDS;
 import java.util.concurrent.CountDownLatch;
 
 import net.sf.briar.BriarTestCase;
-import net.sf.briar.api.clock.SystemTimer;
-import net.sf.briar.api.clock.Timer;
 import net.sf.briar.api.db.DbException;
+import net.sf.briar.api.system.SystemTimer;
+import net.sf.briar.api.system.Timer;
 import net.sf.briar.db.DatabaseCleaner.Callback;
 
 import org.junit.Test;
diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java
index d07bb403ba2b941aef39adcf66c130d111362f3c..b8963654f112a0207a484c026ace816941b80032 100644
--- a/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java
+++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java
@@ -5,10 +5,10 @@ import static net.sf.briar.db.DatabaseConstants.MIN_FREE_SPACE;
 
 import java.util.Collections;
 
-import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.lifecycle.ShutdownManager;
+import net.sf.briar.api.system.SystemClock;
 import net.sf.briar.db.DatabaseCleaner.Callback;
 
 import org.jmock.Expectations;
diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
index d6311265f2bfcb04ce5cf06c2457e28da4bdb3cf..aaef125ab1a9f7fee8374a112c91e6c4caf9844b 100644
--- a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
+++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
@@ -24,18 +24,18 @@ import net.sf.briar.api.db.NoSuchContactException;
 import net.sf.briar.api.db.NoSuchLocalAuthorException;
 import net.sf.briar.api.db.NoSuchSubscriptionException;
 import net.sf.briar.api.db.NoSuchTransportException;
-import net.sf.briar.api.db.event.ContactAddedEvent;
-import net.sf.briar.api.db.event.ContactRemovedEvent;
-import net.sf.briar.api.db.event.DatabaseListener;
-import net.sf.briar.api.db.event.LocalAuthorAddedEvent;
-import net.sf.briar.api.db.event.LocalAuthorRemovedEvent;
-import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent;
-import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent;
-import net.sf.briar.api.db.event.MessageAddedEvent;
-import net.sf.briar.api.db.event.MessageToAckEvent;
-import net.sf.briar.api.db.event.MessageToRequestEvent;
-import net.sf.briar.api.db.event.SubscriptionAddedEvent;
-import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
+import net.sf.briar.api.event.ContactAddedEvent;
+import net.sf.briar.api.event.ContactRemovedEvent;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.event.LocalAuthorAddedEvent;
+import net.sf.briar.api.event.LocalAuthorRemovedEvent;
+import net.sf.briar.api.event.LocalSubscriptionsUpdatedEvent;
+import net.sf.briar.api.event.LocalTransportsUpdatedEvent;
+import net.sf.briar.api.event.MessageAddedEvent;
+import net.sf.briar.api.event.MessageToAckEvent;
+import net.sf.briar.api.event.MessageToRequestEvent;
+import net.sf.briar.api.event.SubscriptionAddedEvent;
+import net.sf.briar.api.event.SubscriptionRemovedEvent;
 import net.sf.briar.api.lifecycle.ShutdownManager;
 import net.sf.briar.api.messaging.Ack;
 import net.sf.briar.api.messaging.Group;
@@ -120,7 +120,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final DatabaseListener listener = context.mock(DatabaseListener.class);
+		final EventListener listener = context.mock(EventListener.class);
 		context.checking(new Expectations() {{
 			exactly(11).of(database).startTransaction();
 			will(returnValue(txn));
@@ -278,7 +278,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final DatabaseListener listener = context.mock(DatabaseListener.class);
+		final EventListener listener = context.mock(EventListener.class);
 		context.checking(new Expectations() {{
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
@@ -990,7 +990,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final DatabaseListener listener = context.mock(DatabaseListener.class);
+		final EventListener listener = context.mock(EventListener.class);
 		context.checking(new Expectations() {{
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
@@ -1031,7 +1031,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final DatabaseListener listener = context.mock(DatabaseListener.class);
+		final EventListener listener = context.mock(EventListener.class);
 		context.checking(new Expectations() {{
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
@@ -1093,7 +1093,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final DatabaseListener listener = context.mock(DatabaseListener.class);
+		final EventListener listener = context.mock(EventListener.class);
 		context.checking(new Expectations() {{
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
@@ -1294,7 +1294,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final DatabaseListener listener = context.mock(DatabaseListener.class);
+		final EventListener listener = context.mock(EventListener.class);
 		context.checking(new Expectations() {{
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
@@ -1326,7 +1326,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final DatabaseListener listener = context.mock(DatabaseListener.class);
+		final EventListener listener = context.mock(EventListener.class);
 		context.checking(new Expectations() {{
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
@@ -1354,7 +1354,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final DatabaseListener listener = context.mock(DatabaseListener.class);
+		final EventListener listener = context.mock(EventListener.class);
 		context.checking(new Expectations() {{
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
@@ -1389,7 +1389,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final DatabaseListener listener = context.mock(DatabaseListener.class);
+		final EventListener listener = context.mock(EventListener.class);
 		context.checking(new Expectations() {{
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
@@ -1421,7 +1421,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final DatabaseListener listener = context.mock(DatabaseListener.class);
+		final EventListener listener = context.mock(EventListener.class);
 		context.checking(new Expectations() {{
 			// setVisibility()
 			oneOf(database).startTransaction();
diff --git a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
index 29d3381e0796b94126f0a627ac37aa1959a80b9f..fd139179572f354ae5d0c3dfca935c7566521878 100644
--- a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
+++ b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
@@ -31,7 +31,6 @@ import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.MessageHeader;
 import net.sf.briar.api.messaging.Group;
@@ -39,6 +38,7 @@ import net.sf.briar.api.messaging.GroupId;
 import net.sf.briar.api.messaging.GroupStatus;
 import net.sf.briar.api.messaging.Message;
 import net.sf.briar.api.messaging.MessageId;
+import net.sf.briar.api.system.SystemClock;
 import net.sf.briar.api.transport.Endpoint;
 import net.sf.briar.api.transport.TemporarySecret;
 
diff --git a/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java b/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java
index 9968be2855742bb7ef0e7809acf9ed3b38c65259..9834c6f4b732cac83862412b89f8534c046b0684 100644
--- a/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java
+++ b/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java
@@ -40,12 +40,12 @@ import net.sf.briar.api.messaging.PacketWriter;
 import net.sf.briar.api.messaging.PacketWriterFactory;
 import net.sf.briar.api.messaging.SubscriptionUpdate;
 import net.sf.briar.api.messaging.TransportUpdate;
-import net.sf.briar.clock.ClockModule;
 import net.sf.briar.crypto.CryptoModule;
 import net.sf.briar.db.DatabaseModule;
 import net.sf.briar.messaging.duplex.DuplexMessagingModule;
 import net.sf.briar.messaging.simplex.SimplexMessagingModule;
 import net.sf.briar.serial.SerialModule;
+import net.sf.briar.system.ClockModule;
 import net.sf.briar.transport.TransportModule;
 
 import org.junit.Test;
diff --git a/briar-tests/src/net/sf/briar/messaging/simplex/OutgoingSimplexConnectionTest.java b/briar-tests/src/net/sf/briar/messaging/simplex/OutgoingSimplexConnectionTest.java
index 3021e53923c7fcdcebfa1d02783a21e98a085c5c..3138549bd4282de25d31dac203a64fcb7ffbe259 100644
--- a/briar-tests/src/net/sf/briar/messaging/simplex/OutgoingSimplexConnectionTest.java
+++ b/briar-tests/src/net/sf/briar/messaging/simplex/OutgoingSimplexConnectionTest.java
@@ -26,11 +26,11 @@ import net.sf.briar.api.messaging.PacketWriterFactory;
 import net.sf.briar.api.transport.ConnectionContext;
 import net.sf.briar.api.transport.ConnectionRegistry;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
-import net.sf.briar.clock.ClockModule;
 import net.sf.briar.crypto.CryptoModule;
 import net.sf.briar.messaging.MessagingModule;
 import net.sf.briar.messaging.duplex.DuplexMessagingModule;
 import net.sf.briar.serial.SerialModule;
+import net.sf.briar.system.ClockModule;
 import net.sf.briar.transport.TransportModule;
 
 import org.jmock.Expectations;
diff --git a/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java b/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java
index 3ddb98eaf19ad60334ff7aaf5a1371f19f630207..197676df365afb057d0823c76d176075025e2c40 100644
--- a/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java
+++ b/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java
@@ -20,9 +20,9 @@ import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.crypto.KeyManager;
 import net.sf.briar.api.db.DatabaseComponent;
-import net.sf.briar.api.db.event.DatabaseEvent;
-import net.sf.briar.api.db.event.DatabaseListener;
-import net.sf.briar.api.db.event.MessageAddedEvent;
+import net.sf.briar.api.event.Event;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.event.MessageAddedEvent;
 import net.sf.briar.api.messaging.Group;
 import net.sf.briar.api.messaging.GroupId;
 import net.sf.briar.api.messaging.Message;
@@ -36,13 +36,13 @@ import net.sf.briar.api.transport.ConnectionRecogniser;
 import net.sf.briar.api.transport.ConnectionRegistry;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
 import net.sf.briar.api.transport.Endpoint;
-import net.sf.briar.clock.ClockModule;
 import net.sf.briar.crypto.CryptoModule;
 import net.sf.briar.db.DatabaseModule;
 import net.sf.briar.messaging.MessagingModule;
 import net.sf.briar.messaging.duplex.DuplexMessagingModule;
 import net.sf.briar.plugins.ImmediateExecutor;
 import net.sf.briar.serial.SerialModule;
+import net.sf.briar.system.ClockModule;
 import net.sf.briar.transport.TransportModule;
 
 import org.junit.After;
@@ -234,11 +234,11 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
 		TestUtils.deleteTestDirectory(testDir);
 	}
 
-	private static class MessageListener implements DatabaseListener {
+	private static class MessageListener implements EventListener {
 
 		private boolean messageAdded = false;
 
-		public void eventOccurred(DatabaseEvent e) {
+		public void eventOccurred(Event e) {
 			if(e instanceof MessageAddedEvent) messageAdded = true;
 		}
 	}
diff --git a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java
index 18be16bfd1ae5a3d953d402fa2d6eeefe9463239..a3b8ca62466873b43a109458b3a74933b71f5ddf 100644
--- a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java
@@ -10,7 +10,7 @@ import java.util.concurrent.Executors;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.SystemClock;
+import net.sf.briar.api.system.SystemClock;
 import net.sf.briar.plugins.DuplexClientTest;
 
 // This is not a JUnit test - it has to be run manually while the server test
diff --git a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java
index 08ade2b39e3f75b0cba19c09b6334be7ae70f6e5..456c81b44b3823b7f0814d6b935b43e605c8373b 100644
--- a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java
@@ -8,7 +8,7 @@ import java.util.concurrent.Executors;
 
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.SystemClock;
+import net.sf.briar.api.system.SystemClock;
 import net.sf.briar.plugins.DuplexServerTest;
 
 // This is not a JUnit test - it has to be run manually while the client test
diff --git a/briar-tests/src/net/sf/briar/plugins/file/RemovableDrivePluginTest.java b/briar-tests/src/net/sf/briar/plugins/file/RemovableDrivePluginTest.java
index 527d1d6d510a00d5e617a6b2852d3b0fb2223749..fd287dc143b3558541d0a43c535dca57aa326da2 100644
--- a/briar-tests/src/net/sf/briar/plugins/file/RemovableDrivePluginTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/file/RemovableDrivePluginTest.java
@@ -15,9 +15,9 @@ import net.sf.briar.BriarTestCase;
 import net.sf.briar.TestFileUtils;
 import net.sf.briar.TestUtils;
 import net.sf.briar.api.ContactId;
-import net.sf.briar.api.os.FileUtils;
 import net.sf.briar.api.plugins.simplex.SimplexPluginCallback;
 import net.sf.briar.api.plugins.simplex.SimplexTransportWriter;
+import net.sf.briar.api.system.FileUtils;
 import net.sf.briar.plugins.ImmediateExecutor;
 import net.sf.briar.plugins.file.RemovableDriveMonitor.Callback;
 
diff --git a/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpClientTest.java b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpClientTest.java
index 9472213b13462f2ca9116889c4458e1a15c0547c..a74ea9d6d8faf27e7c8a2bb3b90c19de08d3bd4e 100644
--- a/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpClientTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpClientTest.java
@@ -9,8 +9,8 @@ import java.util.concurrent.Executors;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.SystemClock;
 import net.sf.briar.plugins.DuplexClientTest;
 
 // This is not a JUnit test - it has to be run manually while the server test
diff --git a/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpPluginTest.java b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpPluginTest.java
index a335a908d69da74f1aa705024cfe35f3771bb485..da8ffbc3d61ff9510a0e5fbdd4e1a5704ec1f91f 100644
--- a/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpPluginTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpPluginTest.java
@@ -17,11 +17,11 @@ import net.sf.briar.BriarTestCase;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
 import net.sf.briar.api.plugins.duplex.DuplexPlugin;
 import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
 import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.SystemClock;
 
 import org.junit.Test;
 
diff --git a/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpServerTest.java b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpServerTest.java
index 16d7d73f651879da2ab28fc165b32b62d9ab0402..5fb926cf8b4c6124d9222c8b3927517ebb43a427 100644
--- a/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpServerTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpServerTest.java
@@ -7,8 +7,8 @@ import java.util.concurrent.Executors;
 
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportProperties;
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.SystemClock;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.SystemClock;
 import net.sf.briar.plugins.DuplexServerTest;
 
 // This is not a JUnit test - it has to be run manually while the client test
diff --git a/briar-tests/src/net/sf/briar/transport/KeyManagerImplTest.java b/briar-tests/src/net/sf/briar/transport/KeyManagerImplTest.java
index 32b418f7961c84d6b2f2c1d2f8d7554572eaf36e..6337df582c864c67a44f0a360e13804dc812318e 100644
--- a/briar-tests/src/net/sf/briar/transport/KeyManagerImplTest.java
+++ b/briar-tests/src/net/sf/briar/transport/KeyManagerImplTest.java
@@ -10,11 +10,11 @@ import net.sf.briar.BriarTestCase;
 import net.sf.briar.TestUtils;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.Timer;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.db.DatabaseComponent;
-import net.sf.briar.api.db.event.DatabaseListener;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.Timer;
 import net.sf.briar.api.transport.ConnectionContext;
 import net.sf.briar.api.transport.ConnectionRecogniser;
 import net.sf.briar.api.transport.Endpoint;
@@ -68,7 +68,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Collections.emptyList()));
 			oneOf(db).getTransportLatencies();
@@ -78,7 +78,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 			oneOf(timer).scheduleAtFixedRate(with(keyManager),
 					with(any(long.class)), with(any(long.class)));
 			// stop()
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 			oneOf(connectionRecogniser).removeSecrets();
 		}});
@@ -110,7 +110,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Collections.emptyList()));
 			oneOf(db).getTransportLatencies();
@@ -135,7 +135,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 			oneOf(connectionRecogniser).addSecret(s1);
 			oneOf(connectionRecogniser).addSecret(s2);
 			// stop()
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 			oneOf(connectionRecogniser).removeSecrets();
 		}});
@@ -168,7 +168,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Collections.emptyList()));
 			oneOf(db).getTransportLatencies();
@@ -196,7 +196,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 			oneOf(db).incrementConnectionCounter(contactId, transportId, 1);
 			will(returnValue(0L));
 			// stop()
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 			oneOf(connectionRecogniser).removeSecrets();
 		}});
@@ -237,7 +237,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
@@ -253,7 +253,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 			oneOf(timer).scheduleAtFixedRate(with(keyManager),
 					with(any(long.class)), with(any(long.class)));
 			// stop()
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 			oneOf(connectionRecogniser).removeSecrets();
 		}});
@@ -287,7 +287,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
@@ -311,7 +311,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 			oneOf(timer).scheduleAtFixedRate(with(keyManager),
 					with(any(long.class)), with(any(long.class)));
 			// stop()
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 			oneOf(connectionRecogniser).removeSecrets();
 		}});
@@ -346,7 +346,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
@@ -371,7 +371,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 			oneOf(timer).scheduleAtFixedRate(with(keyManager),
 					with(any(long.class)), with(any(long.class)));
 			// stop()
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 			oneOf(connectionRecogniser).removeSecrets();
 		}});
@@ -403,7 +403,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
@@ -425,7 +425,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 			oneOf(db).incrementConnectionCounter(contactId, transportId, 1);
 			will(returnValue(0L));
 			// stop()
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 			oneOf(connectionRecogniser).removeSecrets();
 		}});
@@ -468,7 +468,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
@@ -499,7 +499,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 			oneOf(db).incrementConnectionCounter(contactId, transportId, 2);
 			will(returnValue(0L));
 			// stop()
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 			oneOf(connectionRecogniser).removeSecrets();
 		}});
@@ -543,7 +543,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
@@ -576,7 +576,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 			oneOf(db).incrementConnectionCounter(contactId, transportId, 3);
 			will(returnValue(0L));
 			// stop()
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 			oneOf(connectionRecogniser).removeSecrets();
 		}});
diff --git a/briar-tests/src/net/sf/briar/transport/KeyRotationIntegrationTest.java b/briar-tests/src/net/sf/briar/transport/KeyRotationIntegrationTest.java
index 70d664955f4859867008f42c44ec0a37d723b301..ce665878b70bc528be9d590690be86c3f516c8a8 100644
--- a/briar-tests/src/net/sf/briar/transport/KeyRotationIntegrationTest.java
+++ b/briar-tests/src/net/sf/briar/transport/KeyRotationIntegrationTest.java
@@ -11,12 +11,12 @@ import net.sf.briar.BriarTestCase;
 import net.sf.briar.TestUtils;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
-import net.sf.briar.api.clock.Clock;
-import net.sf.briar.api.clock.Timer;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.SecretKey;
 import net.sf.briar.api.db.DatabaseComponent;
-import net.sf.briar.api.db.event.DatabaseListener;
+import net.sf.briar.api.event.EventListener;
+import net.sf.briar.api.system.Clock;
+import net.sf.briar.api.system.Timer;
 import net.sf.briar.api.transport.ConnectionContext;
 import net.sf.briar.api.transport.ConnectionRecogniser;
 import net.sf.briar.api.transport.Endpoint;
@@ -85,7 +85,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Collections.emptyList()));
 			oneOf(db).getTransportLatencies();
@@ -95,7 +95,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 			oneOf(timer).scheduleAtFixedRate(with(keyManager),
 					with(any(long.class)), with(any(long.class)));
 			// stop()
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 		}});
 
@@ -129,7 +129,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Collections.emptyList()));
 			oneOf(db).getTransportLatencies();
@@ -217,7 +217,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 			}
 			oneOf(k2).erase();
 			// Remove the listener and stop the timer
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 		}});
 
@@ -252,7 +252,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Collections.emptyList()));
 			oneOf(db).getTransportLatencies();
@@ -343,7 +343,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 			}
 			oneOf(k2).erase();
 			// Remove the listener and stop the timer
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 		}});
 
@@ -386,7 +386,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Collections.emptyList()));
 			oneOf(db).getTransportLatencies();
@@ -485,7 +485,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 			}
 			oneOf(k2).erase();
 			// Remove the listener and stop the timer
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 		}});
 
@@ -531,7 +531,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
@@ -611,7 +611,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 			}
 			oneOf(k2).erase();
 			// Remove the listener and stop the timer
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 		}});
 
@@ -647,7 +647,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
@@ -735,7 +735,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 			}
 			oneOf(k3).erase();
 			// Remove the listener and stop the timer
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 		}});
 
@@ -772,7 +772,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 
 		context.checking(new Expectations() {{
 			// start()
-			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).addListener(with(any(EventListener.class)));
 			oneOf(db).getSecrets();
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
@@ -861,7 +861,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 			}
 			oneOf(k4).erase();
 			// Remove the listener and stop the timer
-			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(db).removeListener(with(any(EventListener.class)));
 			oneOf(timer).cancel();
 		}});