diff --git a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java
index 7b6d598a4e4133809084a6b504012a96052a7d84..3d530f4680da31deacb6b13a514dfc811a26d496 100644
--- a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java
+++ b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java
@@ -8,6 +8,7 @@ import com.google.inject.Provides;
 import org.briarproject.api.android.AndroidExecutor;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.lifecycle.IoExecutor;
+import org.briarproject.api.plugins.BackoffFactory;
 import org.briarproject.api.plugins.duplex.DuplexPluginConfig;
 import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
 import org.briarproject.api.plugins.simplex.SimplexPluginConfig;
@@ -37,11 +38,11 @@ public class AndroidPluginsModule extends PluginsModule {
 	@Provides
 	DuplexPluginConfig getDuplexPluginConfig(@IoExecutor Executor ioExecutor,
 			AndroidExecutor androidExecutor, Application app,
-			SecureRandom random, LocationUtils locationUtils,
-			EventBus eventBus) {
+			SecureRandom random, BackoffFactory backoffFactory,
+			LocationUtils locationUtils, EventBus eventBus) {
 		Context appContext = app.getApplicationContext();
 		DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
-				androidExecutor, appContext, random);
+				androidExecutor, appContext, random, backoffFactory);
 		DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
 				locationUtils, eventBus);
 		DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java
index 646e498d90cdf25511a249e2e8e2d3195356cc39..56d4c2ec565ff1cfda2eb36fd97045bb7d00fd8a 100644
--- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java
+++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java
@@ -13,6 +13,7 @@ import org.briarproject.api.TransportId;
 import org.briarproject.api.android.AndroidExecutor;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.crypto.PseudoRandom;
+import org.briarproject.api.plugins.Backoff;
 import org.briarproject.api.plugins.duplex.DuplexPlugin;
 import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
 import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
@@ -68,8 +69,9 @@ class DroidtoothPlugin implements DuplexPlugin {
 	private final Context appContext;
 	private final SecureRandom secureRandom;
 	private final Clock clock;
+	private final Backoff backoff;
 	private final DuplexPluginCallback callback;
-	private final int maxLatency, pollingInterval;
+	private final int maxLatency;
 
 	private volatile boolean running = false;
 	private volatile boolean wasEnabledByUs = false;
@@ -81,16 +83,15 @@ class DroidtoothPlugin implements DuplexPlugin {
 
 	DroidtoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor,
 			Context appContext, SecureRandom secureRandom, Clock clock,
-			DuplexPluginCallback callback, int maxLatency,
-			int pollingInterval) {
+			Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
 		this.ioExecutor = ioExecutor;
 		this.androidExecutor = androidExecutor;
 		this.appContext = appContext;
 		this.secureRandom = secureRandom;
 		this.clock = clock;
+		this.backoff = backoff;
 		this.callback = callback;
 		this.maxLatency = maxLatency;
-		this.pollingInterval = pollingInterval;
 	}
 
 	public TransportId getId() {
@@ -173,6 +174,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 				}
 				LOG.info("Socket bound");
 				socket = ss;
+				backoff.reset();
 				callback.transportEnabled();
 				acceptContactConnections();
 			}
@@ -216,6 +218,7 @@ class DroidtoothPlugin implements DuplexPlugin {
 				String address = s.getRemoteDevice().getAddress();
 				LOG.info("Connection from " + address);
 			}
+			backoff.reset();
 			callback.incomingConnectionCreated(wrapSocket(s));
 		}
 	}
@@ -244,11 +247,12 @@ class DroidtoothPlugin implements DuplexPlugin {
 	}
 
 	public int getPollingInterval() {
-		return pollingInterval;
+		return backoff.getPollingInterval();
 	}
 
 	public void poll(Collection<ContactId> connected) {
 		if (!isRunning()) return;
+		backoff.increment();
 		// Try to connect to known devices in parallel
 		Map<ContactId, TransportProperties> remote =
 				callback.getRemoteProperties();
@@ -263,8 +267,10 @@ class DroidtoothPlugin implements DuplexPlugin {
 				public void run() {
 					if (!running) return;
 					BluetoothSocket s = connect(address, uuid);
-					if (s != null)
+					if (s != null) {
+						backoff.reset();
 						callback.outgoingConnectionCreated(c, wrapSocket(s));
+					}
 				}
 			});
 		}
diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java
index 2b04481c4b6e1f2d0641a734011bf90634d00201..15e2fc0bacbc3cb0ab5a2e509b97efa5a83b043c 100644
--- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java
+++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java
@@ -1,36 +1,42 @@
 package org.briarproject.plugins.droidtooth;
 
-import java.security.SecureRandom;
-import java.util.concurrent.Executor;
+import android.content.Context;
 
 import org.briarproject.api.TransportId;
 import org.briarproject.api.android.AndroidExecutor;
+import org.briarproject.api.plugins.Backoff;
+import org.briarproject.api.plugins.BackoffFactory;
 import org.briarproject.api.plugins.duplex.DuplexPlugin;
 import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
 import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
 import org.briarproject.api.system.Clock;
 import org.briarproject.system.SystemClock;
 
-import android.content.Context;
+import java.security.SecureRandom;
+import java.util.concurrent.Executor;
 
 public class DroidtoothPluginFactory implements DuplexPluginFactory {
 
 	private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
-	private static final int POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes
+	private static final int MIN_POLLING_INTERVAL = 2 * 60 * 1000; // 2 minutes
+	private static final int MAX_POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour
+	private static final double BACKOFF_BASE = 1.2;
 
 	private final Executor ioExecutor;
 	private final AndroidExecutor androidExecutor;
 	private final Context appContext;
 	private final SecureRandom secureRandom;
+	private final BackoffFactory backoffFactory;
 	private final Clock clock;
 
 	public DroidtoothPluginFactory(Executor ioExecutor,
 			AndroidExecutor androidExecutor, Context appContext,
-			SecureRandom secureRandom) {
+			SecureRandom secureRandom, BackoffFactory backoffFactory) {
 		this.ioExecutor = ioExecutor;
 		this.androidExecutor = androidExecutor;
 		this.appContext = appContext;
 		this.secureRandom = secureRandom;
+		this.backoffFactory = backoffFactory;
 		clock = new SystemClock();
 	}
 
@@ -39,7 +45,9 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
 	}
 
 	public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
+		Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
+				MAX_POLLING_INTERVAL, BACKOFF_BASE);
 		return new DroidtoothPlugin(ioExecutor, androidExecutor, appContext,
-				secureRandom, clock, callback, MAX_LATENCY, POLLING_INTERVAL);
+				secureRandom, clock, backoff, callback, MAX_LATENCY);
 	}
 }
