Compare commits

...

1 Commits

Author SHA1 Message Date
Santiago Torres
8bb55d2580 WIP: BluetoothPlugin: adds abstract superclass
The droidtooth plugin and bluetooth plugins share a lot of common code.
Add an abstract superclass that shares the common code between both
classes.
2017-05-01 01:16:38 -04:00
3 changed files with 263 additions and 290 deletions

View File

@@ -19,8 +19,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.AbstractBluetoothPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
@@ -75,7 +74,8 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class DroidtoothPlugin implements DuplexPlugin { class DroidtoothPlugin<C, S>
extends AbstractBluetoothPlugin<C, S>{
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(DroidtoothPlugin.class.getName()); Logger.getLogger(DroidtoothPlugin.class.getName());
@@ -84,16 +84,10 @@ class DroidtoothPlugin implements DuplexPlugin {
private static final String DISCOVERY_FINISHED = private static final String DISCOVERY_FINISHED =
"android.bluetooth.adapter.action.DISCOVERY_FINISHED"; "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
private final Executor ioExecutor;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final Context appContext; private final Context appContext;
private final SecureRandom secureRandom;
private final Backoff backoff;
private final DuplexPluginCallback callback;
private final int maxLatency;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false;
private volatile boolean wasEnabledByUs = false; private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null; private volatile BluetoothStateReceiver receiver = null;
private volatile BluetoothServerSocket socket = null; private volatile BluetoothServerSocket socket = null;
@@ -104,29 +98,15 @@ class DroidtoothPlugin implements DuplexPlugin {
DroidtoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor, DroidtoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Backoff backoff, Context appContext, SecureRandom secureRandom, Backoff backoff,
DuplexPluginCallback callback, int maxLatency) { DuplexPluginCallback callback, int maxLatency) {
this.ioExecutor = ioExecutor;
super(ioExecutor, secureRandom, backoff, maxLatency, callback);
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.appContext = appContext; this.appContext = appContext;
this.secureRandom = secureRandom;
this.backoff = backoff;
this.callback = callback;
this.maxLatency = maxLatency;
} }
@Override @Override
public TransportId getId() { protected void close(S ss) throws IOException {
return ID; ((BluetoothServerSocket)ss).close();
}
@Override
public int getMaxLatency() {
return maxLatency;
}
@Override
public int getMaxIdleTime() {
// Bluetooth detects dead connections so we don't need keepalives
return Integer.MAX_VALUE;
} }
@Override @Override
@@ -194,14 +174,14 @@ class DroidtoothPlugin implements DuplexPlugin {
BluetoothServerSocket ss; BluetoothServerSocket ss;
try { try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord( ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", getUuid()); "RFCOMM", UUID.fromString(getUuid()));
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e); LOG.log(WARNING, e.toString(), e);
return; return;
} }
if (!isRunning()) { if (!isRunning()) {
tryToClose(ss); tryToClose((S)ss);
return; return;
} }
LOG.info("Socket bound"); LOG.info("Socket bound");
@@ -213,29 +193,6 @@ class DroidtoothPlugin implements DuplexPlugin {
}); });
} }
private UUID getUuid() {
String uuid = callback.getLocalProperties().get(PROP_UUID);
if (uuid == null) {
byte[] random = new byte[UUID_BYTES];
secureRandom.nextBytes(random);
uuid = UUID.nameUUIDFromBytes(random).toString();
TransportProperties p = new TransportProperties();
p.put(PROP_UUID, uuid);
callback.mergeLocalProperties(p);
}
return UUID.fromString(uuid);
}
private void tryToClose(@Nullable BluetoothServerSocket ss) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} finally {
callback.transportDisabled();
}
}
private void acceptContactConnections() { private void acceptContactConnections() {
while (isRunning()) { while (isRunning()) {
BluetoothSocket s; BluetoothSocket s;
@@ -261,9 +218,9 @@ class DroidtoothPlugin implements DuplexPlugin {
@Override @Override
public void stop() { public void stop() {
running = false; this.running = false;
if (receiver != null) appContext.unregisterReceiver(receiver); if (receiver != null) appContext.unregisterReceiver(receiver);
tryToClose(socket); tryToClose((S)socket);
// Disable Bluetooth if we enabled it and it's still enabled // Disable Bluetooth if we enabled it and it's still enabled
if (wasEnabledByUs && adapter.isEnabled()) { if (wasEnabledByUs && adapter.isEnabled()) {
if (adapter.disable()) LOG.info("Disabling Bluetooth"); if (adapter.disable()) LOG.info("Disabling Bluetooth");
@@ -276,31 +233,9 @@ class DroidtoothPlugin implements DuplexPlugin {
return running && adapter != null && adapter.isEnabled(); return running && adapter != null && adapter.isEnabled();
} }
@Override protected Runnable returnPollRunnable(final String address, final String uuid,
public boolean shouldPoll() { final ContactId c) {
return true; return new Runnable() {
}
@Override
public int getPollingInterval() {
return backoff.getPollingInterval();
}
@Override
public void poll(Collection<ContactId> connected) {
if (!isRunning()) return;
backoff.increment();
// Try to connect to known devices in parallel
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
final ContactId c = e.getKey();
if (connected.contains(c)) continue;
final String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
final String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
ioExecutor.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
if (!running) return; if (!running) return;
@@ -310,8 +245,8 @@ class DroidtoothPlugin implements DuplexPlugin {
callback.outgoingConnectionCreated(c, wrapSocket(s)); callback.outgoingConnectionCreated(c, wrapSocket(s));
} }
} }
});
} };
} }
@Nullable @Nullable
@@ -347,37 +282,17 @@ class DroidtoothPlugin implements DuplexPlugin {
LOG.info("Failed to connect to " + scrubMacAddress(address) LOG.info("Failed to connect to " + scrubMacAddress(address)
+ ": " + e); + ": " + e);
} }
tryToClose(s); tryToClose((S)s);
return null; return null;
} }
} }
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override public DuplexTransportConnection connectToAddress(String address, String uuid) {
public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null;
TransportProperties p = callback.getRemoteProperties().get(c);
if (p == null) return null;
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) return null;
BluetoothSocket s = connect(address, uuid); BluetoothSocket s = connect(address, uuid);
if (s == null) return null; return s == null ? null : wrapSocket(s);
return new DroidtoothTransportConnection(this, s);
} }
@Override
public boolean supportsInvitations() {
return true;
}
@Override @Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r, public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
@@ -426,7 +341,7 @@ class DroidtoothPlugin implements DuplexPlugin {
return null; return null;
} finally { } finally {
// Closing the socket will terminate the listener task // Closing the socket will terminate the listener task
tryToClose(ss); tryToClose((S)ss);
closeSockets(futures, chosen); closeSockets(futures, chosen);
} }
} }
@@ -458,11 +373,6 @@ class DroidtoothPlugin implements DuplexPlugin {
}); });
} }
@Override
public boolean supportsKeyAgreement() {
return true;
}
@Override @Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
if (!isRunning()) return null; if (!isRunning()) return null;
@@ -487,31 +397,6 @@ class DroidtoothPlugin implements DuplexPlugin {
return new BluetoothKeyAgreementListener(descriptor, ss); return new BluetoothKeyAgreementListener(descriptor, ss);
} }
@Override
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor, long timeout) {
if (!isRunning()) return null;
String address;
try {
address = parseAddress(descriptor);
} catch (FormatException e) {
LOG.info("Invalid address in key agreement descriptor");
return null;
}
// No truncation necessary because COMMIT_LENGTH = 16
UUID uuid = UUID.nameUUIDFromBytes(commitment);
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
BluetoothSocket s = connect(address, uuid.toString());
if (s == null) return null;
return new DroidtoothTransportConnection(this, s);
}
private String parseAddress(BdfList descriptor) throws FormatException {
byte[] mac = descriptor.getRaw(1);
if (mac.length != 6) throw new FormatException();
return StringUtils.macToString(mac);
}
private class BluetoothStateReceiver extends BroadcastReceiver { private class BluetoothStateReceiver extends BroadcastReceiver {
@@ -523,7 +408,7 @@ class DroidtoothPlugin implements DuplexPlugin {
bind(); bind();
} else if (state == STATE_OFF) { } else if (state == STATE_OFF) {
LOG.info("Bluetooth disabled"); LOG.info("Bluetooth disabled");
tryToClose(socket); tryToClose((S)socket);
} }
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0); int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
if (scanMode == SCAN_MODE_NONE) { if (scanMode == SCAN_MODE_NONE) {

View File

@@ -0,0 +1,190 @@
package org.briarproject.bramble.api.plugin.duplex;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.util.StringUtils;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/**
* Created by Santiago Torres-Arias on 1/10/17.
*/
public abstract class AbstractBluetoothPlugin<C, S> implements DuplexPlugin {
private static final Logger LOG =
Logger.getLogger("Halp");
protected final Executor ioExecutor;
protected final SecureRandom secureRandom;
protected final Backoff backoff;
protected final int maxLatency;
protected final DuplexPluginCallback callback;
protected final S ss = null;
protected volatile boolean running = false;
protected Runnable pollRunnable = null;
public AbstractBluetoothPlugin(Executor ioExecutor,
SecureRandom secureRandom,
Backoff backoff, int maxLatency,
DuplexPluginCallback callback) {
this.ioExecutor = ioExecutor;
this.secureRandom = secureRandom;
this.backoff = backoff;
this.maxLatency = maxLatency;
this.callback = callback;
}
@Override
public boolean supportsInvitations() {
return true;
}
@Override
public boolean supportsKeyAgreement() {
return true;
}
@Override
public boolean isRunning() {
return running;
}
@Override
public boolean shouldPoll() {
return true;
}
@Override
public int getPollingInterval() {
return backoff.getPollingInterval();
}
protected String getUuid() {
String uuid = callback.getLocalProperties().get(PROP_UUID);
if (uuid == null) {
byte[] random = new byte[UUID_BYTES];
secureRandom.nextBytes(random);
uuid = UUID.nameUUIDFromBytes(random).toString();
TransportProperties p = new TransportProperties();
p.put(PROP_UUID, uuid);
callback.mergeLocalProperties(p);
}
return uuid;
}
@Override
public TransportId getId() {
return ID;
}
@Override
public int getMaxLatency() {
return maxLatency;
}
@Override
public int getMaxIdleTime() {
// Bluetooth detects dead connections so we don't need keepalives
return Integer.MAX_VALUE;
}
protected String parseAddress(BdfList descriptor) throws FormatException {
byte[] mac = descriptor.getRaw(1);
if (mac.length != 6) throw new FormatException();
return StringUtils.macToString(mac);
}
protected void tryToClose(@Nullable S ss) {
try {
if (ss != null) close(ss);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} finally {
callback.transportDisabled();
}
}
protected abstract void close(S ss) throws IOException;
public void stop() {
running = false;
tryToClose(ss);
}
@Override
public void poll(final Collection<ContactId> connected) {
if (!running) return;
backoff.increment();
// Try to connect to known devices in parallel
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
for (Map.Entry<ContactId, TransportProperties> e : remote.entrySet()) {
final ContactId c = e.getKey();
if (connected.contains(c)) continue;
final String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
final String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
ioExecutor.execute(returnPollRunnable(address,uuid, c));
}
}
protected abstract Runnable returnPollRunnable(String address, String uuid,
ContactId c);
@Override
public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null;
TransportProperties p = callback.getRemoteProperties().get(c);
if (p == null) return null;
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 connectToAddress(address, uuid);
}
protected abstract DuplexTransportConnection connectToAddress(String address, String uuid);
@Override
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor, long timeout) {
if (!isRunning()) return null;
String address;
try {
address = parseAddress(descriptor);
} catch (FormatException e) {
LOG.info("Invalid address in key agreement descriptor");
return null;
}
// No truncation necessary because COMMIT_LENGTH = 16
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
return connectToAddress(address, uuid);
}
}

