diff --git a/components/net/sf/briar/db/DatabaseCleanerImpl.java b/components/net/sf/briar/db/DatabaseCleanerImpl.java
index e1242850f5b4afd82122b72f31ce37117ce7794d..a5c6fb194e8705700920a4efb324aac1e4a0183d 100644
--- a/components/net/sf/briar/db/DatabaseCleanerImpl.java
+++ b/components/net/sf/briar/db/DatabaseCleanerImpl.java
@@ -37,7 +37,10 @@ class DatabaseCleanerImpl implements DatabaseCleaner, Runnable {
 						try {
 							wait(msBetweenSweeps);
 						} catch(InterruptedException e) {
+							if(LOG.isLoggable(Level.INFO))
+								LOG.info("Interrupted while waiting to clean");
 							Thread.currentThread().interrupt();
+							return;
 						}
 					}
 				} catch(DbException e) {
diff --git a/components/net/sf/briar/db/DatabaseExecutorImpl.java b/components/net/sf/briar/db/DatabaseExecutorImpl.java
index 8692b3e927656fb76514445a4e18540fff452c6a..f6e0d1cf0f430c3ea07ab4a8ccab268c8972432e 100644
--- a/components/net/sf/briar/db/DatabaseExecutorImpl.java
+++ b/components/net/sf/briar/db/DatabaseExecutorImpl.java
@@ -5,6 +5,8 @@ import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * An executor that limits the number of concurrent database tasks and the
@@ -26,6 +28,9 @@ class DatabaseExecutorImpl implements Executor {
 	/** The maximum number of concurrent tasks. */
 	private static final int MAX_THREADS = 10;
 
+	private static final Logger LOG =
+		Logger.getLogger(DatabaseExecutorImpl.class.getName());
+
 	private final BlockingQueue<Runnable> queue;
 
 	DatabaseExecutorImpl() {
@@ -43,6 +48,8 @@ class DatabaseExecutorImpl implements Executor {
 			// Block until there's space in the queue
 			queue.put(r);
 		} catch(InterruptedException e) {
+			if(LOG.isLoggable(Level.INFO))
+				LOG.info("Interrupted while queueing task");
 			Thread.currentThread().interrupt();
 		}
 	}
diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java
index 441dd7b3c95ce5619967c2687c46b1a91d9d5895..6cd1e1155f6a255ea72007e690957173243c828e 100644
--- a/components/net/sf/briar/db/JdbcDatabase.java
+++ b/components/net/sf/briar/db/JdbcDatabase.java
@@ -446,6 +446,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 	}
 
 	protected void closeAllConnections() throws SQLException {
+		boolean interrupted = false;
 		synchronized(connections) {
 			closed = true;
 			for(Connection c : connections) c.close();
@@ -455,13 +456,16 @@ abstract class JdbcDatabase implements Database<Connection> {
 				try {
 					connections.wait();
 				} catch(InterruptedException e) {
-					Thread.currentThread().interrupt();
+					if(LOG.isLoggable(Level.INFO))
+						LOG.info("Interrupted while closing connections");
+					interrupted = true;
 				}
 				for(Connection c : connections) c.close();
 				openConnections -= connections.size();
 				connections.clear();
 			}
 		}
+		if(interrupted) Thread.currentThread().interrupt();
 	}
 
 	public void addBatchToAck(Connection txn, ContactId c, BatchId b)
diff --git a/components/net/sf/briar/lifecycle/WindowsShutdownManagerImpl.java b/components/net/sf/briar/lifecycle/WindowsShutdownManagerImpl.java
index a84b52440b52ec61517d0de8853a2b703e6e429d..bd1316e602b1ef6dc0d6f071c29b74a1b546fab2 100644
--- a/components/net/sf/briar/lifecycle/WindowsShutdownManagerImpl.java
+++ b/components/net/sf/briar/lifecycle/WindowsShutdownManagerImpl.java
@@ -70,6 +70,7 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
 
 	// Package access for testing
 	synchronized void runShutdownHooks() {
+		boolean interrupted = false;
 		// Start each hook in its own thread
 		for(Thread hook : hooks.values()) hook.start();
 		// Wait for all the hooks to finish
@@ -77,9 +78,12 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
 			try {
 				hook.join();
 			} catch(InterruptedException e) {
-				Thread.currentThread().interrupt();
+				if(LOG.isLoggable(Level.INFO))
+					LOG.info("Interrupted while running shutdown hooks");
+				interrupted = true;
 			}
 		}
+		if(interrupted) Thread.currentThread().interrupt();
 	}
 
 	private class EventLoop extends Thread {
diff --git a/components/net/sf/briar/plugins/PollerImpl.java b/components/net/sf/briar/plugins/PollerImpl.java
index 593b625a2bcf2203ab53b65eb6b25826c911dfc7..75de03ae6dd0a08f2fbbe09e7d7fab7d7ff88a10 100644
--- a/components/net/sf/briar/plugins/PollerImpl.java
+++ b/components/net/sf/briar/plugins/PollerImpl.java
@@ -52,7 +52,10 @@ class PollerImpl implements Poller, Runnable {
 					try {
 						wait(p.time - now);
 					} catch(InterruptedException e) {
+						if(LOG.isLoggable(Level.INFO))
+							LOG.info("Interrupted while waiting to poll");
 						Thread.currentThread().interrupt();
+						return;
 					}
 				}
 			}
