diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPluginFactory.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPluginFactory.java index 96f094d72..d9c4c2d07 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPluginFactory.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPluginFactory.java @@ -24,6 +24,7 @@ import java.util.concurrent.Executor; import javax.annotation.concurrent.Immutable; import javax.inject.Inject; +import static android.os.Build.BRAND; import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; @Immutable @@ -81,8 +82,12 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory { @Override public DuplexPlugin createPlugin(PluginCallback callback) { + // On Motorola devices, don't try to open multiple connections - the + // Bluetooth stack can't handle it + // TODO: Narrow this down to specific models/versions if possible + boolean singleConnection = "motorola".equalsIgnoreCase(BRAND); BluetoothConnectionLimiter connectionLimiter = - new BluetoothConnectionLimiterImpl(eventBus); + new BluetoothConnectionLimiterImpl(eventBus, singleConnection); BluetoothConnectionFactory connectionFactory = new AndroidBluetoothConnectionFactory(connectionLimiter, wakeLockManager, timeoutMonitor); diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/AbstractDuplexTransportConnection.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/AbstractDuplexTransportConnection.java index 27aad596a..4bb264ce1 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/AbstractDuplexTransportConnection.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/AbstractDuplexTransportConnection.java @@ -22,6 +22,8 @@ public abstract class AbstractDuplexTransportConnection private final Writer writer; private final AtomicBoolean halfClosed, closed; + private volatile boolean markedForClose = false; + protected AbstractDuplexTransportConnection(Plugin plugin) { this.plugin = plugin; reader = new Reader(); @@ -52,6 +54,16 @@ public abstract class AbstractDuplexTransportConnection return remote; } + @Override + public boolean isMarkedForClose() { + return markedForClose; + } + + @Override + public void markForClose() { + markedForClose = true; + } + private class Reader implements TransportConnectionReader { @Override diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/DuplexTransportConnection.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/DuplexTransportConnection.java index d0e98bfba..6bceff176 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/DuplexTransportConnection.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/DuplexTransportConnection.java @@ -1,6 +1,7 @@ package org.briarproject.bramble.api.plugin.duplex; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.properties.TransportProperties; @@ -30,4 +31,17 @@ public interface DuplexTransportConnection { * the remote peer. */ TransportProperties getRemoteProperties(); + + /** + * Returns true if the connection should be closed immediately without + * sending anything. + */ + boolean isMarkedForClose(); + + /** + * Call this method before the connection is passed to its + * {@link ConnectionHandler} if the connection should be closed immediately + * without sending anything. + */ + void markForClose(); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/connection/DuplexSyncConnection.java b/bramble-core/src/main/java/org/briarproject/bramble/connection/DuplexSyncConnection.java index 33b90c68f..4a6b503e6 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/connection/DuplexSyncConnection.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/connection/DuplexSyncConnection.java @@ -33,6 +33,7 @@ abstract class DuplexSyncConnection extends SyncConnection final Executor ioExecutor; final TransportId transportId; + final DuplexTransportConnection connection; final TransportConnectionReader reader; final TransportConnectionWriter writer; final TransportProperties remote; @@ -80,6 +81,7 @@ abstract class DuplexSyncConnection extends SyncConnection transportPropertyManager); this.ioExecutor = ioExecutor; this.transportId = transportId; + this.connection = connection; reader = connection.getReader(); writer = connection.getWriter(); remote = connection.getRemoteProperties(); @@ -96,6 +98,13 @@ abstract class DuplexSyncConnection extends SyncConnection disposeOnError(writer); } + void closeOutgoingStream(StreamContext ctx, TransportConnectionWriter w) + throws IOException { + StreamWriter streamWriter = streamWriterFactory.createStreamWriter( + w.getOutputStream(), ctx); + streamWriter.sendEndOfStream(); + } + SyncSession createDuplexOutgoingSession(StreamContext ctx, TransportConnectionWriter w, @Nullable Priority priority) throws IOException { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/connection/IncomingDuplexSyncConnection.java b/bramble-core/src/main/java/org/briarproject/bramble/connection/IncomingDuplexSyncConnection.java index abf34ca0f..ca72db1eb 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/connection/IncomingDuplexSyncConnection.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/connection/IncomingDuplexSyncConnection.java @@ -93,10 +93,16 @@ class IncomingDuplexSyncConnection extends DuplexSyncConnection return; } try { - // Create and run the outgoing session - SyncSession out = createDuplexOutgoingSession(ctx, writer, null); - setOutgoingSession(out); - out.run(); + if (connection.isMarkedForClose()) { + // Close the outgoing stream without sending anything + closeOutgoingStream(ctx, writer); + } else { + // Create and run the outgoing session + SyncSession out = + createDuplexOutgoingSession(ctx, writer, null); + setOutgoingSession(out); + out.run(); + } writer.dispose(false); } catch (IOException e) { logException(LOG, WARNING, e); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/connection/OutgoingDuplexSyncConnection.java b/bramble-core/src/main/java/org/briarproject/bramble/connection/OutgoingDuplexSyncConnection.java index d7f777b7b..4b38e23df 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/connection/OutgoingDuplexSyncConnection.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/connection/OutgoingDuplexSyncConnection.java @@ -65,11 +65,16 @@ class OutgoingDuplexSyncConnection extends DuplexSyncConnection Priority priority = generatePriority(); ioExecutor.execute(() -> runIncomingSession(priority)); try { - // Create and run the outgoing session - SyncSession out = - createDuplexOutgoingSession(ctx, writer, priority); - setOutgoingSession(out); - out.run(); + if (connection.isMarkedForClose()) { + // Close the outgoing stream without sending anything + closeOutgoingStream(ctx, writer); + } else { + // Create and run the outgoing session + SyncSession out = + createDuplexOutgoingSession(ctx, writer, priority); + setOutgoingSession(out); + out.run(); + } writer.dispose(false); } catch (IOException e) { logException(LOG, WARNING, e); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiter.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiter.java index c3b115e58..f15cceeeb 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiter.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiter.java @@ -23,7 +23,9 @@ interface BluetoothConnectionLimiter { boolean canOpenContactConnection(); /** - * Informs the limiter that the given connection has been opened. + * Informs the limiter that the given connection has been opened. If the + * connection is above the limit it will be + * {@link DuplexTransportConnection#markForClose() marked for close}. */ void connectionOpened(DuplexTransportConnection conn); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiterImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiterImpl.java index e76bbafc6..7a28f5df4 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiterImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionLimiterImpl.java @@ -24,6 +24,7 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter { getLogger(BluetoothConnectionLimiterImpl.class.getName()); private final EventBus eventBus; + private final boolean singleConnection; private final Object lock = new Object(); @GuardedBy("lock") @@ -32,8 +33,10 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter { @GuardedBy("lock") private boolean keyAgreementInProgress = false; - BluetoothConnectionLimiterImpl(EventBus eventBus) { + BluetoothConnectionLimiterImpl(EventBus eventBus, + boolean singleConnection) { this.eventBus = eventBus; + this.singleConnection = singleConnection; } @Override @@ -59,6 +62,9 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter { if (keyAgreementInProgress) { LOG.info("Can't open contact connection during key agreement"); return false; + } else if (singleConnection && !connections.isEmpty()) { + LOG.info("Can't open contact connection due to limit"); + return false; } else { LOG.info("Can open contact connection"); return true; @@ -68,12 +74,18 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter { @Override public void connectionOpened(DuplexTransportConnection conn) { + boolean shouldClose = false; synchronized (lock) { connections.add(conn); if (LOG.isLoggable(INFO)) { LOG.info("Connection opened, " + connections.size() + " open"); } + if (singleConnection && connections.size() > 1) { + LOG.info("Marking connection for close"); + shouldClose = true; + } } + if (shouldClose) conn.markForClose(); } @Override diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothPluginFactory.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothPluginFactory.java index 8f87c78bb..09d23713a 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothPluginFactory.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothPluginFactory.java @@ -65,7 +65,7 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory { @Override public DuplexPlugin createPlugin(PluginCallback callback) { BluetoothConnectionLimiter connectionLimiter = - new BluetoothConnectionLimiterImpl(eventBus); + new BluetoothConnectionLimiterImpl(eventBus, false); BluetoothConnectionFactory connectionFactory = new JavaBluetoothConnectionFactory(connectionLimiter, timeoutMonitor);