Compare commits

...

7 Commits

Author SHA1 Message Date
akwizgran
80fa7d29b2 Poll for connections in series, with a delay between attempts.
The delay reduces interference between Bluetooth and wifi.
2020-05-08 18:02:31 +01:00
akwizgran
0db14bd9ad Delegate all other methods to wrapped InputStream. 2020-05-08 16:25:55 +01:00
akwizgran
dd049012ce Add javadoc. 2020-05-08 16:25:55 +01:00
akwizgran
c382ce921c Add unit test for TimeoutInputStream. 2020-05-08 16:25:54 +01:00
akwizgran
639dd43388 Only check timeouts when we have some streams to monitor. 2020-05-08 16:25:54 +01:00
akwizgran
99adf37deb Add timeout monitor for Bluetooth connections. 2020-05-08 16:25:54 +01:00
akwizgran
78391c604b Use keepalives to detect dead connections. 2020-05-08 16:25:54 +01:00
20 changed files with 535 additions and 58 deletions

View File

@@ -9,6 +9,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
@@ -30,6 +31,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.Nullable;
@@ -76,11 +78,13 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Clock clock,
Backoff backoff, PluginCallback callback, int maxLatency) {
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
maxLatency);
TimeoutMonitor timeoutMonitor, Executor ioExecutor,
ScheduledExecutorService scheduler, SecureRandom secureRandom,
AndroidExecutor androidExecutor, Context appContext, Clock clock,
Backoff backoff, PluginCallback callback, int maxLatency,
int maxIdleTime) {
super(connectionLimiter, timeoutMonitor, ioExecutor, scheduler,
secureRandom, backoff, callback, maxLatency, maxIdleTime);
this.androidExecutor = androidExecutor;
this.appContext = appContext;
this.clock = clock;
@@ -172,9 +176,10 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
return wrapSocket(ss.accept());
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
return new AndroidBluetoothTransportConnection(this,
connectionLimiter, s);
private DuplexTransportConnection wrapSocket(BluetoothSocket s)
throws IOException {
return new AndroidBluetoothTransportConnection(this, connectionLimiter,
timeoutMonitor, s);
}
@Override

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.bluetooth;
import android.content.Context;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -15,6 +16,7 @@ import org.briarproject.bramble.api.system.Clock;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.concurrent.Immutable;
@@ -25,28 +27,34 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final SecureRandom secureRandom;
private final EventBus eventBus;
private final Clock clock;
private final TimeoutMonitor timeoutMonitor;
private final BackoffFactory backoffFactory;
public AndroidBluetoothPluginFactory(Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, EventBus eventBus, Clock clock,
ScheduledExecutorService scheduler, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, EventBus eventBus,
Clock clock, TimeoutMonitor timeoutMonitor,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.androidExecutor = androidExecutor;
this.appContext = appContext;
this.secureRandom = secureRandom;
this.eventBus = eventBus;
this.clock = clock;
this.timeoutMonitor = timeoutMonitor;
this.backoffFactory = backoffFactory;
}
@@ -67,8 +75,9 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, ioExecutor, androidExecutor, appContext,
secureRandom, clock, backoff, callback, MAX_LATENCY);
connectionLimiter, timeoutMonitor, ioExecutor, scheduler,
secureRandom, androidExecutor, appContext, clock, backoff,
callback, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin.bluetooth;
import android.bluetooth.BluetoothSocket;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
@@ -17,22 +18,26 @@ import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress
class AndroidBluetoothTransportConnection
extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionManager;
private final BluetoothConnectionLimiter connectionLimiter;
private final BluetoothSocket socket;
private final InputStream in;
AndroidBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionManager,
BluetoothSocket socket) {
BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor, BluetoothSocket socket)
throws IOException {
super(plugin);
this.connectionManager = connectionManager;
this.connectionLimiter = connectionLimiter;
this.socket = socket;
in = timeoutMonitor.createTimeoutInputStream(
socket.getInputStream(), plugin.getMaxIdleTime() * 2);
String address = socket.getRemoteDevice().getAddress();
if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address);
}
@Override
protected InputStream getInputStream() throws IOException {
return socket.getInputStream();
protected InputStream getInputStream() {
return in;
}
@Override
@@ -45,7 +50,7 @@ class AndroidBluetoothTransportConnection
try {
socket.close();
} finally {
connectionManager.connectionClosed(this);
connectionLimiter.connectionClosed(this);
}
}
}

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.io;
import java.io.InputStream;
public interface TimeoutMonitor {
/**
* Returns an {@link InputStream} that wraps the given stream and allows
* read timeouts to be detected.
*
* @param timeoutMs The read timeout in milliseconds. Timeouts will be
* detected eventually but are not guaranteed to be detected immediately.
*/
InputStream createTimeoutInputStream(InputStream in, long timeoutMs);
}