diff --git a/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java b/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
index a06c13a2625450a8c75a994461cd18eb834478fc..b6c53a4b224fd947eccc434d16a416787dd9530e 100644
--- a/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
+++ b/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
@@ -246,11 +246,18 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
 		synchronized(discoveryLock) {
 			try {
 				discoveryAgent.startInquiry(DiscoveryAgent.GIAC, listener);
-				return listener.waitForUrls();
 			} catch(BluetoothStateException e) {
 				if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
 				return Collections.emptyMap();
 			}
+			try {
+				return listener.waitForUrls();
+			} catch(InterruptedException e) {
+				if(LOG.isLoggable(Level.INFO))
+					LOG.info("Interrupted while waiting for URLs");
+				Thread.currentThread().interrupt();
+				return Collections.emptyMap();
+			}
 		}
 	}
 
@@ -294,8 +301,15 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
 		ConnectionCallback c = new ConnectionCallback(uuid, timeout);
 		startOutgoingInvitationThread(c);
 		startIncomingInvitationThread(c);
-		StreamConnection s = c.waitForConnection();
-		return s == null ? null : new BluetoothTransportConnection(s);
+		try {
+			StreamConnection s = c.waitForConnection();
+			return s == null ? null : new BluetoothTransportConnection(s);
+		} catch(InterruptedException e) {
+			if(LOG.isLoggable(Level.INFO))
+				LOG.info("Interrupted while waiting for connection");
+			Thread.currentThread().interrupt();
+			return null;
+		}
 	}
 
 	private String convertInvitationCodeToUuid(int code) {
@@ -333,6 +347,11 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
 					if(LOG.isLoggable(Level.WARNING))
 						LOG.warning(e.getMessage());
 					return;
+				} catch(InterruptedException e) {
+					if(LOG.isLoggable(Level.INFO))
+						LOG.info("Interrupted while waiting for URL");
+					Thread.currentThread().interrupt();
+					return;
 				}
 			}
 			synchronized(this) {
@@ -376,9 +395,13 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
 		// Close the socket when the invitation times out
 		try {
 			Thread.sleep(c.getTimeout());
-			scn.close();
 		} catch(InterruptedException e) {
+			if(LOG.isLoggable(Level.INFO))
+				LOG.info("Interrupted while waiting for invitation timeout");
 			Thread.currentThread().interrupt();
+		}
+		try {
+			scn.close();
 		} catch(IOException e) {
 			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
 		}
diff --git a/components/net/sf/briar/plugins/bluetooth/ConnectionCallback.java b/components/net/sf/briar/plugins/bluetooth/ConnectionCallback.java
index 3e75bd2b371abcb22c5431b4fb5f253c9faafe43..7700263f3a3516f58fb4d3cb64fcf25e53a423ff 100644
--- a/components/net/sf/briar/plugins/bluetooth/ConnectionCallback.java
+++ b/components/net/sf/briar/plugins/bluetooth/ConnectionCallback.java
@@ -31,14 +31,11 @@ class ConnectionCallback {
 		return timeout;
 	}
 
-	synchronized StreamConnection waitForConnection() {
+	synchronized StreamConnection waitForConnection()
+	throws InterruptedException {
 		long now = System.currentTimeMillis();
 		while(connection == null && now < end) {
-			try {
-				wait(end - now);
-			} catch(InterruptedException e) {
-				Thread.currentThread().interrupt();
-			}
+			wait(end - now);
 			now = System.currentTimeMillis();
 		}
 		return connection;
diff --git a/components/net/sf/briar/plugins/bluetooth/ContactListener.java b/components/net/sf/briar/plugins/bluetooth/ContactListener.java
index c5e765cb112afb21ee6cd84a3ba721ddd77868c8..5f52a435394dee1ca2bdcc4d97ae02e144719dc3 100644
--- a/components/net/sf/briar/plugins/bluetooth/ContactListener.java
+++ b/components/net/sf/briar/plugins/bluetooth/ContactListener.java
@@ -34,12 +34,8 @@ class ContactListener extends AbstractListener {
 		urls = Collections.synchronizedMap(new HashMap<ContactId, String>());
 	}
 
-	Map<ContactId, String> waitForUrls() {
-		try {
-			finished.await();
-		} catch(InterruptedException e) {
-			Thread.currentThread().interrupt();
-		}
+	Map<ContactId, String> waitForUrls() throws InterruptedException {
+		finished.await();
 		return urls;
 	}
 
diff --git a/components/net/sf/briar/plugins/bluetooth/InvitationListener.java b/components/net/sf/briar/plugins/bluetooth/InvitationListener.java
index 993086a798a96f2e7e466ce73dfa1c1801d6dccb..e7f35bf16270300e03024c8ddddfc0f637fb110f 100644
--- a/components/net/sf/briar/plugins/bluetooth/InvitationListener.java
+++ b/components/net/sf/briar/plugins/bluetooth/InvitationListener.java
@@ -26,12 +26,8 @@ class InvitationListener extends AbstractListener {
 		this.uuid = uuid;
 	}
 
-	String waitForUrl() {
-		try {
-			finished.await();
-		} catch(InterruptedException e) {
-			Thread.currentThread().interrupt();
-		}
+	String waitForUrl() throws InterruptedException {
+		finished.await();
 		return url;
 	}
 
diff --git a/components/net/sf/briar/plugins/file/FileListener.java b/components/net/sf/briar/plugins/file/FileListener.java
index 367764ad2de2362b1d55c5942306e35989e97529..53142c138b4cb14c9423543dc3328738f2589367 100644
--- a/components/net/sf/briar/plugins/file/FileListener.java
+++ b/components/net/sf/briar/plugins/file/FileListener.java
@@ -17,13 +17,8 @@ class FileListener {
 		end = System.currentTimeMillis() + timeout;
 	}
 
-	File waitForFile() {
-		long now = System.currentTimeMillis();
-		try {
-			finished.await(end - now, TimeUnit.MILLISECONDS);
-		} catch(InterruptedException e) {
-			Thread.currentThread().interrupt();
-		}
+	File waitForFile() throws InterruptedException {
+		finished.await(end - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
 		return file;
 	}
 
diff --git a/components/net/sf/briar/plugins/file/FilePlugin.java b/components/net/sf/briar/plugins/file/FilePlugin.java
index 3e9f080300d4eec6bd151e036cd92c643cc05ff4..de1b0466be21d2f8a3505ab31a6ef30e30e0990b 100644
--- a/components/net/sf/briar/plugins/file/FilePlugin.java
+++ b/components/net/sf/briar/plugins/file/FilePlugin.java
@@ -116,8 +116,15 @@ abstract class FilePlugin extends AbstractPlugin implements BatchPlugin {
 			if(files.isEmpty()) {
 				// Wait for a matching file to arrive
 				listener = new FileListener(filename, timeout);
-				File f = listener.waitForFile();
-				if(f != null) files.add(f);
+				File f;
+				try {
+					f = listener.waitForFile();
+					if(f != null) files.add(f);
+				} catch(InterruptedException e) {
+					if(LOG.isLoggable(Level.INFO))
+						LOG.info("Interrupted while waiting for file");
+					Thread.currentThread().interrupt();
+				}
 				listener = null;
 			}
 		}
diff --git a/components/net/sf/briar/plugins/file/PollingRemovableDriveMonitor.java b/components/net/sf/briar/plugins/file/PollingRemovableDriveMonitor.java
index 72f9e9eaea1c1f7309d86b69321a8dde331a63e0..05fd5a057f49b9a9e1b8b174ef8b537cd3bbb763 100644
--- a/components/net/sf/briar/plugins/file/PollingRemovableDriveMonitor.java
+++ b/components/net/sf/briar/plugins/file/PollingRemovableDriveMonitor.java
@@ -3,9 +3,14 @@ package net.sf.briar.plugins.file;
 import java.io.File;
 import java.io.IOException;
 import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
 
+	private static final Logger LOG =
+		Logger.getLogger(PollingRemovableDriveMonitor.class.getName());
+
 	private final RemovableDriveFinder finder;
 	private final long pollingInterval;
 	private final Object pollingLock = new Object();
@@ -48,7 +53,10 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
 					try {
 						pollingLock.wait(pollingInterval);
 					} catch(InterruptedException e) {
+						if(LOG.isLoggable(Level.INFO))
+							LOG.info("Interrupted while waiting to poll");
 						Thread.currentThread().interrupt();
+						return;
 					}
 				}
 				if(!running) return;
diff --git a/components/net/sf/briar/protocol/VerificationExecutorImpl.java b/components/net/sf/briar/protocol/VerificationExecutorImpl.java
index 203ae33ffd2681223327f4fd18fe597de837f040..0c8b439a82830064a1458a9e4e510e36a360aa48 100644
--- a/components/net/sf/briar/protocol/VerificationExecutorImpl.java
+++ b/components/net/sf/briar/protocol/VerificationExecutorImpl.java
@@ -5,6 +5,8 @@ import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * An executor that limits the number of concurrent message verification tasks
@@ -23,6 +25,9 @@ class VerificationExecutorImpl implements Executor {
 	/** The number of idle threads to keep in the pool. */
 	private static final int MIN_THREADS = 1;
 
+	private static final Logger LOG =
+		Logger.getLogger(VerificationExecutorImpl.class.getName());
+
 	private final BlockingQueue<Runnable> queue;
 
 	VerificationExecutorImpl() {
@@ -41,6 +46,8 @@ class VerificationExecutorImpl implements Executor {
 			// Block until there's space in the queue
 			queue.put(r);
 		} catch(InterruptedException e) {
+			if(LOG.isLoggable(Level.INFO))
+				LOG.info("Interrupted while queueing task");
 			Thread.currentThread().interrupt();
 		}
 	}
diff --git a/components/net/sf/briar/transport/FrameScheduler.java b/components/net/sf/briar/transport/FrameScheduler.java
index 2b0d0a9b6ed448edd233b7cc0a8ad75b5e515128..1a3f02d065052dd217e4549b1fec88015fb12742 100644
--- a/components/net/sf/briar/transport/FrameScheduler.java
+++ b/components/net/sf/briar/transport/FrameScheduler.java
@@ -2,6 +2,9 @@ package net.sf.briar.transport;
 
 import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
 
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
 /**
  * A thread that calls the writeFullFrame() method of a PaddedConnectionWriter
  * at regular intervals. The interval between calls is determined by a target
@@ -10,6 +13,9 @@ import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
  */
 class FrameScheduler extends Thread {
 
+	private static final Logger LOG =
+		Logger.getLogger(FrameScheduler.class.getName());
+
 	private final PaddedConnectionWriter writer;
 	private final int millisPerFrame;
 
@@ -21,18 +27,18 @@ class FrameScheduler extends Thread {
 	@Override
 	public void run() {
 		long lastCall = System.currentTimeMillis();
-		while(true) {
-			long now = System.currentTimeMillis();
-			long nextCall = lastCall + millisPerFrame;
-			if(nextCall > now) {
-				try {
-					Thread.sleep(nextCall - now);
-				} catch(InterruptedException e) {
-					Thread.currentThread().interrupt();
-				}
+		try {
+			while(true) {
+				long now = System.currentTimeMillis();
+				long nextCall = lastCall + millisPerFrame;
+				if(nextCall > now) Thread.sleep(nextCall - now);
+				lastCall = System.currentTimeMillis();
+				if(!writer.writeFullFrame()) return;
 			}
-			lastCall = System.currentTimeMillis();
-			if(!writer.writeFullFrame()) return;
+		} catch(InterruptedException e) {
+			if(LOG.isLoggable(Level.INFO))
+				LOG.info("Interrupted while waiting to write frame");
+			Thread.currentThread().interrupt();
 		}
 	}
 }
\ No newline at end of file
diff --git a/components/net/sf/briar/transport/PaddedConnectionWriter.java b/components/net/sf/briar/transport/PaddedConnectionWriter.java
index 6bc7c2762b34d46b859393de582fcafd8ca367c7..4bdbb5572ecfd65995ef0bfb82aacb645c70f2b7 100644
--- a/components/net/sf/briar/transport/PaddedConnectionWriter.java
+++ b/components/net/sf/briar/transport/PaddedConnectionWriter.java
@@ -3,10 +3,12 @@ package net.sf.briar.transport;
 import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
 
 import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.crypto.Mac;
-import net.sf.briar.api.crypto.ErasableKey;
 
+import net.sf.briar.api.crypto.ErasableKey;
 import net.sf.briar.util.ByteUtils;
 
 /**
@@ -17,6 +19,9 @@ import net.sf.briar.util.ByteUtils;
  */
 class PaddedConnectionWriter extends ConnectionWriterImpl {
 
+	private static final Logger LOG =
+		Logger.getLogger(PaddedConnectionWriter.class.getName());
+
 	private final byte[] padding;
 
 	private boolean closed = false;
@@ -108,6 +113,8 @@ class PaddedConnectionWriter extends ConnectionWriterImpl {
 		try {
 			wait();
 		} catch(InterruptedException e) {
+			if(LOG.isLoggable(Level.INFO))
+				LOG.info("Interrupted while waiting for space");
 			Thread.currentThread().interrupt();
 		}
 		if(exception != null) throw exception;
diff --git a/components/net/sf/briar/transport/stream/StreamConnection.java b/components/net/sf/briar/transport/stream/StreamConnection.java
index 8540e0b40e661c5ec15f630fd2fa0534e2237cc7..a3af59c873abee57fc08586dcbaec868342dd00f 100644
--- a/components/net/sf/briar/transport/stream/StreamConnection.java
+++ b/components/net/sf/briar/transport/stream/StreamConnection.java
@@ -199,18 +199,18 @@ abstract class StreamConnection implements DatabaseListener {
 			dbExecutor.execute(new GenerateOffer());
 			// Main loop
 			while(true) {
-				try {
-					Runnable task = writerTasks.take();
-					if(task == CLOSE) break;
-					task.run();
-				} catch(InterruptedException e) {
-					Thread.currentThread().interrupt();
-				}
+				Runnable task = writerTasks.take();
+				if(task == CLOSE) break;
+				task.run();
 			}
 			if(!disposed.getAndSet(true)) transport.dispose(true);
 		} catch(DbException e) {
 			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
 			if(!disposed.getAndSet(true)) transport.dispose(false);
+		} catch(InterruptedException e) {
+			if(LOG.isLoggable(Level.INFO))
+				LOG.info("Interrupted while waiting for task");
+			if(!disposed.getAndSet(true)) transport.dispose(false);
 		} catch(IOException e) {
 			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
 			if(!disposed.getAndSet(true)) transport.dispose(false);