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;
import android.os.PowerManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface AndroidWakeLockFactory {
/**
* Creates and returns a wake lock.
*
* @param levelAndFlags See {@link PowerManager#newWakeLock(int, String)}
*/
AndroidWakeLock createWakeLock(int levelAndFlags);
AndroidWakeLock createWakeLock();
}

View File

@@ -13,7 +13,6 @@ import java.io.IOException;
import java.io.InputStream;
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.util.AndroidUtils.isValidBluetoothAddress;
@@ -36,7 +35,7 @@ class AndroidBluetoothTransportConnection
this.socket = socket;
in = timeoutMonitor.createTimeoutInputStream(
socket.getInputStream(), plugin.getMaxIdleTime() * 2);
wakeLock = wakeLockFactory.createWakeLock(PARTIAL_WAKE_LOCK);
wakeLock = wakeLockFactory.createWakeLock();
wakeLock.acquire();
String address = socket.getRemoteDevice().getAddress();
if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address);

View File

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

View File

@@ -15,6 +15,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
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.SECONDS;
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 final TaskScheduler scheduler;
private final PowerManager powerManager;
private final String tag;
private final SharedWakeLock sharedWakeLock;
@Inject
AndroidWakeLockFactoryImpl(TaskScheduler scheduler, Application app) {
this.scheduler = scheduler;
powerManager = (PowerManager)
PowerManager powerManager = (PowerManager)
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
public AndroidWakeLock createWakeLock(int levelAndFlags) {
return new RenewableWakeLock(powerManager, scheduler, levelAndFlags,
tag, LOCK_DURATION_MS, SAFETY_MARGIN_MS);
public AndroidWakeLock createWakeLock() {
return new AndroidWakeLockImpl(sharedWakeLock);
}
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 org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidWakeLock;
import org.briarproject.bramble.api.system.TaskScheduler;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@ThreadSafe
@NotNullByDefault
class RenewableWakeLock implements AndroidWakeLock {
class RenewableWakeLock implements SharedWakeLock {
private static final Logger LOG =
getLogger(RenewableWakeLock.class.getName());
@@ -31,10 +32,14 @@ class RenewableWakeLock implements AndroidWakeLock {
private final long durationMs, safetyMarginMs;
private final Object lock = new Object();
@GuardedBy("lock")
@Nullable
private WakeLock wakeLock; // Locking: lock
private WakeLock wakeLock;
@GuardedBy("lock")
@Nullable
private Future<?> future; // Locking: lock
private Future<?> future;
@GuardedBy("lock")
private int refCount = 0;
RenewableWakeLock(PowerManager powerManager, TaskScheduler scheduler,
int levelAndFlags, String tag, long durationMs,
@@ -49,16 +54,21 @@ class RenewableWakeLock implements AndroidWakeLock {
@Override
public void acquire() {
if (LOG.isLoggable(INFO)) LOG.info("Acquiring wake lock " + tag);
synchronized (lock) {
if (wakeLock != null) {
LOG.info("Already acquired");
return;
refCount++;
if (refCount == 1) {
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
public void release() {
if (LOG.isLoggable(INFO)) LOG.info("Releasing wake lock " + tag);
synchronized (lock) {
if (wakeLock == null) {
LOG.info("Already released");
return;
refCount--;
if (refCount == 0) {
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();
}