Limit the number of open Bluetooth connections.

This commit is contained in:
akwizgran
2018-02-13 17:19:05 +00:00
parent 3f1fb1ca1c
commit 55150fe02a
9 changed files with 162 additions and 20 deletions

View File

@@ -55,10 +55,12 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
// Non-null if the plugin started successfully
private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor,
AndroidBluetoothPlugin(BluetoothConnectionManager connectionManager,
Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Backoff backoff,
DuplexPluginCallback callback, int maxLatency) {
super(ioExecutor, secureRandom, backoff, callback, maxLatency);
super(connectionManager, ioExecutor, secureRandom, backoff, callback,
maxLatency);
this.androidExecutor = androidExecutor;
this.appContext = appContext;
}
@@ -154,7 +156,8 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
return new AndroidBluetoothTransportConnection(this, s);
return new AndroidBluetoothTransportConnection(this,
connectionManager, s);
}
@Override

View File

@@ -59,11 +59,13 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
@Override
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
BluetoothConnectionManager connectionManager =
new BluetoothConnectionManagerImpl();
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(ioExecutor,
androidExecutor, appContext, secureRandom, backoff, callback,
MAX_LATENCY);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionManager, ioExecutor, androidExecutor, appContext,
secureRandom, backoff, callback, MAX_LATENCY);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -14,10 +14,14 @@ import java.io.OutputStream;
class AndroidBluetoothTransportConnection
extends AbstractDuplexTransportConnection {
private final BluetoothConnectionManager connectionManager;
private final BluetoothSocket socket;
AndroidBluetoothTransportConnection(Plugin plugin, BluetoothSocket socket) {
AndroidBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionManager connectionManager,
BluetoothSocket socket) {
super(plugin);
this.connectionManager = connectionManager;
this.socket = socket;
}
@@ -33,6 +37,10 @@ class AndroidBluetoothTransportConnection
@Override
protected void closeConnection(boolean exception) throws IOException {
socket.close();
try {
socket.close();
} finally {
connectionManager.connectionClosed();
}
}
}

View File

@@ -0,0 +1,26 @@
package org.briarproject.bramble.plugin.bluetooth;
interface BluetoothConnectionManager {
/**
* Returns true if a contact connection can be opened without exceeding
* the connection limit.
*/
boolean canOpenConnection();
/**
* Increments the number of open connections and returns true if the new
* connection can be kept open without exceeding the connection limit.
*/
boolean connectionOpened();
/**
* Decrements the number of open connections.
*/
void connectionClosed();
/**
* Resets the number of open connections.
*/
void allConnectionsClosed();
}

View File

@@ -0,0 +1,45 @@
package org.briarproject.bramble.plugin.bluetooth;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
class BluetoothConnectionManagerImpl implements BluetoothConnectionManager {
private static final int MAX_OPEN_CONNECTIONS = 5;
private static final Logger LOG =
Logger.getLogger(BluetoothConnectionManagerImpl.class.getName());
private final AtomicInteger openConnections = new AtomicInteger(0);
@Override
public boolean canOpenConnection() {
int open = openConnections.get();
if (LOG.isLoggable(INFO))
LOG.info(open + " open connections");
return open < MAX_OPEN_CONNECTIONS;
}
@Override
public boolean connectionOpened() {
int open = openConnections.incrementAndGet();
if (LOG.isLoggable(INFO))
LOG.info("Connection opened, " + open + " open");
return open <= MAX_OPEN_CONNECTIONS;
}
@Override
public void connectionClosed() {
int open = openConnections.decrementAndGet();
if (LOG.isLoggable(INFO))
LOG.info("Connection closed, " + open + " open");
}
@Override
public void allConnectionsClosed() {
LOG.info("All connections closed");
openConnections.set(0);
}
}

View File