View File

@@ -11,6 +11,11 @@ public interface Clock {
*/
long currentTimeMillis();
/**
* @see System#nanoTime()
*/
long nanoTime();
/**
* @see Thread#sleep(long)
*/

View File

@@ -16,6 +16,11 @@ public class ArrayClock implements Clock {
return times[index++];
}
@Override
public long nanoTime() {
return times[index++] * 1_000_000;
}
@Override
public void sleep(long milliseconds) throws InterruptedException {
Thread.sleep(milliseconds);

View File

@@ -17,6 +17,11 @@ public class SettableClock implements Clock {
return time.get();
}
@Override
public long nanoTime() {
return time.get() * 1_000_000;
}
@Override
public void sleep(long milliseconds) throws InterruptedException {
Thread.sleep(milliseconds);

View File

@@ -9,6 +9,7 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
import org.briarproject.bramble.db.DatabaseModule;
import org.briarproject.bramble.event.EventModule;
import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.io.IoModule;
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule;
@@ -35,6 +36,7 @@ import dagger.Module;
DatabaseExecutorModule.class,
EventModule.class,
IdentityModule.class,
IoModule.class,
KeyAgreementModule.class,
LifecycleModule.class,
PluginModule.class,

View File

@@ -0,0 +1,18 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class IoModule {
@Provides
@Singleton
TimeoutMonitor provideTimeoutMonitor(TimeoutMonitorImpl timeoutMonitor) {
return timeoutMonitor;
}
}

View File

@@ -0,0 +1,104 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.concurrent.GuardedBy;
@NotNullByDefault
class TimeoutInputStream extends InputStream {
private final Clock clock;
private final InputStream in;
private final long timeoutNs;
private final CloseListener listener;
private final Object lock = new Object();
@GuardedBy("lock")
private long readStartedNs = -1;
TimeoutInputStream(Clock clock, InputStream in, long timeoutNs,
CloseListener listener) {
this.clock = clock;
this.in = in;
this.timeoutNs = timeoutNs;
this.listener = listener;
}
@Override
public int read() throws IOException {
synchronized (lock) {
readStartedNs = clock.nanoTime();
}
int input = in.read();
synchronized (lock) {
readStartedNs = -1;
}
return input;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
synchronized (lock) {
readStartedNs = clock.nanoTime();
}
int read = in.read(b, off, len);
synchronized (lock) {
readStartedNs = -1;
}
return read;
}
@Override
public void close() throws IOException {
try {
in.close();
} finally {
listener.onClose(this);
}
}
@Override
public int available() throws IOException {
return in.available();
}
@Override
public void mark(int readlimit) {
in.mark(readlimit);
}
@Override
public boolean markSupported() {
return in.markSupported();
}
@Override
public void reset() throws IOException {
in.reset();
}
@Override
public long skip(long n) throws IOException {
return in.skip(n);
}
boolean hasTimedOut() {
synchronized (lock) {
return readStartedNs != -1 &&
clock.nanoTime() - readStartedNs > timeoutNs;
}
}
interface CloseListener {
void onClose(TimeoutInputStream closed);
}
}

View File

@@ -0,0 +1,96 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.inject.Inject;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
class TimeoutMonitorImpl implements TimeoutMonitor {
private static final Logger LOG =
getLogger(TimeoutMonitorImpl.class.getName());
private static final long CHECK_INTERVAL_MS = SECONDS.toMillis(10);
private final ScheduledExecutorService scheduler;
private final Executor ioExecutor;
private final Clock clock;
private final Object lock = new Object();
@GuardedBy("lock")
private final List<TimeoutInputStream> streams = new ArrayList<>();
@GuardedBy("lock")
private Future<?> task = null;
@Inject
TimeoutMonitorImpl(@Scheduler ScheduledExecutorService scheduler,
@IoExecutor Executor ioExecutor, Clock clock) {
this.scheduler = scheduler;
this.ioExecutor = ioExecutor;
this.clock = clock;
}
@Override
public InputStream createTimeoutInputStream(InputStream in,
long timeoutMs) {
TimeoutInputStream stream = new TimeoutInputStream(clock, in,
timeoutMs * 1_000_000, this::removeStream);
synchronized (lock) {
if (streams.isEmpty()) {
task = scheduler.scheduleWithFixedDelay(this::checkTimeouts,
CHECK_INTERVAL_MS, CHECK_INTERVAL_MS, MILLISECONDS);
}
streams.add(stream);
}
return stream;
}
private void removeStream(TimeoutInputStream stream) {
Future<?> toCancel = null;
synchronized (lock) {
if (streams.remove(stream) && streams.isEmpty()) {
toCancel = task;
task = null;
}
}
if (toCancel != null) toCancel.cancel(false);
}
@Scheduler
private void checkTimeouts() {
ioExecutor.execute(() -> {
List<TimeoutInputStream> snapshot;
synchronized (lock) {
snapshot = new ArrayList<>(streams);
}
for (TimeoutInputStream stream : snapshot) {
if (stream.hasTimedOut()) {
LOG.info("Input stream has timed out");
try {
stream.close();
} catch (IOException e) {
logException(LOG, INFO, e);
}
}
}
});
}
}

View File

@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
@@ -30,13 +31,17 @@ import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.LinkedList;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
@@ -59,13 +64,22 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private static final Logger LOG =
getLogger(BluetoothPlugin.class.getName());
/**
* The delay between connection attempts when polling. This reduces
* interference between Bluetooth and wifi.
*/
private static final long CONNECTION_ATTEMPT_INTERVAL_MS =
SECONDS.toMillis(5);
final BluetoothConnectionLimiter connectionLimiter;
final TimeoutMonitor timeoutMonitor;
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final SecureRandom secureRandom;
private final Backoff backoff;
private final PluginCallback callback;
private final int maxLatency;
private final int maxLatency, maxIdleTime;
private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false, contactConnections = false;
@@ -105,14 +119,19 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection discoverAndConnect(String uuid);
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, SecureRandom secureRandom,
Backoff backoff, PluginCallback callback, int maxLatency) {
TimeoutMonitor timeoutMonitor, Executor ioExecutor,
ScheduledExecutorService scheduler, SecureRandom secureRandom,
Backoff backoff, PluginCallback callback, int maxLatency,
int maxIdleTime) {
this.connectionLimiter = connectionLimiter;
this.timeoutMonitor = timeoutMonitor;
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.secureRandom = secureRandom;
this.backoff = backoff;
this.callback = callback;
this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime;
}
void onAdapterEnabled() {
@@ -141,8 +160,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override
public int getMaxIdleTime() {
// Bluetooth detects dead connections so we don't need keepalives
return Integer.MAX_VALUE;
return maxIdleTime;
}
@Override
@@ -264,21 +282,31 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
properties) {
if (!isRunning() || !shouldAllowContactConnections()) return;
backoff.increment();
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
connect(p.getFirst(), p.getSecond());
LinkedList<Pair<TransportProperties, ConnectionHandler>> connectable =
new LinkedList<>();
for (Pair<TransportProperties, ConnectionHandler> pair : properties) {
TransportProperties p = pair.getFirst();
if (isNullOrEmpty(p.get(PROP_ADDRESS))) continue;
if (isNullOrEmpty(p.get(PROP_UUID))) continue;
connectable.add(pair);
}
if (!connectable.isEmpty()) poll(connectable);
}
private void connect(TransportProperties p, ConnectionHandler h) {
String address = p.get(PROP_ADDRESS);
if (isNullOrEmpty(address)) return;
String uuid = p.get(PROP_UUID);
if (isNullOrEmpty(uuid)) return;
private void poll(LinkedList<Pair<TransportProperties, ConnectionHandler>>
connectable) {
ioExecutor.execute(() -> {
DuplexTransportConnection d = createConnection(p);
if (!isRunning() || !shouldAllowContactConnections()) return;
Pair<TransportProperties, ConnectionHandler> pair =
connectable.removeFirst();
DuplexTransportConnection d = createConnection(pair.getFirst());
if (d != null) {
backoff.reset();
h.handleConnection(d);
pair.getSecond().handleConnection(d);
}
if (!connectable.isEmpty()) {
scheduler.schedule(() -> poll(connectable),
CONNECTION_ATTEMPT_INTERVAL_MS, MILLISECONDS);
}
});
}

View File

@@ -12,6 +12,11 @@ public class SystemClock implements Clock {
return System.currentTimeMillis();
}
@Override
public long nanoTime() {
return System.nanoTime();
}
@Override
public void sleep(long milliseconds) throws InterruptedException {
Thread.sleep(milliseconds);

View File

@@ -2420,6 +2420,11 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
return time;
}
@Override
public long nanoTime() {
return time * 1_000_000;
}
@Override
public void sleep(long milliseconds) throws InterruptedException {
Thread.sleep(milliseconds);

View File

@@ -0,0 +1,143 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.SettableClock;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class TimeoutInputStreamTest extends BrambleTestCase {
private static final long TIMEOUT_MS = MINUTES.toMillis(1);
private final long now = System.currentTimeMillis();
private AtomicLong time;
private UnresponsiveInputStream in;
private AtomicBoolean listenerCalled;
private TimeoutInputStream stream;
private CountDownLatch readReturned;
@Before
public void setUp() {
time = new AtomicLong(now);
in = new UnresponsiveInputStream();
listenerCalled = new AtomicBoolean(false);
stream = new TimeoutInputStream(new SettableClock(time), in,
TIMEOUT_MS * 1_000_000, stream -> listenerCalled.set(true));
readReturned = new CountDownLatch(1);
}
@Test
public void testTimeoutIsReportedIfReadDoesNotReturn() throws Exception {
startReading();
try {
// The stream should not report a timeout
assertFalse(stream.hasTimedOut());
// Time passes
time.set(now + TIMEOUT_MS);
// The stream still shouldn't report a timeout
assertFalse(stream.hasTimedOut());
// Time passes
time.set(now + TIMEOUT_MS + 1);
// The stream should report a timeout
assertTrue(stream.hasTimedOut());
// The listener should not have been called yet
assertFalse(listenerCalled.get());
// Close the stream
stream.close();
// The listener should have been called
assertTrue(listenerCalled.get());
} finally {
// Allow the read to return
in.readFinished.countDown();
}
}
@Test
public void testTimeoutIsNotReportedIfReadReturns() throws Exception {
startReading();
try {
// The stream should not report a timeout
assertFalse(stream.hasTimedOut());
// Time passes
time.set(now + TIMEOUT_MS);
// The stream still shouldn't report a timeout
assertFalse(stream.hasTimedOut());
// Allow the read to finish and wait for it to return
in.readFinished.countDown();
readReturned.await(10, SECONDS);
// Time passes
time.set(now + TIMEOUT_MS + 1);
// The stream should not report a timeout as the read has returned
assertFalse(stream.hasTimedOut());
// The listener should not have been called yet
assertFalse(listenerCalled.get());
// Close the stream
stream.close();
// The listener should have been called
assertTrue(listenerCalled.get());
} finally {
// Allow the read to return in case an assertion was thrown
in.readFinished.countDown();
}
}
private void startReading() throws Exception {
// Start a background thread to read from the unresponsive stream
new Thread(() -> {
try {
assertEquals(123, stream.read());
readReturned.countDown();
} catch (IOException e) {
fail();
}
}).start();
// Wait for the background thread to start reading
assertTrue(in.readStarted.await(10, SECONDS));
}
private class UnresponsiveInputStream extends InputStream {
private final CountDownLatch readStarted = new CountDownLatch(1);
private final CountDownLatch readFinished = new CountDownLatch(1);
@Override
public int read() throws IOException {
readStarted.countDown();
try {
readFinished.await();
return 123;
} catch (InterruptedException e) {
throw new IOException(e);
}
}
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -9,6 +10,7 @@ import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory;
import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory;
import org.briarproject.bramble.plugin.modem.ModemPluginFactory;
import org.briarproject.bramble.plugin.tcp.LanTcpPluginFactory;
@@ -17,6 +19,7 @@ import org.briarproject.bramble.plugin.tcp.WanTcpPluginFactory;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import dagger.Module;
import dagger.Provides;
@@ -29,12 +32,14 @@ public class DesktopPluginModule extends PluginModule {
@Provides
PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
SecureRandom random, BackoffFactory backoffFactory,
ReliabilityLayerFactory reliabilityFactory,
ShutdownManager shutdownManager, EventBus eventBus) {
DuplexPluginFactory bluetooth =
new JavaBluetoothPluginFactory(ioExecutor, random, eventBus,
backoffFactory);
ShutdownManager shutdownManager, EventBus eventBus,
TimeoutMonitor timeoutMonitor) {
DuplexPluginFactory bluetooth = new JavaBluetoothPluginFactory(
ioExecutor, scheduler, random, eventBus, timeoutMonitor,
backoffFactory);
DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
reliabilityFactory);
DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor,

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
@@ -9,6 +10,7 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.Nullable;
@@ -34,10 +36,12 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
private volatile LocalDevice localDevice = null;
JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager,
Executor ioExecutor, SecureRandom secureRandom,
Backoff backoff, PluginCallback callback, int maxLatency) {
super(connectionManager, ioExecutor, secureRandom, backoff, callback,
maxLatency);
TimeoutMonitor timeoutMonitor, Executor ioExecutor,
ScheduledExecutorService scheduler, SecureRandom secureRandom,
Backoff backoff, PluginCallback callback, int maxLatency,
int maxIdleTime) {
super(connectionManager, timeoutMonitor, ioExecutor, scheduler,
secureRandom, backoff, callback, maxLatency, maxIdleTime);
}
@Override
@@ -119,7 +123,9 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
}
private DuplexTransportConnection wrapSocket(StreamConnection s) {
return new JavaBluetoothTransportConnection(this, connectionLimiter, s);
private DuplexTransportConnection wrapSocket(StreamConnection s)
throws IOException {
return new JavaBluetoothTransportConnection(this, connectionLimiter,
timeoutMonitor, s);
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -11,6 +12,7 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.concurrent.Immutable;
@@ -21,22 +23,28 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final SecureRandom secureRandom;
private final BackoffFactory backoffFactory;
private final EventBus eventBus;
private final TimeoutMonitor timeoutMonitor;
private final BackoffFactory backoffFactory;
public JavaBluetoothPluginFactory(Executor ioExecutor,
SecureRandom secureRandom, EventBus eventBus,
ScheduledExecutorService scheduler, SecureRandom secureRandom,
EventBus eventBus, TimeoutMonitor timeoutMonitor,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.secureRandom = secureRandom;
this.backoffFactory = backoffFactory;
this.eventBus = eventBus;
this.timeoutMonitor = timeoutMonitor;
this.backoffFactory = backoffFactory;
}
@Override
@@ -56,7 +64,8 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
ioExecutor, secureRandom, backoff, callback, MAX_LATENCY);
timeoutMonitor, ioExecutor, scheduler, secureRandom, backoff,
callback, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
@@ -14,20 +15,24 @@ import javax.microedition.io.StreamConnection;
class JavaBluetoothTransportConnection
extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionManager;
private final BluetoothConnectionLimiter connectionLimiter;
private final StreamConnection stream;
private final InputStream in;
JavaBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionManager,
StreamConnection stream) {
BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor,
StreamConnection stream) throws IOException {
super(plugin);
this.connectionLimiter = connectionLimiter;
this.stream = stream;
this.connectionManager = connectionManager;
in = timeoutMonitor.createTimeoutInputStream(
stream.openInputStream(), plugin.getMaxIdleTime() * 2);
}
@Override
protected InputStream getInputStream() throws IOException {
return stream.openInputStream();
protected InputStream getInputStream() {
return in;
}
@Override
@@ -40,7 +45,7 @@ class JavaBluetoothTransportConnection
try {
stream.close();
} finally {
connectionManager.connectionClosed(this);
connectionLimiter.connectionClosed(this);
}
}
}

View File

@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.network.NetworkManager;
@@ -122,11 +123,12 @@ public class AppModule {
LocationUtils locationUtils, EventBus eventBus,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Clock clock) {
BatteryManager batteryManager, Clock clock,
TimeoutMonitor timeoutMonitor) {
Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth =
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
appContext, random, eventBus, clock, backoffFactory);
DuplexPluginFactory bluetooth = new AndroidBluetoothPluginFactory(
ioExecutor, scheduler, androidExecutor, appContext, random,
eventBus, clock, timeoutMonitor, backoffFactory);
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
scheduler, appContext, networkManager, locationUtils, eventBus,
torSocketFactory, backoffFactory, resourceProvider,