View File

@@ -10,14 +10,14 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.AbstractBluetoothPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.util.OsUtils; import org.briarproject.bramble.util.OsUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import java.io.IOError;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.SecureRandom; import java.security.SecureRandom;
@@ -57,46 +57,22 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class BluetoothPlugin implements DuplexPlugin { class BluetoothPlugin<C, S> extends AbstractBluetoothPlugin<C, S> {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(BluetoothPlugin.class.getName()); Logger.getLogger(BluetoothPlugin.class.getName());
private final Executor ioExecutor;
private final SecureRandom secureRandom;
private final Backoff backoff;
private final DuplexPluginCallback callback;
private final int maxLatency;
private final Semaphore discoverySemaphore = new Semaphore(1); private final Semaphore discoverySemaphore = new Semaphore(1);
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false;
private volatile StreamConnectionNotifier socket = null; private volatile StreamConnectionNotifier socket = null;
private volatile LocalDevice localDevice = null; private volatile LocalDevice localDevice = null;
BluetoothPlugin(Executor ioExecutor, SecureRandom secureRandom, BluetoothPlugin(Executor ioExecutor,
Backoff backoff, DuplexPluginCallback callback, int maxLatency) { SecureRandom secureRandom,
this.ioExecutor = ioExecutor; Backoff backoff, int maxLatency,
this.secureRandom = secureRandom; DuplexPluginCallback callback) {
this.backoff = backoff; super(ioExecutor, secureRandom, backoff, maxLatency, callback);
this.callback = callback;
this.maxLatency = maxLatency;
}
@Override
public TransportId getId() {
return ID;
}
@Override
public int getMaxLatency() {
return maxLatency;
}
@Override
public int getMaxIdleTime() {
// Bluetooth detects dead connections so we don't need keepalives
return Integer.MAX_VALUE;
} }
@Override @Override
@@ -139,7 +115,7 @@ class BluetoothPlugin implements DuplexPlugin {
return; return;
} }
if (!running) { if (!running) {
tryToClose(ss); tryToClose((S)ss);
return; return;
} }
socket = ss; socket = ss;
@@ -153,29 +129,16 @@ class BluetoothPlugin implements DuplexPlugin {
private String makeUrl(String address, String uuid) { private String makeUrl(String address, String uuid) {
return "btspp://" + address + ":" + uuid + ";name=RFCOMM"; return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
} }
//
private String getUuid() { // private void tryToClose(@Nullable StreamConnectionNotifier ss) {
String uuid = callback.getLocalProperties().get(PROP_UUID); // try {
if (uuid == null) { // if (ss != null) ss.close();
byte[] random = new byte[UUID_BYTES]; // } catch (IOException e) {
secureRandom.nextBytes(random); // if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
uuid = UUID.nameUUIDFromBytes(random).toString(); // } finally {
TransportProperties p = new TransportProperties(); // callback.transportDisabled();
p.put(PROP_UUID, uuid); // }
callback.mergeLocalProperties(p); // }
}
return uuid;
}
private void tryToClose(@Nullable StreamConnectionNotifier ss) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} finally {
callback.transportDisabled();
}
}
private void acceptContactConnections(StreamConnectionNotifier ss) { private void acceptContactConnections(StreamConnectionNotifier ss) {
while (true) { while (true) {
@@ -196,43 +159,23 @@ class BluetoothPlugin implements DuplexPlugin {
private DuplexTransportConnection wrapSocket(StreamConnection s) { private DuplexTransportConnection wrapSocket(StreamConnection s) {
return new BluetoothTransportConnection(this, s); return new BluetoothTransportConnection(this, s);
} }
//
// @Override
// public void stop() {
// running = false;
// tryToClose(socket);
// }
@Override @Override
public void stop() { protected void close(S ss) throws IOException {
running = false; ((StreamConnection)ss).close();
tryToClose(socket);
} }
@Override @Override
public boolean isRunning() { public Runnable returnPollRunnable(final String address, final String uuid,
return running; final ContactId c) {
}
@Override return new Runnable() {
public boolean shouldPoll() {
return true;
}
@Override
public int getPollingInterval() {
return backoff.getPollingInterval();
}
@Override
public void poll(final Collection<ContactId> connected) {
if (!running) return;
backoff.increment();
// Try to connect to known devices in parallel
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
final ContactId c = e.getKey();
if (connected.contains(c)) continue;
final String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
final String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
ioExecutor.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
if (!running) return; if (!running) return;
@@ -242,8 +185,7 @@ class BluetoothPlugin implements DuplexPlugin {
callback.outgoingConnectionCreated(c, wrapSocket(s)); callback.outgoingConnectionCreated(c, wrapSocket(s));
} }
} }
}); };
}
} }
private StreamConnection connect(String url) { private StreamConnection connect(String url) {
@@ -259,25 +201,13 @@ class BluetoothPlugin implements DuplexPlugin {
} }
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { protected DuplexTransportConnection connectToAddress(String address, String uuid) {
if (!running) return null;
TransportProperties p = callback.getRemoteProperties().get(c);
if (p == null) return null;
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) return null;
String url = makeUrl(address, uuid); String url = makeUrl(address, uuid);
StreamConnection s = connect(url); StreamConnection s = connect(url);
if (s == null) return null; if (s == null) return null;
return new BluetoothTransportConnection(this, s); return new BluetoothTransportConnection(this, s);
} }
@Override
public boolean supportsInvitations() {
return true;
}
@Override @Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r, public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) { long timeout, boolean alice) {
@@ -297,7 +227,7 @@ class BluetoothPlugin implements DuplexPlugin {
return null; return null;
} }
if (!running) { if (!running) {
tryToClose(ss); tryToClose((S)ss);
return null; return null;
} }
// Create the background tasks // Create the background tasks
@@ -330,7 +260,7 @@ class BluetoothPlugin implements DuplexPlugin {
return null; return null;
} finally { } finally {
// Closing the socket will terminate the listener task // Closing the socket will terminate the listener task
tryToClose(ss); tryToClose((S)ss);
closeSockets(futures, chosen); closeSockets(futures, chosen);
} }
} }
@@ -362,11 +292,6 @@ class BluetoothPlugin implements DuplexPlugin {
}); });
} }
@Override
public boolean supportsKeyAgreement() {
return true;
}
@Override @Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
if (!running) return null; if (!running) return null;
@@ -385,7 +310,7 @@ class BluetoothPlugin implements DuplexPlugin {
return null; return null;
} }
if (!running) { if (!running) {
tryToClose(ss); tryToClose((S)ss);
return null; return null;
} }
BdfList descriptor = new BdfList(); BdfList descriptor = new BdfList();
@@ -395,33 +320,6 @@ class BluetoothPlugin implements DuplexPlugin {
return new BluetoothKeyAgreementListener(descriptor, ss); return new BluetoothKeyAgreementListener(descriptor, ss);
} }
@Override
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor, long timeout) {
if (!isRunning()) return null;
String address;
try {
address = parseAddress(descriptor);
} catch (FormatException e) {
LOG.info("Invalid address in key agreement descriptor");
return null;
}
// No truncation necessary because COMMIT_LENGTH = 16
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
String url = makeUrl(address, uuid);
StreamConnection s = connect(url);
if (s == null) return null;
return new BluetoothTransportConnection(this, s);
}
private String parseAddress(BdfList descriptor) throws FormatException {
byte[] mac = descriptor.getRaw(1);
if (mac.length != 6) throw new FormatException();
return StringUtils.macToString(mac);
}
private void makeDeviceDiscoverable() { private void makeDeviceDiscoverable() {
// Try to make the device discoverable (requires root on Linux) // Try to make the device discoverable (requires root on Linux)
try { try {