diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java index 0cb68003a1b3f3a68471459f3c92733fc8e04bdc..16d40d43a9a568d21660547334e1bb44bede7e90 100644 --- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java +++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java @@ -19,11 +19,10 @@ import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.properties.TransportProperties; -import org.briarproject.api.system.Clock; -import org.briarproject.util.LatchedReference; import org.briarproject.util.StringUtils; import java.io.IOException; +import java.io.InputStream; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; @@ -33,9 +32,12 @@ import java.util.Map; import java.util.Map.Entry; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.Future; import java.util.logging.Logger; import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED; @@ -69,7 +71,6 @@ class DroidtoothPlugin implements DuplexPlugin { private final AndroidExecutor androidExecutor; private final Context appContext; private final SecureRandom secureRandom; - private final Clock clock; private final Backoff backoff; private final DuplexPluginCallback callback; private final int maxLatency; @@ -83,13 +84,12 @@ class DroidtoothPlugin implements DuplexPlugin { private volatile BluetoothAdapter adapter = null; DroidtoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor, - Context appContext, SecureRandom secureRandom, Clock clock, - Backoff backoff, DuplexPluginCallback callback, int maxLatency) { + Context appContext, SecureRandom secureRandom, Backoff backoff, + DuplexPluginCallback callback, int maxLatency) { this.ioExecutor = ioExecutor; this.androidExecutor = androidExecutor; this.appContext = appContext; this.secureRandom = secureRandom; - this.clock = clock; this.backoff = backoff; this.callback = callback; this.maxLatency = maxLatency; @@ -339,7 +339,7 @@ class DroidtoothPlugin implements DuplexPlugin { } public DuplexTransportConnection createInvitationConnection(PseudoRandom r, - long timeout) { + long timeout, boolean alice) { if (!isRunning()) return null; // Use the invitation codes to generate the UUID byte[] b = r.nextBytes(UUID_BYTES); @@ -353,23 +353,67 @@ class DroidtoothPlugin implements DuplexPlugin { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); return null; } - // Start the background threads - LatchedReference<BluetoothSocket> socketLatch = - new LatchedReference<BluetoothSocket>(); - new DiscoveryThread(socketLatch, uuid.toString(), timeout).start(); - new BluetoothListenerThread(socketLatch, ss).start(); - // Wait for an incoming or outgoing connection + // Create the background tasks + CompletionService<BluetoothSocket> complete = + new ExecutorCompletionService<BluetoothSocket>(ioExecutor); + List<Future<BluetoothSocket>> futures = + new ArrayList<Future<BluetoothSocket>>(); + if (alice) { + // Return the first connected socket + futures.add(complete.submit(new ListeningTask(ss))); + futures.add(complete.submit(new DiscoveryTask(uuid.toString()))); + } else { + // Return the first socket with readable data + futures.add(complete.submit(new ReadableTask( + new ListeningTask(ss)))); + futures.add(complete.submit(new ReadableTask( + new DiscoveryTask(uuid.toString())))); + } + BluetoothSocket chosen = null; try { - BluetoothSocket s = socketLatch.waitForReference(timeout); - if (s != null) return new DroidtoothTransportConnection(this, s); + Future<BluetoothSocket> f = complete.poll(timeout, MILLISECONDS); + if (f == null) return null; // No task completed within the timeout + chosen = f.get(); + return new DroidtoothTransportConnection(this, chosen); } catch (InterruptedException e) { - LOG.warning("Interrupted while exchanging invitations"); - Thread.currentThread().interrupt(); + LOG.info("Interrupted while waiting for connection"); + return null; + } catch (ExecutionException e) { + if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + return null; } finally { - // Closing the socket will terminate the listener thread + // Closing the socket will terminate the listener task tryToClose(ss); + closeSockets(futures, chosen); } - return null; + } + + private void closeSockets(final List<Future<BluetoothSocket>> futures, + final BluetoothSocket chosen) { + ioExecutor.execute(new Runnable() { + public void run() { + for (Future<BluetoothSocket> f : futures) { + try { + if (f.cancel(true)) { + LOG.info("Cancelled task"); + } else { + BluetoothSocket s = f.get(); + if (s != null && s != chosen) { + LOG.info("Closing unwanted socket"); + s.close(); + } + } + } catch (InterruptedException e) { + LOG.info("Interrupted while closing sockets"); + return; + } catch (ExecutionException e) { + if (LOG.isLoggable(INFO)) LOG.info(e.toString()); + } catch (IOException e) { + if (LOG.isLoggable(INFO)) LOG.info(e.toString()); + } + } + } + }); } private class BluetoothStateReceiver extends BroadcastReceiver { @@ -395,61 +439,37 @@ class DroidtoothPlugin implements DuplexPlugin { } } - private class DiscoveryThread extends Thread { + private class DiscoveryTask implements Callable<BluetoothSocket> { - private final LatchedReference<BluetoothSocket> socketLatch; private final String uuid; - private final long timeout; - private DiscoveryThread(LatchedReference<BluetoothSocket> socketLatch, - String uuid, long timeout) { - this.socketLatch = socketLatch; + private DiscoveryTask(String uuid) { this.uuid = uuid; - this.timeout = timeout; } @Override - public void run() { - long end = clock.currentTimeMillis() + timeout; - while (!finished(end)) { + public BluetoothSocket call() throws Exception { + // Repeat discovery until we connect or get interrupted + while (true) { // Discover nearby devices LOG.info("Discovering nearby devices"); - List<String> addresses; - try { - long now = clock.currentTimeMillis(); - addresses = discoverDevices(end - now); - } catch (InterruptedException e) { - LOG.warning("Interrupted while discovering devices"); - Thread.currentThread().interrupt(); - return; - } + List<String> addresses = discoverDevices(); if (addresses.isEmpty()) { LOG.info("No devices discovered"); continue; } // Connect to any device with the right UUID for (String address : addresses) { - if (finished(end)) return; BluetoothSocket s = connect(address, uuid); if (s != null) { LOG.info("Outgoing connection"); - if (!socketLatch.set(s)) { - LOG.info("Closing redundant connection"); - tryToClose(s); - } - return; + return s; } } } } - private boolean finished(long end) { - long now = clock.currentTimeMillis(); - return now >= end || !isRunning() || socketLatch.isSet(); - } - - private List<String> discoverDevices(long timeout) - throws InterruptedException { + private List<String> discoverDevices() throws InterruptedException { IntentFilter filter = new IntentFilter(); filter.addAction(FOUND); filter.addAction(DISCOVERY_FINISHED); @@ -457,7 +477,7 @@ class DroidtoothPlugin implements DuplexPlugin { appContext.registerReceiver(disco, filter); LOG.info("Starting discovery"); adapter.startDiscovery(); - return disco.waitForAddresses(timeout); + return disco.waitForAddresses(); } } @@ -481,38 +501,47 @@ class DroidtoothPlugin implements DuplexPlugin { } } - private List<String> waitForAddresses(long timeout) - throws InterruptedException { - finished.await(timeout, MILLISECONDS); + private List<String> waitForAddresses() throws InterruptedException { + finished.await(); + Collections.shuffle(addresses); return Collections.unmodifiableList(addresses); } } - private static class BluetoothListenerThread extends Thread { + private static class ListeningTask implements Callable<BluetoothSocket> { - private final LatchedReference<BluetoothSocket> socketLatch; private final BluetoothServerSocket serverSocket; - private BluetoothListenerThread( - LatchedReference<BluetoothSocket> socketLatch, - BluetoothServerSocket serverSocket) { - this.socketLatch = socketLatch; + private ListeningTask(BluetoothServerSocket serverSocket) { this.serverSocket = serverSocket; } @Override - public void run() { - try { - BluetoothSocket s = serverSocket.accept(); - LOG.info("Incoming connection"); - if (!socketLatch.set(s)) { - LOG.info("Closing redundant connection"); - s.close(); - } - } catch (IOException e) { - // This is expected when the socket is closed - if (LOG.isLoggable(INFO)) LOG.info(e.toString()); + public BluetoothSocket call() throws IOException { + BluetoothSocket s = serverSocket.accept(); + LOG.info("Incoming connection"); + return s; + } + } + + private static class ReadableTask implements Callable<BluetoothSocket> { + + private final Callable<BluetoothSocket> connectionTask; + + private ReadableTask(Callable<BluetoothSocket> connectionTask) { + this.connectionTask = connectionTask; + } + + @Override + public BluetoothSocket call() throws Exception { + BluetoothSocket s = connectionTask.call(); + InputStream in = s.getInputStream(); + while (in.available() == 0) { + LOG.info("Waiting for data"); + Thread.sleep(1000); } + LOG.info("Data available"); + return s; } } } diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java index 15e2fc0bacbc3cb0ab5a2e509b97efa5a83b043c..db89929a2282a62e493f0872218c8e9df4ee5143 100644 --- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java @@ -9,8 +9,6 @@ import org.briarproject.api.plugins.BackoffFactory; import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginFactory; -import org.briarproject.api.system.Clock; -import org.briarproject.system.SystemClock; import java.security.SecureRandom; import java.util.concurrent.Executor; @@ -27,7 +25,6 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory { private final Context appContext; private final SecureRandom secureRandom; private final BackoffFactory backoffFactory; - private final Clock clock; public DroidtoothPluginFactory(Executor ioExecutor, AndroidExecutor androidExecutor, Context appContext, @@ -37,7 +34,6 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory { this.appContext = appContext; this.secureRandom = secureRandom; this.backoffFactory = backoffFactory; - clock = new SystemClock(); } public TransportId getId() { @@ -48,6 +44,6 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); return new DroidtoothPlugin(ioExecutor, androidExecutor, appContext, - secureRandom, clock, backoff, callback, MAX_LATENCY); + secureRandom, backoff, callback, MAX_LATENCY); } } diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index d98c2134363e611077a4f84e34500aca9714af4e..01d10f91a8188d7e733492f70fdc6c4dafab8467 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -565,7 +565,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, } public DuplexTransportConnection createInvitationConnection(PseudoRandom r, - long timeout) { + long timeout, boolean alice) { throw new UnsupportedOperationException(); } diff --git a/briar-api/src/org/briarproject/api/plugins/duplex/DuplexPlugin.java b/briar-api/src/org/briarproject/api/plugins/duplex/DuplexPlugin.java index b64f4a1498e200ec61c537081cff3b2dd6289f42..519400b5639fb2cc598dad8f88b16b7433853feb 100644 --- a/briar-api/src/org/briarproject/api/plugins/duplex/DuplexPlugin.java +++ b/briar-api/src/org/briarproject/api/plugins/duplex/DuplexPlugin.java @@ -23,5 +23,5 @@ public interface DuplexPlugin extends Plugin { * time. */ DuplexTransportConnection createInvitationConnection(PseudoRandom r, - long timeout); + long timeout, boolean alice); } diff --git a/briar-core/src/org/briarproject/invitation/AliceConnector.java b/briar-core/src/org/briarproject/invitation/AliceConnector.java index 39945bf6b61a0585e003df8dc3073a238f6d10c1..210d173d9e6de69942a56c7a56b834134e1d4867 100644 --- a/briar-core/src/org/briarproject/invitation/AliceConnector.java +++ b/briar-core/src/org/briarproject/invitation/AliceConnector.java @@ -51,7 +51,7 @@ class AliceConnector extends Connector { @Override public void run() { // Create an incoming or outgoing connection - DuplexTransportConnection conn = createInvitationConnection(); + DuplexTransportConnection conn = createInvitationConnection(true); if (conn == null) return; if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected"); // Don't proceed with more than one connection diff --git a/briar-core/src/org/briarproject/invitation/BobConnector.java b/briar-core/src/org/briarproject/invitation/BobConnector.java index 53bdbe3e47b7d32afb2e36667ba65175f45fc91b..f2c6e2619673922d15a32db891563e5797f25d9d 100644 --- a/briar-core/src/org/briarproject/invitation/BobConnector.java +++ b/briar-core/src/org/briarproject/invitation/BobConnector.java @@ -51,7 +51,7 @@ class BobConnector extends Connector { @Override public void run() { // Create an incoming or outgoing connection - DuplexTransportConnection conn = createInvitationConnection(); + DuplexTransportConnection conn = createInvitationConnection(false); if (conn == null) return; if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected"); // Carry out the key agreement protocol diff --git a/briar-core/src/org/briarproject/invitation/Connector.java b/briar-core/src/org/briarproject/invitation/Connector.java index 6310c4f170ad3031faeecc00c80697f2b95932dc..2934a0716e10ae432b7f11b2c6d00a2e50720979 100644 --- a/briar-core/src/org/briarproject/invitation/Connector.java +++ b/briar-core/src/org/briarproject/invitation/Connector.java @@ -93,10 +93,12 @@ abstract class Connector extends Thread { messageDigest = crypto.getMessageDigest(); } - protected DuplexTransportConnection createInvitationConnection() { + protected DuplexTransportConnection createInvitationConnection( + boolean alice) { if (LOG.isLoggable(INFO)) LOG.info(pluginName + " creating invitation connection"); - return plugin.createInvitationConnection(random, CONNECTION_TIMEOUT); + return plugin.createInvitationConnection(random, CONNECTION_TIMEOUT, + alice); } protected void sendPublicKeyHash(BdfWriter w) throws IOException { diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java index 64f91c4d1aba73cc117984620541496b98190924..54376310a0c33bd0b4e4f665f503d1c1ca97b76e 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java @@ -246,7 +246,7 @@ abstract class TcpPlugin implements DuplexPlugin { } public DuplexTransportConnection createInvitationConnection(PseudoRandom r, - long timeout) { + long timeout, boolean alice) { throw new UnsupportedOperationException(); } diff --git a/briar-core/src/org/briarproject/util/LatchedReference.java b/briar-core/src/org/briarproject/util/LatchedReference.java deleted file mode 100644 index 43763bfa79f596901b5194c7dce53173b6ee8eea..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/util/LatchedReference.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.briarproject.util; - -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -public class LatchedReference<T> { - - private final CountDownLatch latch = new CountDownLatch(1); - private final AtomicReference<T> reference = new AtomicReference<T>(); - - public boolean isSet() { - return reference.get() != null; - } - - public boolean set(T t) { - if (t == null) throw new IllegalArgumentException(); - if (reference.compareAndSet(null, t)) { - latch.countDown(); - return true; - } - return false; - } - - public T waitForReference(long timeout) throws InterruptedException { - latch.await(timeout, MILLISECONDS); - return reference.get(); - } -} diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java index 786dcf22280a16ad044fb8ab366f13a88bb56852..669965f0d37a1d6e189ea66f7778c978aebf44dd 100644 --- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java @@ -8,18 +8,24 @@ import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.properties.TransportProperties; -import org.briarproject.api.system.Clock; -import org.briarproject.util.LatchedReference; import org.briarproject.util.OsUtils; import org.briarproject.util.StringUtils; import java.io.IOException; +import java.io.InputStream; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.logging.Logger; @@ -30,6 +36,7 @@ import javax.microedition.io.Connector; import javax.microedition.io.StreamConnection; import javax.microedition.io.StreamConnectionNotifier; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static javax.bluetooth.DiscoveryAgent.GIAC; @@ -45,7 +52,6 @@ class BluetoothPlugin implements DuplexPlugin { private final Executor ioExecutor; private final SecureRandom secureRandom; - private final Clock clock; private final Backoff backoff; private final DuplexPluginCallback callback; private final int maxLatency; @@ -55,11 +61,10 @@ class BluetoothPlugin implements DuplexPlugin { private volatile StreamConnectionNotifier socket = null; private volatile LocalDevice localDevice = null; - BluetoothPlugin(Executor ioExecutor, SecureRandom secureRandom, Clock clock, + BluetoothPlugin(Executor ioExecutor, SecureRandom secureRandom, Backoff backoff, DuplexPluginCallback callback, int maxLatency) { this.ioExecutor = ioExecutor; this.secureRandom = secureRandom; - this.clock = clock; this.backoff = backoff; this.callback = callback; this.maxLatency = maxLatency; @@ -246,7 +251,7 @@ class BluetoothPlugin implements DuplexPlugin { } public DuplexTransportConnection createInvitationConnection(PseudoRandom r, - long timeout) { + long timeout, boolean alice) { if (!running) return null; // Use the invitation codes to generate the UUID byte[] b = r.nextBytes(UUID_BYTES); @@ -266,23 +271,67 @@ class BluetoothPlugin implements DuplexPlugin { tryToClose(ss); return null; } - // Start the background threads - LatchedReference<StreamConnection> socketLatch = - new LatchedReference<StreamConnection>(); - new DiscoveryThread(socketLatch, uuid, timeout).start(); - new BluetoothListenerThread(socketLatch, ss).start(); - // Wait for an incoming or outgoing connection + // Create the background tasks + CompletionService<StreamConnection> complete = + new ExecutorCompletionService<StreamConnection>(ioExecutor); + List<Future<StreamConnection>> futures = + new ArrayList<Future<StreamConnection>>(); + if (alice) { + // Return the first connected socket + futures.add(complete.submit(new ListeningTask(ss))); + futures.add(complete.submit(new DiscoveryTask(uuid))); + } else { + // Return the first socket with readable data + futures.add(complete.submit(new ReadableTask( + new ListeningTask(ss)))); + futures.add(complete.submit(new ReadableTask( + new DiscoveryTask(uuid)))); + } + StreamConnection chosen = null; try { - StreamConnection s = socketLatch.waitForReference(timeout); - if (s != null) return new BluetoothTransportConnection(this, s); + Future<StreamConnection> f = complete.poll(timeout, MILLISECONDS); + if (f == null) return null; // No task completed within the timeout + chosen = f.get(); + return new BluetoothTransportConnection(this, chosen); } catch (InterruptedException e) { - LOG.warning("Interrupted while exchanging invitations"); - Thread.currentThread().interrupt(); + LOG.info("Interrupted while waiting for connection"); + return null; + } catch (ExecutionException e) { + if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + return null; } finally { - // Closing the socket will terminate the listener thread + // Closing the socket will terminate the listener task tryToClose(ss); + closeSockets(futures, chosen); } - return null; + } + + private void closeSockets(final List<Future<StreamConnection>> futures, + final StreamConnection chosen) { + ioExecutor.execute(new Runnable() { + public void run() { + for (Future<StreamConnection> f : futures) { + try { + if (f.cancel(true)) { + LOG.info("Cancelled task"); + } else { + StreamConnection s = f.get(); + if (s != null && s != chosen) { + LOG.info("Closing unwanted socket"); + s.close(); + } + } + } catch (InterruptedException e) { + LOG.info("Interrupted while closing sockets"); + return; + } catch (ExecutionException e) { + if (LOG.isLoggable(INFO)) LOG.info(e.toString()); + } catch (IOException e) { + if (LOG.isLoggable(INFO)) LOG.info(e.toString()); + } + } + } + }); } private void makeDeviceDiscoverable() { @@ -294,93 +343,74 @@ class BluetoothPlugin implements DuplexPlugin { } } - private class DiscoveryThread extends Thread { + private class DiscoveryTask implements Callable<StreamConnection> { - private final LatchedReference<StreamConnection> socketLatch; private final String uuid; - private final long timeout; - private DiscoveryThread(LatchedReference<StreamConnection> socketLatch, - String uuid, long timeout) { - this.socketLatch = socketLatch; + private DiscoveryTask(String uuid) { this.uuid = uuid; - this.timeout = timeout; } @Override - public void run() { + public StreamConnection call() throws Exception { + // Repeat discovery until we connect or get interrupted DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent(); - long now = clock.currentTimeMillis(); - long end = now + timeout; - while (now < end && running && !socketLatch.isSet()) { - if (!discoverySemaphore.tryAcquire()) { - LOG.info("Another device discovery is in progress"); - return; - } + while (true) { + if (!discoverySemaphore.tryAcquire()) + throw new Exception("Discovery is already in progress"); try { InvitationListener listener = new InvitationListener(discoveryAgent, uuid); discoveryAgent.startInquiry(GIAC, listener); String url = listener.waitForUrl(); - if (url == null) continue; - StreamConnection s = connect(url); - if (s == null) continue; - LOG.info("Outgoing connection"); - if (!socketLatch.set(s)) { - LOG.info("Closing redundant connection"); - tryToClose(s); + if (url != null) { + StreamConnection s = connect(url); + if (s != null) { + LOG.info("Outgoing connection"); + return s; + } } - return; - } catch (BluetoothStateException e) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - return; - } catch (InterruptedException e) { - LOG.warning("Interrupted while waiting for URL"); - Thread.currentThread().interrupt(); - return; } finally { discoverySemaphore.release(); } } } - - private void tryToClose(StreamConnection s) { - try { - if (s != null) s.close(); - } catch (IOException e) { - if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } } - private static class BluetoothListenerThread extends Thread { + private static class ListeningTask implements Callable<StreamConnection> { - private final LatchedReference<StreamConnection> socketLatch; private final StreamConnectionNotifier serverSocket; - private BluetoothListenerThread( - LatchedReference<StreamConnection> socketLatch, - StreamConnectionNotifier serverSocket) { - this.socketLatch = socketLatch; + private ListeningTask(StreamConnectionNotifier serverSocket) { this.serverSocket = serverSocket; } @Override - public void run() { - LOG.info("Listening for invitation connections"); - // Listen until a connection is received or the socket is closed - try { - StreamConnection s = serverSocket.acceptAndOpen(); - LOG.info("Incoming connection"); - if (!socketLatch.set(s)) { - LOG.info("Closing redundant connection"); - s.close(); - } - } catch (IOException e) { - // This is expected when the socket is closed - if (LOG.isLoggable(INFO)) LOG.info(e.toString()); + public StreamConnection call() throws Exception { + StreamConnection s = serverSocket.acceptAndOpen(); + LOG.info("Incoming connection"); + return s; + } + } + + private static class ReadableTask implements Callable<StreamConnection> { + + private final Callable<StreamConnection> connectionTask; + + private ReadableTask(Callable<StreamConnection> connectionTask) { + this.connectionTask = connectionTask; + } + + @Override + public StreamConnection call() throws Exception { + StreamConnection s = connectionTask.call(); + InputStream in = s.openInputStream(); + while (in.available() == 0) { + LOG.info("Waiting for data"); + Thread.sleep(1000); } + LOG.info("Data available"); + return s; } } } diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java index 5259ece49b8d7f96143b9ed719f87adfaa8771cc..2d7f156e22d861f777808cc98ba0fbbb69224d13 100644 --- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java +++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java @@ -6,8 +6,6 @@ import org.briarproject.api.plugins.BackoffFactory; import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginFactory; -import org.briarproject.api.system.Clock; -import org.briarproject.system.SystemClock; import java.security.SecureRandom; import java.util.concurrent.Executor; @@ -22,14 +20,12 @@ public class BluetoothPluginFactory implements DuplexPluginFactory { private final Executor ioExecutor; private final SecureRandom secureRandom; private final BackoffFactory backoffFactory; - private final Clock clock; public BluetoothPluginFactory(Executor ioExecutor, SecureRandom secureRandom, BackoffFactory backoffFactory) { this.ioExecutor = ioExecutor; this.secureRandom = secureRandom; this.backoffFactory = backoffFactory; - clock = new SystemClock(); } public TransportId getId() { @@ -39,7 +35,7 @@ public class BluetoothPluginFactory implements DuplexPluginFactory { public DuplexPlugin createPlugin(DuplexPluginCallback callback) { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); - return new BluetoothPlugin(ioExecutor, secureRandom, clock, backoff, - callback, MAX_LATENCY); + return new BluetoothPlugin(ioExecutor, secureRandom, backoff, callback, + MAX_LATENCY); } } diff --git a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java index 0245ad17d37fb8ba7f6ca5b0a8158d18e11ad851..954529a5e8221fa1341f7b0a7b198409e33efa19 100644 --- a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java @@ -154,7 +154,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } public DuplexTransportConnection createInvitationConnection(PseudoRandom r, - long timeout) { + long timeout, boolean alice) { throw new UnsupportedOperationException(); } diff --git a/briar-tests/src/org/briarproject/plugins/DuplexClientTest.java b/briar-tests/src/org/briarproject/plugins/DuplexClientTest.java deleted file mode 100644 index b241ec4e8fcdb0b99aacd003e5e4263e5e8514cb..0000000000000000000000000000000000000000 --- a/briar-tests/src/org/briarproject/plugins/DuplexClientTest.java +++ /dev/null @@ -1,111 +0,0 @@ -package org.briarproject.plugins; - -import org.briarproject.api.contact.ContactId; -import org.briarproject.api.crypto.PseudoRandom; -import org.briarproject.api.plugins.duplex.DuplexPluginCallback; -import org.briarproject.api.plugins.duplex.DuplexTransportConnection; -import org.briarproject.api.properties.TransportProperties; -import org.briarproject.api.settings.Settings; - -import java.io.IOException; -import java.util.Map; - -import static org.briarproject.api.invitation.InvitationConstants.CONNECTION_TIMEOUT; - -public abstract class DuplexClientTest extends DuplexTest { - - protected ClientCallback callback = null; - - protected void run() throws IOException { - assert plugin != null; - // Start the plugin - System.out.println("Starting plugin"); - if (!plugin.start()) { - System.out.println("Plugin failed to start"); - return; - } - try { - // Try to connect to the server - System.out.println("Creating connection"); - DuplexTransportConnection d = plugin.createConnection(contactId); - if (d == null) { - System.out.println("Connection failed"); - return; - } else { - System.out.println("Connection created"); - receiveChallengeSendResponse(d); - } - if (!plugin.supportsInvitations()) { - System.out.println("Skipping invitation test"); - return; - } - // Try to create an invitation connection - System.out.println("Creating invitation connection"); - PseudoRandom r = getPseudoRandom(123); - d = plugin.createInvitationConnection(r, CONNECTION_TIMEOUT); - if (d == null) { - System.out.println("Connection failed"); - } else { - System.out.println("Connection created"); - sendChallengeReceiveResponse(d); - } - } finally { - // Stop the plugin - System.out.println("Stopping plugin"); - plugin.stop(); - } - } - - protected static class ClientCallback implements DuplexPluginCallback { - - private Settings settings = null; - private TransportProperties local = null; - private Map<ContactId, TransportProperties> remote = null; - - public ClientCallback(Settings settings, TransportProperties local, - Map<ContactId, TransportProperties> remote) { - this.settings = settings; - this.local = local; - this.remote = remote; - } - - public Settings getSettings() { - return settings; - } - - public TransportProperties getLocalProperties() { - return local; - } - - public Map<ContactId, TransportProperties> getRemoteProperties() { - return remote; - } - - public void mergeSettings(Settings s) { - settings = s; - } - - public void mergeLocalProperties(TransportProperties p) { - local = p; - } - - public int showChoice(String[] options, String... message) { - return -1; - } - - public boolean showConfirmationMessage(String... message) { - return false; - } - - public void showMessage(String... message) {} - - public void incomingConnectionCreated(DuplexTransportConnection d) {} - - public void outgoingConnectionCreated(ContactId contactId, - DuplexTransportConnection d) {} - - public void transportEnabled() {} - - public void transportDisabled() {} - } -} diff --git a/briar-tests/src/org/briarproject/plugins/DuplexServerTest.java b/briar-tests/src/org/briarproject/plugins/DuplexServerTest.java deleted file mode 100644 index ab60c9272549849861c9ddefe754ff497d3a79fc..0000000000000000000000000000000000000000 --- a/briar-tests/src/org/briarproject/plugins/DuplexServerTest.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.briarproject.plugins; - -import org.briarproject.api.contact.ContactId; -import org.briarproject.api.plugins.duplex.DuplexPluginCallback; -import org.briarproject.api.plugins.duplex.DuplexTransportConnection; -import org.briarproject.api.properties.TransportProperties; -import org.briarproject.api.settings.Settings; - -import java.util.Map; -import java.util.concurrent.CountDownLatch; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.briarproject.api.invitation.InvitationConstants.CONNECTION_TIMEOUT; - -public abstract class DuplexServerTest extends DuplexTest { - - protected ServerCallback callback = null; - - protected void run() throws Exception { - assert callback != null; - assert plugin != null; - // Start the plugin - System.out.println("Starting plugin"); - if (!plugin.start()) { - System.out.println("Plugin failed to start"); - return; - } - try { - // Wait for a connection - System.out.println("Waiting for connection"); - if (!callback.latch.await(120, SECONDS)) { - System.out.println("No connection received"); - return; - } - if (!plugin.supportsInvitations()) { - System.out.println("Skipping invitation test"); - return; - } - // Try to create an invitation connection - System.out.println("Creating invitation connection"); - DuplexTransportConnection d = plugin.createInvitationConnection( - getPseudoRandom(123), CONNECTION_TIMEOUT); - if (d == null) { - System.out.println("Connection failed"); - } else { - System.out.println("Connection created"); - receiveChallengeSendResponse(d); - } - } finally { - // Stop the plugin - System.out.println("Stopping plugin"); - plugin.stop(); - } - } - - protected class ServerCallback implements DuplexPluginCallback { - - private final CountDownLatch latch = new CountDownLatch(1); - - private Settings settings; - private TransportProperties local; - private Map<ContactId, TransportProperties> remote; - - public ServerCallback(Settings settings, TransportProperties local, - Map<ContactId, TransportProperties> remote) { - this.settings = settings; - this.local = local; - this.remote = remote; - } - - public Settings getSettings() { - return settings; - } - - public TransportProperties getLocalProperties() { - return local; - } - - public Map<ContactId, TransportProperties> getRemoteProperties() { - return remote; - } - - public void mergeSettings(Settings s) { - settings = s; - } - - public void mergeLocalProperties(TransportProperties p) { - local = p; - } - - public int showChoice(String[] options, String... message) { - return -1; - } - - public boolean showConfirmationMessage(String... message) { - return false; - } - - public void showMessage(String... message) {} - - public void incomingConnectionCreated(DuplexTransportConnection d) { - System.out.println("Connection received"); - sendChallengeReceiveResponse(d); - latch.countDown(); - } - - public void outgoingConnectionCreated(ContactId c, - DuplexTransportConnection d) {} - - public void transportEnabled() {} - - public void transportDisabled() {} - } -} diff --git a/briar-tests/src/org/briarproject/plugins/DuplexTest.java b/briar-tests/src/org/briarproject/plugins/DuplexTest.java deleted file mode 100644 index d20418a3f52cc06a5176522200a69d5fc675566d..0000000000000000000000000000000000000000 --- a/briar-tests/src/org/briarproject/plugins/DuplexTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.briarproject.plugins; - -import org.briarproject.api.contact.ContactId; -import org.briarproject.api.crypto.PseudoRandom; -import org.briarproject.api.plugins.TransportConnectionReader; -import org.briarproject.api.plugins.TransportConnectionWriter; -import org.briarproject.api.plugins.duplex.DuplexPlugin; -import org.briarproject.api.plugins.duplex.DuplexTransportConnection; - -import java.io.IOException; -import java.io.PrintStream; -import java.util.Random; -import java.util.Scanner; - -abstract class DuplexTest { - - protected static final String CHALLENGE = "Carrots!"; - protected static final String RESPONSE = "Potatoes!"; - - protected final ContactId contactId = new ContactId(234); - - protected DuplexPlugin plugin = null; - - protected void sendChallengeReceiveResponse(DuplexTransportConnection d) { - assert plugin != null; - TransportConnectionReader r = d.getReader(); - TransportConnectionWriter w = d.getWriter(); - try { - PrintStream out = new PrintStream(w.getOutputStream()); - out.println(CHALLENGE); - out.flush(); - System.out.println("Sent challenge: " + CHALLENGE); - Scanner in = new Scanner(r.getInputStream()); - if (in.hasNextLine()) { - String response = in.nextLine(); - System.out.println("Received response: " + response); - if (RESPONSE.equals(response)) { - System.out.println("Correct response"); - } else { - System.out.println("Incorrect response"); - } - } else { - System.out.println("No response"); - } - r.dispose(false, true); - w.dispose(false); - } catch (IOException e) { - e.printStackTrace(); - try { - r.dispose(true, true); - w.dispose(true); - } catch (IOException e1) { - e1.printStackTrace(); - } - } - } - - protected void receiveChallengeSendResponse(DuplexTransportConnection d) { - assert plugin != null; - TransportConnectionReader r = d.getReader(); - TransportConnectionWriter w = d.getWriter(); - try { - Scanner in = new Scanner(r.getInputStream()); - if (in.hasNextLine()) { - String challenge = in.nextLine(); - System.out.println("Received challenge: " + challenge); - if (CHALLENGE.equals(challenge)) { - - PrintStream out = new PrintStream(w.getOutputStream()); - out.println(RESPONSE); - out.flush(); - System.out.println("Sent response: " + RESPONSE); - } else { - System.out.println("Incorrect challenge"); - } - } else { - System.out.println("No challenge"); - } - r.dispose(false, true); - w.dispose(false); - } catch (IOException e) { - e.printStackTrace(); - try { - r.dispose(true, true); - w.dispose(true); - } catch (IOException e1) { - e1.printStackTrace(); - } - } - } - - protected PseudoRandom getPseudoRandom(int seed) { - final Random random = new Random(seed); - return new PseudoRandom() { - public byte[] nextBytes(int bytes) { - byte[] b = new byte[bytes]; - random.nextBytes(b); - return b; - } - }; - } -}