Share a single OS wake lock among all holders.

This commit is contained in:
akwizgran
2020-08-04 17:24:24 +01:00
parent eb6b4aa850
commit 286f6f492c
7 changed files with 110 additions and 42 deletions

View File

@@ -1,16 +1,9 @@
package org.briarproject.bramble.api.system; package org.briarproject.bramble.api.system;
import android.os.PowerManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface AndroidWakeLockFactory { public interface AndroidWakeLockFactory {
/** AndroidWakeLock createWakeLock();
* Creates and returns a wake lock.
*
* @param levelAndFlags See {@link PowerManager#newWakeLock(int, String)}
*/
AndroidWakeLock createWakeLock(int levelAndFlags);
} }

View File

@@ -13,7 +13,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress; import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress;
@@ -36,7 +35,7 @@ class AndroidBluetoothTransportConnection
this.socket = socket; this.socket = socket;
in = timeoutMonitor.createTimeoutInputStream( in = timeoutMonitor.createTimeoutInputStream(
socket.getInputStream(), plugin.getMaxIdleTime() * 2); socket.getInputStream(), plugin.getMaxIdleTime() * 2);
wakeLock = wakeLockFactory.createWakeLock(PARTIAL_WAKE_LOCK); wakeLock = wakeLockFactory.createWakeLock();
wakeLock.acquire(); wakeLock.acquire();
String address = socket.getRemoteDevice().getAddress(); String address = socket.getRemoteDevice().getAddress();
if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address); if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address);

View File

@@ -23,7 +23,6 @@ import java.util.concurrent.Executor;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -54,7 +53,7 @@ class AndroidTorPlugin extends TorPlugin {
maxLatency, maxIdleTime, maxLatency, maxIdleTime,
appContext.getDir("tor", MODE_PRIVATE)); appContext.getDir("tor", MODE_PRIVATE));
this.appContext = appContext; this.appContext = appContext;
wakeLock = wakeLockFactory.createWakeLock(PARTIAL_WAKE_LOCK); wakeLock = wakeLockFactory.createWakeLock();
} }
@Override @Override

View File

