diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
index 2eb061d32eb67b4639c6a22cdb9ae78de2465f22..46a4f28ceb32c1a7815f34ee0a1ab5b6be5cbe6f 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
@@ -36,6 +36,7 @@ import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
 import org.briarproject.bramble.api.system.Clock;
 import org.briarproject.bramble.api.system.LocationUtils;
 import org.briarproject.bramble.util.IoUtils;
+import org.briarproject.bramble.util.RenewableWakeLock;
 import org.briarproject.bramble.util.StringUtils;
 
 import java.io.Closeable;
@@ -103,6 +104,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 	private static final int COOKIE_TIMEOUT_MS = 3000;
 	private static final int COOKIE_POLLING_INTERVAL_MS = 200;
 	private static final Pattern ONION = Pattern.compile("[a-z2-7]{16}");
+	// This tag may prevent Huawei's power manager from killing us
+	private static final String WAKE_LOCK_TAG = "LocationManagerService";
 	private static final Logger LOG =
 			Logger.getLogger(TorPlugin.class.getName());
 
@@ -119,7 +122,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 	private final ConnectionStatus connectionStatus;
 	private final File torDirectory, torFile, geoIpFile, configFile;
 	private final File doneFile, cookieFile;
-	private final PowerManager.WakeLock wakeLock;
+	private final RenewableWakeLock wakeLock;
 	private final AtomicReference<Future<?>> connectivityCheck =
 			new AtomicReference<>();
 	private final AtomicBoolean used = new AtomicBoolean(false);
@@ -156,14 +159,13 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 		configFile = new File(torDirectory, "torrc");
 		doneFile = new File(torDirectory, "done");
 		cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
-		Object o = appContext.getSystemService(POWER_SERVICE);
-		PowerManager pm = (PowerManager) o;
-		// This tag will prevent Huawei's powermanager from killing us.
-		wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "LocationManagerService");
-		wakeLock.setReferenceCounted(false);
 		// Don't execute more than one connection status check at a time
 		connectionStatusExecutor = new PoliteExecutor("TorPlugin",
 				ioExecutor, 1);
+		PowerManager pm = (PowerManager)
+				appContext.getSystemService(POWER_SERVICE);
+		wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK,
+				WAKE_LOCK_TAG, 1, MINUTES);
 	}
 
 	@Override
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/util/RenewableWakeLock.java b/bramble-android/src/main/java/org/briarproject/bramble/util/RenewableWakeLock.java
new file mode 100644
index 0000000000000000000000000000000000000000..1681cbfd18d30ce84630d7657828718c404bbd5f
--- /dev/null
+++ b/bramble-android/src/main/java/org/briarproject/bramble/util/RenewableWakeLock.java
@@ -0,0 +1,100 @@
+package org.briarproject.bramble.util;
+
+import android.os.PowerManager;
+
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.logging.Level.INFO;
+
+@ThreadSafe
+@NotNullByDefault
+public class RenewableWakeLock {
+
+	private static final Logger LOG =
+			Logger.getLogger(RenewableWakeLock.class.getName());
+
+	/**
+	 * Automatically release the lock this many milliseconds after it's due
+	 * to have been replaced and released.
+	 */
+	private static final int SAFETY_MARGIN_MS = 10_000;
+
+	private final PowerManager powerManager;
+	private final ScheduledExecutorService scheduler;
+	private final int levelAndFlags;
+	private final String tag;
+	private final long durationMs;
+	private final Runnable renewTask;
+
+	private final Object lock = new Object();
+	@Nullable
+	private PowerManager.WakeLock wakeLock; // Locking: lock
+	@Nullable
+	private ScheduledFuture future; // Locking: lock
+
+	public RenewableWakeLock(PowerManager powerManager,
+			ScheduledExecutorService scheduler, int levelAndFlags, String tag,
+			long duration, TimeUnit timeUnit) {
+		this.powerManager = powerManager;
+		this.scheduler = scheduler;
+		this.levelAndFlags = levelAndFlags;
+		this.tag = tag;
+		durationMs = MILLISECONDS.convert(duration, timeUnit);
+		renewTask = this::renew;
+	}
+
+	public void acquire() {
+		if (LOG.isLoggable(INFO)) LOG.info("Acquiring wake lock " + tag);
+		synchronized (lock) {
+			if (wakeLock != null) {
+				LOG.info("Already acquired");
+				return;
+			}
+			wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
+			wakeLock.setReferenceCounted(false);
+			wakeLock.acquire(durationMs + SAFETY_MARGIN_MS);
+			future = scheduler.schedule(renewTask, durationMs, MILLISECONDS);
+		}
+	}
+
+	private void renew() {
+		if (LOG.isLoggable(INFO)) LOG.info("Renewing wake lock " + tag);
+		synchronized (lock) {
+			if (wakeLock == null) {
+				LOG.info("Already released");
+				return;
+			}
+			PowerManager.WakeLock oldWakeLock = wakeLock;
+			wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
+			wakeLock.setReferenceCounted(false);
+			wakeLock.acquire(durationMs + SAFETY_MARGIN_MS);
+			oldWakeLock.release();
+			future = scheduler.schedule(renewTask, durationMs, MILLISECONDS);
+		}
+	}
+
+	public void release() {
+		if (LOG.isLoggable(INFO)) LOG.info("Releasing wake lock " + tag);
+		synchronized (lock) {
+			if (wakeLock == null) {
+				LOG.info("Already released");
+				return;
+			}
+			if (future == null) throw new AssertionError();
+			future.cancel(false);
+			future = null;
+			wakeLock.release();
+			wakeLock = null;
+		}
+	}
+}
+