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

@@ -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);
};
}