mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
1 Commits
user-testi
...
831_refact
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bb55d2580 |
@@ -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,42 +233,20 @@ 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 void run() {
|
||||||
@Override
|
if (!running) return;
|
||||||
public int getPollingInterval() {
|
BluetoothSocket s = connect(address, uuid);
|
||||||
return backoff.getPollingInterval();
|
if (s != null) {
|
||||||
}
|
backoff.reset();
|
||||||
|
callback.outgoingConnectionCreated(c, wrapSocket(s));
|
||||||
@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
|
|
||||||
public void run() {
|
|
||||||
if (!running) return;
|
|
||||||
BluetoothSocket s = connect(address, uuid);
|
|
||||||
if (s != null) {
|
|
||||||
backoff.reset();
|
|
||||||
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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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,54 +159,33 @@ 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() {
|
@Override
|
||||||
return true;
|
public void run() {
|
||||||
}
|
if (!running) return;
|
||||||
|
StreamConnection s = connect(makeUrl(address, uuid));
|
||||||
@Override
|
if (s != null) {
|
||||||
public int getPollingInterval() {
|
backoff.reset();
|
||||||
return backoff.getPollingInterval();
|
callback.outgoingConnectionCreated(c, wrapSocket(s));
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
|
||||||
public void run() {
|
|
||||||
if (!running) return;
|
|
||||||
StreamConnection s = connect(makeUrl(address, uuid));
|
|
||||||
if (s != null) {
|
|
||||||
backoff.reset();
|
|
||||||
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;
|
String url = makeUrl(address, uuid);
|
||||||
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);
|
|
||||||
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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user