@@ -52,6 +52,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private static final Logger LOG =
Logger.getLogger(BluetoothPlugin.class.getName());
protected final BluetoothConnectionManager connectionManager;
private final Executor ioExecutor;
private final SecureRandom secureRandom;
private final Backoff backoff;
@@ -92,8 +94,10 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection connectTo(String address, String uuid)
throws IOException;
BluetoothPlugin(Executor ioExecutor, SecureRandom secureRandom,
BluetoothPlugin(BluetoothConnectionManager connectionManager,
Executor ioExecutor, SecureRandom secureRandom,
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
this.connectionManager = connectionManager;
this.ioExecutor = ioExecutor;
this.secureRandom = secureRandom;
this.backoff = backoff;
@@ -111,6 +115,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
void onAdapterDisabled() {
LOG.info("Bluetooth disabled");
tryToClose(socket);
connectionManager.allConnectionsClosed();
callback.transportDisabled();
}
@@ -214,11 +219,25 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
return;
}
backoff.reset();
callback.incomingConnectionCreated(conn);
if (connectionManager.connectionOpened()) {
callback.incomingConnectionCreated(conn);
} else {
LOG.info("Closing incoming connection");
tryToCloseUnusedConnection(conn);
}
if (!running) return;
}
}
private void tryToCloseUnusedConnection(DuplexTransportConnection conn) {
try {
conn.getWriter().dispose(false);
conn.getReader().dispose(false, false);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
public void stop() {
running = false;
@@ -258,10 +277,19 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (StringUtils.isNullOrEmpty(uuid)) continue;
ioExecutor.execute(() -> {
if (!isRunning() || !shouldAllowContactConnections()) return;
if (!connectionManager.canOpenConnection()) {
LOG.info("Not connecting, too many open connections");
return;
}
DuplexTransportConnection conn = connect(address, uuid);
if (conn != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, conn);
if (connectionManager.connectionOpened()) {
callback.outgoingConnectionCreated(c, conn);
} else {
LOG.info("Closing outgoing connection");
tryToCloseUnusedConnection(conn);
}
}
});
}
@@ -301,12 +329,25 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override
public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning() || !shouldAllowContactConnections()) return null;
if (!connectionManager.canOpenConnection()) {
LOG.info("Not connecting, too many open connections");
return null;
}
TransportProperties p = callback.getRemoteProperties(c);
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) return null;
return connect(address, uuid);
DuplexTransportConnection conn = connect(address, uuid);
if (conn == null) return null;
// TODO: Why don't we reset the backoff here?
if (connectionManager.connectionOpened()) {
return conn;
} else {
LOG.info("Closing outgoing connection");
tryToCloseUnusedConnection(conn);
return null;
}
}
@Override
@@ -356,7 +397,10 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
return connect(address, uuid);
DuplexTransportConnection conn = connect(address, uuid);
// The connection limit doesn't apply to key agreement
if (conn != null) connectionManager.connectionOpened();
return conn;
}
private String parseAddress(BdfList descriptor) throws FormatException {
@@ -411,6 +455,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
DuplexTransportConnection conn = acceptConnection(ss);
if (LOG.isLoggable(INFO))
LOG.info(ID.getString() + ": Incoming connection");
// The connection limit doesn't apply to key agreement
connectionManager.connectionOpened();
return new KeyAgreementConnection(conn, ID);
};
}

View File

@@ -31,9 +31,11 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
// Non-null if the plugin started successfully
private volatile LocalDevice localDevice = null;
JavaBluetoothPlugin(Executor ioExecutor, SecureRandom secureRandom,
JavaBluetoothPlugin(BluetoothConnectionManager connectionManager,
Executor ioExecutor, SecureRandom secureRandom,
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
super(ioExecutor, secureRandom, backoff, callback, maxLatency);
super(connectionManager, ioExecutor, secureRandom, backoff, callback,
maxLatency);
}
@Override
@@ -110,6 +112,6 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
}
private DuplexTransportConnection wrapSocket(StreamConnection s) {
return new JavaBluetoothTransportConnection(this, s);
return new JavaBluetoothTransportConnection(this, connectionManager, s);
}
}

View File

@@ -51,10 +51,12 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
@Override
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
BluetoothConnectionManager connectionManager =
new BluetoothConnectionManagerImpl();
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(ioExecutor,
secureRandom, backoff, callback, MAX_LATENCY);
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionManager,
ioExecutor, secureRandom, backoff, callback, MAX_LATENCY);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -14,11 +14,15 @@ import javax.microedition.io.StreamConnection;
class JavaBluetoothTransportConnection
extends AbstractDuplexTransportConnection {
private final BluetoothConnectionManager connectionManager;
private final StreamConnection stream;
JavaBluetoothTransportConnection(Plugin plugin, StreamConnection stream) {
JavaBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionManager connectionManager,
StreamConnection stream) {
super(plugin);
this.stream = stream;
this.connectionManager = connectionManager;
}
@Override
@@ -33,6 +37,10 @@ class JavaBluetoothTransportConnection
@Override
protected void closeConnection(boolean exception) throws IOException {
stream.close();
try {
stream.close();
} finally {
connectionManager.connectionClosed();
}
}
}