@@ -15,6 +15,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.content.Context.POWER_SERVICE; import static android.content.Context.POWER_SERVICE;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@@ -34,22 +35,20 @@ class AndroidWakeLockFactoryImpl implements AndroidWakeLockFactory {
*/ */
private static final long SAFETY_MARGIN_MS = SECONDS.toMillis(10); private static final long SAFETY_MARGIN_MS = SECONDS.toMillis(10);
private final TaskScheduler scheduler; private final SharedWakeLock sharedWakeLock;
private final PowerManager powerManager;
private final String tag;
@Inject @Inject
AndroidWakeLockFactoryImpl(TaskScheduler scheduler, Application app) { AndroidWakeLockFactoryImpl(TaskScheduler scheduler, Application app) {
this.scheduler = scheduler; PowerManager powerManager = (PowerManager)
powerManager = (PowerManager)
requireNonNull(app.getSystemService(POWER_SERVICE)); requireNonNull(app.getSystemService(POWER_SERVICE));
tag = getWakeLockTag(app); String tag = getWakeLockTag(app);
sharedWakeLock = new RenewableWakeLock(powerManager, scheduler,
PARTIAL_WAKE_LOCK, tag, LOCK_DURATION_MS, SAFETY_MARGIN_MS);
} }
@Override @Override
public AndroidWakeLock createWakeLock(int levelAndFlags) { public AndroidWakeLock createWakeLock() {
return new RenewableWakeLock(powerManager, scheduler, levelAndFlags, return new AndroidWakeLockImpl(sharedWakeLock);
tag, LOCK_DURATION_MS, SAFETY_MARGIN_MS);
} }
private String getWakeLockTag(Context ctx) { private String getWakeLockTag(Context ctx) {

View File

@@ -0,0 +1,46 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidWakeLock;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
/**
* A wrapper around a {@link SharedWakeLock} that provides the more convenient
* semantics of {@link AndroidWakeLock} (i.e. calls to acquire() and release()
* don't need to be balanced).
*/
@ThreadSafe
@NotNullByDefault
class AndroidWakeLockImpl implements AndroidWakeLock {
private final SharedWakeLock sharedWakeLock;
private final Object lock = new Object();
@GuardedBy("lock")
private boolean held = false;
AndroidWakeLockImpl(SharedWakeLock sharedWakeLock) {
this.sharedWakeLock = sharedWakeLock;
}
@Override
public void acquire() {
synchronized (lock) {
if (!held) {
held = true;
sharedWakeLock.acquire();
}
}
}
@Override
public void release() {
synchronized (lock) {
if (held) {
held = false;
sharedWakeLock.release();
}
}
}
}

View File

@@ -4,22 +4,23 @@ import android.os.PowerManager;
import android.os.PowerManager.WakeLock; import android.os.PowerManager.WakeLock;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidWakeLock;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.system.TaskScheduler;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
class RenewableWakeLock implements AndroidWakeLock { class RenewableWakeLock implements SharedWakeLock {
private static final Logger LOG = private static final Logger LOG =
getLogger(RenewableWakeLock.class.getName()); getLogger(RenewableWakeLock.class.getName());
@@ -31,10 +32,14 @@ class RenewableWakeLock implements AndroidWakeLock {
private final long durationMs, safetyMarginMs; private final long durationMs, safetyMarginMs;
private final Object lock = new Object(); private final Object lock = new Object();
@GuardedBy("lock")
@Nullable @Nullable
private WakeLock wakeLock; // Locking: lock private WakeLock wakeLock;
@GuardedBy("lock")
@Nullable @Nullable
private Future<?> future; // Locking: lock private Future<?> future;
@GuardedBy("lock")
private int refCount = 0;
RenewableWakeLock(PowerManager powerManager, TaskScheduler scheduler, RenewableWakeLock(PowerManager powerManager, TaskScheduler scheduler,
int levelAndFlags, String tag, long durationMs, int levelAndFlags, String tag, long durationMs,
@@ -49,16 +54,21 @@ class RenewableWakeLock implements AndroidWakeLock {
@Override @Override
public void acquire() { public void acquire() {
if (LOG.isLoggable(INFO)) LOG.info("Acquiring wake lock " + tag);
synchronized (lock) { synchronized (lock) {
if (wakeLock != null) { refCount++;
LOG.info("Already acquired"); if (refCount == 1) {
return; if (LOG.isLoggable(INFO)) {
LOG.info("Acquiring wake lock " + tag);
}
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
// We do our own reference counting so we can replace the lock
// TODO: Check whether using a ref-counted wake lock affects
// power management apps
wakeLock.setReferenceCounted(false);
wakeLock.acquire(durationMs + safetyMarginMs);
future = scheduler.schedule(this::renew, durationMs,
MILLISECONDS);
} }
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
wakeLock.setReferenceCounted(false);
wakeLock.acquire(durationMs + safetyMarginMs);
future = scheduler.schedule(this::renew, durationMs, MILLISECONDS);
} }
} }
@@ -80,17 +90,17 @@ class RenewableWakeLock implements AndroidWakeLock {
@Override @Override
public void release() { public void release() {
if (LOG.isLoggable(INFO)) LOG.info("Releasing wake lock " + tag);
synchronized (lock) { synchronized (lock) {
if (wakeLock == null) { refCount--;
LOG.info("Already released"); if (refCount == 0) {
return; if (LOG.isLoggable(INFO)) {
LOG.info("Releasing wake lock " + tag);
}
requireNonNull(future).cancel(false);
future = null;
requireNonNull(wakeLock).release();
wakeLock = null;
} }
if (future == null) throw new AssertionError();
future.cancel(false);
future = null;
wakeLock.release();
wakeLock = null;
} }
} }
} }

View File

@@ -0,0 +1,22 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidWakeLock;
@NotNullByDefault
interface SharedWakeLock {
/**
* Acquires the wake lock. This increments the wake lock's reference count,
* so unlike {@link AndroidWakeLock#acquire()} every call to this method
* must be followed by a balancing call to {@link #release()}.
*/
void acquire();
/**
* Releases the wake lock. This decrements the wake lock's reference count,
* so unlike {@link AndroidWakeLock#release()} every call to this method
* must follow a balancing call to {@link #acquire()}.
*/
void release();
}