diff --git a/briar-api/src/org/briarproject/api/plugins/Backoff.java b/briar-api/src/org/briarproject/api/plugins/Backoff.java
new file mode 100644
index 0000000000000000000000000000000000000000..927e8a737bec33bd9eda33022d4521d309699e02
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/plugins/Backoff.java
@@ -0,0 +1,22 @@
+package org.briarproject.api.plugins;
+
+/**
+ * Calculates polling intervals for transport plugins that use backoff.
+ */
+public interface Backoff {
+
+	/**
+	 * Returns the current polling interval.
+	 */
+	int getPollingInterval();
+
+	/**
+	 * Increments the backoff counter.
+	 */
+	void increment();
+
+	/**
+	 * Resets the backoff counter.
+	 */
+	void reset();
+}
diff --git a/briar-api/src/org/briarproject/api/plugins/BackoffFactory.java b/briar-api/src/org/briarproject/api/plugins/BackoffFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..4633ba3a5df5cee19979abbfe1be5fb368adf6cf
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/plugins/BackoffFactory.java
@@ -0,0 +1,7 @@
+package org.briarproject.api.plugins;
+
+public interface BackoffFactory {
+
+	Backoff createBackoff(int minInterval, int maxInterval,
+			double base);
+}
diff --git a/briar-core/src/org/briarproject/plugins/BackoffFactoryImpl.java b/briar-core/src/org/briarproject/plugins/BackoffFactoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c3e5eceef3231451ebfa8f6d89ab0d10eb426ba
--- /dev/null
+++ b/briar-core/src/org/briarproject/plugins/BackoffFactoryImpl.java
@@ -0,0 +1,13 @@
+package org.briarproject.plugins;
+
+import org.briarproject.api.plugins.Backoff;
+import org.briarproject.api.plugins.BackoffFactory;
+
+class BackoffFactoryImpl implements BackoffFactory {
+
+	@Override
+	public Backoff createBackoff(int minInterval, int maxInterval,
+			double base) {
+		return new BackoffImpl(minInterval, maxInterval, base);
+	}
+}
diff --git a/briar-core/src/org/briarproject/plugins/BackoffImpl.java b/briar-core/src/org/briarproject/plugins/BackoffImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..175c0d35ffa209ba846b9649fef1c5754385f735
--- /dev/null
+++ b/briar-core/src/org/briarproject/plugins/BackoffImpl.java
@@ -0,0 +1,37 @@
+package org.briarproject.plugins;
+
+import org.briarproject.api.plugins.Backoff;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+class BackoffImpl implements Backoff {
+
+	private final int minInterval, maxInterval;
+	private final double base;
+	private final AtomicInteger backoff;
+
+	BackoffImpl(int minInterval, int maxInterval, double base) {
+		this.minInterval = minInterval;
+		this.maxInterval = maxInterval;
+		this.base = base;
+		backoff = new AtomicInteger(0);
+	}
+
+	@Override
+	public int getPollingInterval() {
+		double multiplier = Math.pow(base, backoff.get());
+		// Large or infinite values will be rounded to Integer.MAX_VALUE
+		int interval = (int) (minInterval * multiplier);
+		return Math.min(interval, maxInterval);
+	}
+
+	@Override
+	public void increment() {
+		backoff.incrementAndGet();
+	}
+
+	@Override
+	public void reset() {
+		backoff.set(0);
+	}
+}
diff --git a/briar-core/src/org/briarproject/plugins/PluginsModule.java b/briar-core/src/org/briarproject/plugins/PluginsModule.java
index cb0c330173a778279935e53953ec199fbb42c1af..d7f8962d787bfcf1a37faeb3eaecd7e608fd6ece 100644
--- a/briar-core/src/org/briarproject/plugins/PluginsModule.java
+++ b/briar-core/src/org/briarproject/plugins/PluginsModule.java
@@ -1,19 +1,21 @@
 package org.briarproject.plugins;
 
-import javax.inject.Singleton;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
 
 import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.plugins.BackoffFactory;
 import org.briarproject.api.plugins.ConnectionManager;
 import org.briarproject.api.plugins.ConnectionRegistry;
 import org.briarproject.api.plugins.PluginManager;
 
-import com.google.inject.AbstractModule;
-import com.google.inject.Provides;
+import javax.inject.Singleton;
 
 public class PluginsModule extends AbstractModule {
 
 	@Override
 	protected void configure() {
+		bind(BackoffFactory.class).to(BackoffFactoryImpl.class);
 		bind(Poller.class).to(PollerImpl.class);
 		bind(ConnectionManager.class).to(ConnectionManagerImpl.class);
 		bind(ConnectionRegistry.class).to(
diff --git a/briar-core/src/org/briarproject/plugins/PollerImpl.java b/briar-core/src/org/briarproject/plugins/PollerImpl.java
index 38f79f7254308dceaac36bb477cb6ca494be2edc..9611499b6b3f74ac1ee521d0b5ac99e99bb37845 100644
--- a/briar-core/src/org/briarproject/plugins/PollerImpl.java
+++ b/briar-core/src/org/briarproject/plugins/PollerImpl.java
@@ -1,6 +1,9 @@
 package org.briarproject.plugins;
 
-import static java.util.logging.Level.INFO;
+import org.briarproject.api.lifecycle.IoExecutor;
+import org.briarproject.api.plugins.ConnectionRegistry;
+import org.briarproject.api.plugins.Plugin;
+import org.briarproject.api.system.Timer;
 
 import java.util.TimerTask;
 import java.util.concurrent.Executor;
@@ -8,10 +11,7 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
-import org.briarproject.api.lifecycle.IoExecutor;
-import org.briarproject.api.plugins.ConnectionRegistry;
-import org.briarproject.api.plugins.Plugin;
-import org.briarproject.api.system.Timer;
+import static java.util.logging.Level.INFO;
 
 class PollerImpl implements Poller {
 
diff --git a/briar-desktop/src/org/briarproject/plugins/DesktopPluginsModule.java b/briar-desktop/src/org/briarproject/plugins/DesktopPluginsModule.java
index 0b05dc0120adc42580da2e4d22f5072bfe97f5e9..aa56c79b65123c3835a0bb95b8b28ec6ecf1378b 100644
--- a/briar-desktop/src/org/briarproject/plugins/DesktopPluginsModule.java
+++ b/briar-desktop/src/org/briarproject/plugins/DesktopPluginsModule.java
@@ -4,6 +4,7 @@ import com.google.inject.Provides;
 
 import org.briarproject.api.lifecycle.IoExecutor;
 import org.briarproject.api.lifecycle.ShutdownManager;
+import org.briarproject.api.plugins.BackoffFactory;
 import org.briarproject.api.plugins.duplex.DuplexPluginConfig;
 import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
 import org.briarproject.api.plugins.simplex.SimplexPluginConfig;
@@ -39,10 +40,11 @@ public class DesktopPluginsModule extends PluginsModule {
 
 	@Provides
 	DuplexPluginConfig getDuplexPluginConfig(@IoExecutor Executor ioExecutor,
-			SecureRandom random, ReliabilityLayerFactory reliabilityFactory,
+			SecureRandom random, BackoffFactory backoffFactory,
+			ReliabilityLayerFactory reliabilityFactory,
 			ShutdownManager shutdownManager) {
 		DuplexPluginFactory bluetooth = new BluetoothPluginFactory(ioExecutor,
-				random);
+				random, backoffFactory);
 		DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
 				reliabilityFactory);
 		DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor);
diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java
index 4eeccc5677b8a53de2c45c9dd9b6e945398641cc..786dcf22280a16ad044fb8ab366f13a88bb56852 100644
--- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java
+++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java
@@ -3,6 +3,7 @@ package org.briarproject.plugins.bluetooth;
 import org.briarproject.api.TransportId;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.crypto.PseudoRandom;
+import org.briarproject.api.plugins.Backoff;
 import org.briarproject.api.plugins.duplex.DuplexPlugin;
 import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
 import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
@@ -43,25 +44,25 @@ class BluetoothPlugin implements DuplexPlugin {
 	private static final int UUID_BYTES = 16;
 
 	private final Executor ioExecutor;
-	private final Clock clock;
 	private final SecureRandom secureRandom;
+	private final Clock clock;
+	private final Backoff backoff;
 	private final DuplexPluginCallback callback;
-	private final int maxLatency, pollingInterval;
+	private final int maxLatency;
 	private final Semaphore discoverySemaphore = new Semaphore(1);
 
 	private volatile boolean running = false;
 	private volatile StreamConnectionNotifier socket = null;
 	private volatile LocalDevice localDevice = null;
 
-	BluetoothPlugin(Executor ioExecutor, Clock clock, SecureRandom secureRandom,
-			DuplexPluginCallback callback, int maxLatency,
-			int pollingInterval) {
+	BluetoothPlugin(Executor ioExecutor, SecureRandom secureRandom, Clock clock,
+			Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
 		this.ioExecutor = ioExecutor;
-		this.clock = clock;
 		this.secureRandom = secureRandom;
+		this.clock = clock;
+		this.backoff = backoff;
 		this.callback = callback;
 		this.maxLatency = maxLatency;
-		this.pollingInterval = pollingInterval;
 	}
 
 	public TransportId getId() {
@@ -117,6 +118,7 @@ class BluetoothPlugin implements DuplexPlugin {
 					return;
 				}
 				socket = ss;
+				backoff.reset();
 				callback.transportEnabled();
 				acceptContactConnections(ss);
 			}
@@ -160,6 +162,7 @@ class BluetoothPlugin implements DuplexPlugin {
 				if (LOG.isLoggable(INFO)) LOG.info(e.toString());
 				return;
 			}
+			backoff.reset();
 			callback.incomingConnectionCreated(wrapSocket(s));
 			if (!running) return;
 		}
@@ -183,11 +186,12 @@ class BluetoothPlugin implements DuplexPlugin {
 	}
 
 	public int getPollingInterval() {
-		return pollingInterval;
+		return backoff.getPollingInterval();
 	}
 
 	public void poll(final Collection<ContactId> connected) {
 		if (!running) return;
+		backoff.increment();
 		// Try to connect to known devices in parallel
 		Map<ContactId, TransportProperties> remote =
 				callback.getRemoteProperties();
@@ -202,8 +206,10 @@ class BluetoothPlugin implements DuplexPlugin {
 				public void run() {
 					if (!running) return;
 					StreamConnection s = connect(makeUrl(address, uuid));
-					if (s != null)
+					if (s != null) {
+						backoff.reset();
 						callback.outgoingConnectionCreated(c, wrapSocket(s));
+					}
 				}
 			});
 		}
diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java
index 28859c4d7eae9e79ff42baf2081b70f71dafc809..5259ece49b8d7f96143b9ed719f87adfaa8771cc 100644
--- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java
+++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java
@@ -1,28 +1,34 @@
 package org.briarproject.plugins.bluetooth;
 
-import java.security.SecureRandom;
-import java.util.concurrent.Executor;
-
 import org.briarproject.api.TransportId;
+import org.briarproject.api.plugins.Backoff;
+import org.briarproject.api.plugins.BackoffFactory;
 import org.briarproject.api.plugins.duplex.DuplexPlugin;
 import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
 import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
 import org.briarproject.api.system.Clock;
 import org.briarproject.system.SystemClock;
 
+import java.security.SecureRandom;
+import java.util.concurrent.Executor;
+
 public class BluetoothPluginFactory implements DuplexPluginFactory {
 
 	private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
-	private static final int POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes
+	private static final int MIN_POLLING_INTERVAL = 2 * 60 * 1000; // 2 minutes
+	private static final int MAX_POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour
+	private static final double BACKOFF_BASE = 1.2;
 
 	private final Executor ioExecutor;
 	private final SecureRandom secureRandom;
+	private final BackoffFactory backoffFactory;
 	private final Clock clock;
 
 	public BluetoothPluginFactory(Executor ioExecutor,
-			SecureRandom secureRandom) {
+			SecureRandom secureRandom, BackoffFactory backoffFactory) {
 		this.ioExecutor = ioExecutor;
 		this.secureRandom = secureRandom;
+		this.backoffFactory = backoffFactory;
 		clock = new SystemClock();
 	}
 
@@ -31,7 +37,9 @@ public class BluetoothPluginFactory implements DuplexPluginFactory {
 	}
 
 	public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
-		return new BluetoothPlugin(ioExecutor, clock, secureRandom, callback,
-				MAX_LATENCY, POLLING_INTERVAL);
+		Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
+				MAX_POLLING_INTERVAL, BACKOFF_BASE);
+		return new BluetoothPlugin(ioExecutor, secureRandom, clock, backoff,
+				callback, MAX_LATENCY);
 	}
 }
diff --git a/briar-tests/src/org/briarproject/plugins/BackoffImplTest.java b/briar-tests/src/org/briarproject/plugins/BackoffImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..81e48b5614df1243db9bd6a7ca0d9d8d73deec71
--- /dev/null
+++ b/briar-tests/src/org/briarproject/plugins/BackoffImplTest.java
@@ -0,0 +1,63 @@
+package org.briarproject.plugins;
+
+import org.briarproject.BriarTestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class BackoffImplTest extends BriarTestCase {
+
+	private static final int MIN_INTERVAL = 60 * 1000;
+	private static final int MAX_INTERVAL = 60 * 60 * 1000;
+	private static final double BASE = 1.2;
+
+	@Test
+	public void testPollingIntervalStartsAtMinimum() {
+		BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
+		assertEquals(MIN_INTERVAL, b.getPollingInterval());
+	}
+
+	@Test
+	public void testIncrementIncreasesPollingInterval() {
+		BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
+		b.increment();
+		assertTrue(b.getPollingInterval() > MIN_INTERVAL);
+	}
+
+	@Test
+	public void testResetResetsPollingInterval() {
+		BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
+		b.increment();
+		b.increment();
+		b.reset();
+		assertEquals(MIN_INTERVAL, b.getPollingInterval());
+	}
+
+	@Test
+	public void testBaseAffectsBackoffSpeed() {
+		BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
+		b.increment();
+		int interval = b.getPollingInterval();
+		BackoffImpl b1 = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE * 2);
+		b1.increment();
+		int interval1 = b1.getPollingInterval();
+		assertTrue(interval < interval1);
+	}
+
+	@Test
+	public void testIntervalDoesNotExceedMaxInterval() {
+		BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
+		for (int i = 0; i < 100; i++) b.increment();
+		assertEquals(MAX_INTERVAL, b.getPollingInterval());
+	}
+
+	@Test
+	public void testIntervalDoesNotExceedMaxIntervalWithInfiniteMultiplier() {
+		BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL,
+				Double.POSITIVE_INFINITY);
+		b.increment();
+		assertEquals(MAX_INTERVAL, b.getPollingInterval());
+	}
+}
+
diff --git a/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothClientTest.java b/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothClientTest.java
deleted file mode 100644
index 35102e1c951a6380e1bd7556c043bd82008c32f9..0000000000000000000000000000000000000000
--- a/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothClientTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.briarproject.plugins.bluetooth;
-
-import org.briarproject.api.contact.ContactId;
-import org.briarproject.api.properties.TransportProperties;
-import org.briarproject.api.settings.Settings;
-import org.briarproject.plugins.DuplexClientTest;
-import org.briarproject.system.SystemClock;
-
-import java.security.SecureRandom;
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-// This is not a JUnit test - it has to be run manually while the server test
-// is running on another machine
-public class BluetoothClientTest extends DuplexClientTest {
-
-	private BluetoothClientTest(Executor executor, String serverAddress) {
-		// Store the server's Bluetooth address and UUID
-		TransportProperties p = new TransportProperties();
-		p.put("address", serverAddress);
-		p.put("uuid", BluetoothTest.EMPTY_UUID);
-		Map<ContactId, TransportProperties> remote =
-				Collections.singletonMap(contactId, p);
-		// Create the plugin
-		callback = new ClientCallback(new Settings(),
-				new TransportProperties(), remote);
-		plugin = new BluetoothPlugin(executor, new SystemClock(),
-				new SecureRandom(), callback, 0, 0);
-	}
-
-	public static void main(String[] args) throws Exception {
-		if (args.length != 1) {
-			System.err.println("Please specify the server's Bluetooth address");
-			System.exit(1);
-		}
-		ExecutorService executor = Executors.newCachedThreadPool();
-		try {
-			new BluetoothClientTest(executor, args[0]).run();
-		} finally {
-			executor.shutdown();
-		}
-	}
-}
diff --git a/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothServerTest.java b/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothServerTest.java
deleted file mode 100644
index 105519c423c9379869012d7bb73da711fc355d41..0000000000000000000000000000000000000000
--- a/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothServerTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.briarproject.plugins.bluetooth;
-
-import org.briarproject.api.properties.TransportProperties;
-import org.briarproject.api.settings.Settings;
-import org.briarproject.plugins.DuplexServerTest;
-import org.briarproject.system.SystemClock;
-
-import java.security.SecureRandom;
-import java.util.Collections;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-// This is not a JUnit test - it has to be run manually while the client test
-// is running on another machine
-public class BluetoothServerTest extends DuplexServerTest {
-
-	private BluetoothServerTest(Executor executor) {
-		// Store the UUID
-		TransportProperties local = new TransportProperties();
-		local.put("uuid", BluetoothTest.EMPTY_UUID);
-		// Create the plugin
-		callback = new ServerCallback(new Settings(), local,
-				Collections.singletonMap(contactId, new TransportProperties()));
-		plugin = new BluetoothPlugin(executor, new SystemClock(),
-				new SecureRandom(), callback, 0, 0);
-	}
-
-	public static void main(String[] args) throws Exception {
-		ExecutorService executor = Executors.newCachedThreadPool();
-		try {
-			new BluetoothServerTest(executor).run();
-		} finally {
-			executor.shutdown();
-		}
-	}
-}
diff --git a/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothTest.java b/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothTest.java
deleted file mode 100644
index 300a5062e0d14ef77c30cc12a0647340e2f2f89f..0000000000000000000000000000000000000000
--- a/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothTest.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.briarproject.plugins.bluetooth;
-
-import java.util.UUID;
-
-class BluetoothTest {
-
-	static final String EMPTY_UUID =
-			UUID.nameUUIDFromBytes(new byte[0]).toString();
-}
\ No newline at end of file