mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 14:19:53 +01:00
Compare commits
1 Commits
1712-write
...
recently-o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1cda4fb1e |
@@ -9,7 +9,6 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
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;
|
||||||
@@ -77,12 +76,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
private volatile BluetoothAdapter adapter = null;
|
private volatile BluetoothAdapter adapter = null;
|
||||||
|
|
||||||
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
||||||
TimeoutMonitor timeoutMonitor, Executor ioExecutor,
|
Executor ioExecutor, AndroidExecutor androidExecutor,
|
||||||
SecureRandom secureRandom, AndroidExecutor androidExecutor,
|
Context appContext, SecureRandom secureRandom, Clock clock,
|
||||||
Context appContext, Clock clock, Backoff backoff,
|
Backoff backoff, PluginCallback callback, int maxLatency) {
|
||||||
PluginCallback callback, int maxLatency, int maxIdleTime) {
|
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
|
||||||
super(connectionLimiter, timeoutMonitor, ioExecutor, secureRandom,
|
maxLatency);
|
||||||
backoff, callback, maxLatency, maxIdleTime);
|
|
||||||
this.androidExecutor = androidExecutor;
|
this.androidExecutor = androidExecutor;
|
||||||
this.appContext = appContext;
|
this.appContext = appContext;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
@@ -174,10 +172,9 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
return wrapSocket(ss.accept());
|
return wrapSocket(ss.accept());
|
||||||
}
|
}
|
||||||
|
|
||||||
private DuplexTransportConnection wrapSocket(BluetoothSocket s)
|
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
|
||||||
throws IOException {
|
return new AndroidBluetoothTransportConnection(this,
|
||||||
return new AndroidBluetoothTransportConnection(this, connectionLimiter,
|
connectionLimiter, s);
|
||||||
timeoutMonitor, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.bluetooth;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
@@ -26,7 +25,6 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
|||||||
public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
||||||
|
|
||||||
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
||||||
private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
|
|
||||||
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
|
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
|
||||||
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
||||||
private static final double BACKOFF_BASE = 1.2;
|
private static final double BACKOFF_BASE = 1.2;
|
||||||
@@ -37,20 +35,18 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final TimeoutMonitor timeoutMonitor;
|
|
||||||
private final BackoffFactory backoffFactory;
|
private final BackoffFactory backoffFactory;
|
||||||
|
|
||||||
public AndroidBluetoothPluginFactory(Executor ioExecutor,
|
public AndroidBluetoothPluginFactory(Executor ioExecutor,
|
||||||
AndroidExecutor androidExecutor, Context appContext,
|
AndroidExecutor androidExecutor, Context appContext,
|
||||||
SecureRandom secureRandom, EventBus eventBus, Clock clock,
|
SecureRandom secureRandom, EventBus eventBus, Clock clock,
|
||||||
TimeoutMonitor timeoutMonitor, BackoffFactory backoffFactory) {
|
BackoffFactory backoffFactory) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.androidExecutor = androidExecutor;
|
this.androidExecutor = androidExecutor;
|
||||||
this.appContext = appContext;
|
this.appContext = appContext;
|
||||||
this.secureRandom = secureRandom;
|
this.secureRandom = secureRandom;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.timeoutMonitor = timeoutMonitor;
|
|
||||||
this.backoffFactory = backoffFactory;
|
this.backoffFactory = backoffFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,9 +67,8 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
||||||
connectionLimiter, timeoutMonitor, ioExecutor, secureRandom,
|
connectionLimiter, ioExecutor, androidExecutor, appContext,
|
||||||
androidExecutor, appContext, clock, backoff,
|
secureRandom, clock, backoff, callback, MAX_LATENCY);
|
||||||
callback, MAX_LATENCY, MAX_IDLE_TIME);
|
|
||||||
eventBus.addListener(plugin);
|
eventBus.addListener(plugin);
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.briarproject.bramble.plugin.bluetooth;
|
|||||||
|
|
||||||
import android.bluetooth.BluetoothSocket;
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
||||||
@@ -18,26 +17,22 @@ import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress
|
|||||||
class AndroidBluetoothTransportConnection
|
class AndroidBluetoothTransportConnection
|
||||||
extends AbstractDuplexTransportConnection {
|
extends AbstractDuplexTransportConnection {
|
||||||
|
|
||||||
private final BluetoothConnectionLimiter connectionLimiter;
|
private final BluetoothConnectionLimiter connectionManager;
|
||||||
private final BluetoothSocket socket;
|
private final BluetoothSocket socket;
|
||||||
private final InputStream in;
|
|
||||||
|
|
||||||
AndroidBluetoothTransportConnection(Plugin plugin,
|
AndroidBluetoothTransportConnection(Plugin plugin,
|
||||||
BluetoothConnectionLimiter connectionLimiter,
|
BluetoothConnectionLimiter connectionManager,
|
||||||
TimeoutMonitor timeoutMonitor, BluetoothSocket socket)
|
BluetoothSocket socket) {
|
||||||
throws IOException {
|
|
||||||
super(plugin);
|
super(plugin);
|
||||||
this.connectionLimiter = connectionLimiter;
|
this.connectionManager = connectionManager;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
in = timeoutMonitor.createTimeoutInputStream(
|
|
||||||
socket.getInputStream(), plugin.getMaxIdleTime() * 2);
|
|
||||||
String address = socket.getRemoteDevice().getAddress();
|
String address = socket.getRemoteDevice().getAddress();
|
||||||
if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address);
|
if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InputStream getInputStream() {
|
protected InputStream getInputStream() throws IOException {
|
||||||
return in;
|
return socket.getInputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -50,7 +45,7 @@ class AndroidBluetoothTransportConnection
|
|||||||
try {
|
try {
|
||||||
socket.close();
|
socket.close();
|
||||||
} finally {
|
} finally {
|
||||||
connectionLimiter.connectionClosed(this);
|
connectionManager.connectionClosed(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps track of which contacts are currently connected by which transports.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface ConnectionRegistry {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a connection with the given contact over the given transport.
|
|
||||||
* <p>
|
|
||||||
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
|
|
||||||
* {@link ContactConnectedEvent} if this is the only connection with the
|
|
||||||
* contact.
|
|
||||||
*/
|
|
||||||
void registerConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, boolean incoming);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters a connection with the given contact over the given transport.
|
|
||||||
* <p>
|
|
||||||
* Broadcasts {@link ConnectionClosedEvent}. Also broadcasts
|
|
||||||
* {@link ContactDisconnectedEvent} if this is the only connection with
|
|
||||||
* the contact.
|
|
||||||
*/
|
|
||||||
void unregisterConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, boolean incoming, boolean exception);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the {@link Priority priority} of a connection that was previously
|
|
||||||
* registered via {@link #registerConnection(ContactId, TransportId,
|
|
||||||
* InterruptibleConnection, boolean)}.
|
|
||||||
* <p>
|
|
||||||
* If the registry has any "better" connections with the given contact, the
|
|
||||||
* given connection will be interrupted. If the registry has any "worse"
|
|
||||||
* connections with the given contact, those connections will be
|
|
||||||
* interrupted.
|
|
||||||
* <p>
|
|
||||||
* Connection A is considered "better" than connection B if both
|
|
||||||
* connections have had their priorities set, and either A's transport is
|
|
||||||
* {@link PluginConfig#getTransportPreferences() preferred} to B's, or
|
|
||||||
* they use the same transport and A has higher {@link Priority priority}
|
|
||||||
* than B.
|
|
||||||
* <p>
|
|
||||||
* For backward compatibility, connections without priorities are not
|
|
||||||
* considered better or worse than other connections.
|
|
||||||
*/
|
|
||||||
void setPriority(ContactId c, TransportId t, InterruptibleConnection conn,
|
|
||||||
Priority priority);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns any contacts that are connected via the given transport.
|
|
||||||
*/
|
|
||||||
Collection<ContactId> getConnectedContacts(TransportId t);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns any contacts that are connected via the given transport or any
|
|
||||||
* {@link PluginConfig#getTransportPreferences() better} transport.
|
|
||||||
*/
|
|
||||||
Collection<ContactId> getConnectedOrBetterContacts(TransportId t);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given contact is connected via the given transport.
|
|
||||||
*/
|
|
||||||
boolean isConnected(ContactId c, TransportId t);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given contact is connected via any transport.
|
|
||||||
*/
|
|
||||||
boolean isConnected(ContactId c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a connection with the given pending contact. Broadcasts
|
|
||||||
* {@link RendezvousConnectionOpenedEvent} if this is the only connection
|
|
||||||
* with the pending contact.
|
|
||||||
*
|
|
||||||
* @return True if this is the only connection with the pending contact,
|
|
||||||
* false if it is redundant and should be closed
|
|
||||||
*/
|
|
||||||
boolean registerConnection(PendingContactId p);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters a connection with the given pending contact. Broadcasts
|
|
||||||
* {@link RendezvousConnectionClosedEvent}.
|
|
||||||
*/
|
|
||||||
void unregisterConnection(PendingContactId p, boolean success);
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A duplex sync connection that can be closed by interrupting its outgoing
|
|
||||||
* sync session.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface InterruptibleConnection {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interrupts the connection's outgoing sync session. If the underlying
|
|
||||||
* transport connection is alive and the remote peer is cooperative, this
|
|
||||||
* should result in both sync sessions ending and the connection being
|
|
||||||
* cleanly closed.
|
|
||||||
*/
|
|
||||||
void interruptOutgoingSession();
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.io;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public interface TimeoutMonitor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an {@link InputStream} that wraps the given stream and allows
|
|
||||||
* read timeouts to be detected.
|
|
||||||
*
|
|
||||||
* @param timeoutMs The read timeout in milliseconds. Timeouts will be
|
|
||||||
* detected eventually but are not guaranteed to be detected immediately.
|
|
||||||
*/
|
|
||||||
InputStream createTimeoutInputStream(InputStream in, long timeoutMs);
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
package org.briarproject.bramble.api.connection;
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of which contacts are currently connected by which transports.
|
||||||
|
*/
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface ConnectionRegistry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a connection with the given contact over the given transport.
|
||||||
|
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
|
||||||
|
* {@link ConnectionStatusChangedEvent} if this is the only connection with
|
||||||
|
* the contact.
|
||||||
|
*/
|
||||||
|
void registerConnection(ContactId c, TransportId t, boolean incoming);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a connection with the given contact over the given transport.
|
||||||
|
* Broadcasts {@link ConnectionClosedEvent}. Also broadcasts
|
||||||
|
* {@link ConnectionStatusChangedEvent} if this is the only connection with
|
||||||
|
* the contact.
|
||||||
|
*/
|
||||||
|
void unregisterConnection(ContactId c, TransportId t, boolean incoming);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns any contacts that are connected via the given transport.
|
||||||
|
*/
|
||||||
|
Collection<ContactId> getConnectedContacts(TransportId t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given contact is connected via the given transport.
|
||||||
|
*/
|
||||||
|
boolean isConnected(ContactId c, TransportId t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the connection status of the given contact via all transports.
|
||||||
|
*/
|
||||||
|
ConnectionStatus getConnectionStatus(ContactId c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a connection with the given pending contact. Broadcasts
|
||||||
|
* {@link RendezvousConnectionOpenedEvent} if this is the only connection
|
||||||
|
* with the pending contact.
|
||||||
|
*
|
||||||
|
* @return True if this is the only connection with the pending contact,
|
||||||
|
* false if it is redundant and should be closed
|
||||||
|
*/
|
||||||
|
boolean registerConnection(PendingContactId p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a connection with the given pending contact. Broadcasts
|
||||||
|
* {@link RendezvousConnectionClosedEvent}.
|
||||||
|
*/
|
||||||
|
void unregisterConnection(PendingContactId p, boolean success);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
|
public enum ConnectionStatus {
|
||||||
|
CONNECTED, RECENTLY_CONNECTED, DISCONNECTED
|
||||||
|
}
|
||||||
@@ -5,8 +5,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
|||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface PluginConfig {
|
public interface PluginConfig {
|
||||||
@@ -16,11 +14,4 @@ public interface PluginConfig {
|
|||||||
Collection<SimplexPluginFactory> getSimplexFactories();
|
Collection<SimplexPluginFactory> getSimplexFactories();
|
||||||
|
|
||||||
boolean shouldPoll();
|
boolean shouldPoll();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a map representing transport preferences. For each entry in the
|
|
||||||
* map, connections via the transports identified by the value are
|
|
||||||
* preferred to connections via the transport identified by the key.
|
|
||||||
*/
|
|
||||||
Map<TransportId, List<TransportId>> getTransportPreferences();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,13 @@ public class ConnectionClosedEvent extends Event {
|
|||||||
|
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final boolean incoming, exception;
|
private final boolean incoming;
|
||||||
|
|
||||||
public ConnectionClosedEvent(ContactId contactId, TransportId transportId,
|
public ConnectionClosedEvent(ContactId contactId, TransportId transportId,
|
||||||
boolean incoming, boolean exception) {
|
boolean incoming) {
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
this.incoming = incoming;
|
this.incoming = incoming;
|
||||||
this.exception = exception;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContactId getContactId() {
|
public ContactId getContactId() {
|
||||||
@@ -34,8 +33,4 @@ public class ConnectionClosedEvent extends Event {
|
|||||||
public boolean isIncoming() {
|
public boolean isIncoming() {
|
||||||
return incoming;
|
return incoming;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isException() {
|
|
||||||
return exception;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,31 @@ package org.briarproject.bramble.api.plugin.event;
|
|||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event that is broadcast when a contact disconnects and is no longer
|
* An event that is broadcast when a contact's connection status changes.
|
||||||
* connected via any transport.
|
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ContactDisconnectedEvent extends Event {
|
public class ConnectionStatusChangedEvent extends Event {
|
||||||
|
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
|
private final ConnectionStatus status;
|
||||||
|
|
||||||
public ContactDisconnectedEvent(ContactId contactId) {
|
public ConnectionStatusChangedEvent(ContactId contactId,
|
||||||
|
ConnectionStatus status) {
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContactId getContactId() {
|
public ContactId getContactId() {
|
||||||
return contactId;
|
return contactId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConnectionStatus getConnectionStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.plugin.event;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An event that is broadcast when a contact connects that was not previously
|
|
||||||
* connected via any transport.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class ContactConnectedEvent extends Event {
|
|
||||||
|
|
||||||
private final ContactId contactId;
|
|
||||||
|
|
||||||
public ContactConnectedEvent(ContactId contactId) {
|
|
||||||
this.contactId = contactId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContactId getContactId() {
|
|
||||||
return contactId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.sync;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A record containing a nonce for choosing between redundant sessions.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class Priority {
|
|
||||||
|
|
||||||
private final byte[] nonce;
|
|
||||||
|
|
||||||
public Priority(byte[] nonce) {
|
|
||||||
this.nonce = nonce;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getNonce() {
|
|
||||||
return nonce;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.sync;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for handling a {@link Priority} record received by an
|
|
||||||
* incoming {@link SyncSession}.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface PriorityHandler {
|
|
||||||
|
|
||||||
void handle(Priority p);
|
|
||||||
}
|
|
||||||
@@ -10,5 +10,4 @@ public interface RecordTypes {
|
|||||||
byte OFFER = 2;
|
byte OFFER = 2;
|
||||||
byte REQUEST = 3;
|
byte REQUEST = 3;
|
||||||
byte VERSIONS = 4;
|
byte VERSIONS = 4;
|
||||||
byte PRIORITY = 5;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,10 +49,4 @@ public interface SyncConstants {
|
|||||||
* simultaneously.
|
* simultaneously.
|
||||||
*/
|
*/
|
||||||
int MAX_SUPPORTED_VERSIONS = 10;
|
int MAX_SUPPORTED_VERSIONS = 10;
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the priority nonce used for choosing between redundant
|
|
||||||
* connections.
|
|
||||||
*/
|
|
||||||
int PRIORITY_NONCE_BYTES = 16;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,4 @@ public interface SyncRecordReader {
|
|||||||
boolean hasVersions() throws IOException;
|
boolean hasVersions() throws IOException;
|
||||||
|
|
||||||
Versions readVersions() throws IOException;
|
Versions readVersions() throws IOException;
|
||||||
|
|
||||||
boolean hasPriority() throws IOException;
|
|
||||||
|
|
||||||
Priority readPriority() throws IOException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,5 @@ public interface SyncRecordWriter {
|
|||||||
|
|
||||||
void writeVersions(Versions v) throws IOException;
|
void writeVersions(Versions v) throws IOException;
|
||||||
|
|
||||||
void writePriority(Priority p) throws IOException;
|
|
||||||
|
|
||||||
void flush() throws IOException;
|
void flush() throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,14 @@ import org.briarproject.bramble.api.transport.StreamWriter;
|
|||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface SyncSessionFactory {
|
public interface SyncSessionFactory {
|
||||||
|
|
||||||
SyncSession createIncomingSession(ContactId c, InputStream in,
|
SyncSession createIncomingSession(ContactId c, InputStream in);
|
||||||
PriorityHandler handler);
|
|
||||||
|
|
||||||
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
|
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
|
||||||
StreamWriter streamWriter);
|
StreamWriter streamWriter);
|
||||||
|
|
||||||
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
|
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
|
||||||
int maxIdleTime, StreamWriter streamWriter,
|
int maxIdleTime, StreamWriter streamWriter);
|
||||||
@Nullable Priority priority);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble;
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
import org.briarproject.bramble.client.ClientModule;
|
import org.briarproject.bramble.client.ClientModule;
|
||||||
import org.briarproject.bramble.connection.ConnectionModule;
|
|
||||||
import org.briarproject.bramble.contact.ContactModule;
|
import org.briarproject.bramble.contact.ContactModule;
|
||||||
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
||||||
import org.briarproject.bramble.crypto.CryptoModule;
|
import org.briarproject.bramble.crypto.CryptoModule;
|
||||||
@@ -10,7 +9,6 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.io.IoModule;
|
|
||||||
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
@@ -29,7 +27,6 @@ import dagger.Module;
|
|||||||
|
|
||||||
@Module(includes = {
|
@Module(includes = {
|
||||||
ClientModule.class,
|
ClientModule.class,
|
||||||
ConnectionModule.class,
|
|
||||||
ContactModule.class,
|
ContactModule.class,
|
||||||
CryptoModule.class,
|
CryptoModule.class,
|
||||||
CryptoExecutorModule.class,
|
CryptoExecutorModule.class,
|
||||||
@@ -38,7 +35,6 @@ import dagger.Module;
|
|||||||
DatabaseExecutorModule.class,
|
DatabaseExecutorModule.class,
|
||||||
EventModule.class,
|
EventModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
IoModule.class,
|
|
||||||
KeyAgreementModule.class,
|
KeyAgreementModule.class,
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
import static org.briarproject.bramble.util.IoUtils.read;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class Connection {
|
|
||||||
|
|
||||||
protected static final Logger LOG = getLogger(Connection.class.getName());
|
|
||||||
|
|
||||||
final KeyManager keyManager;
|
|
||||||
final ConnectionRegistry connectionRegistry;
|
|
||||||
final StreamReaderFactory streamReaderFactory;
|
|
||||||
final StreamWriterFactory streamWriterFactory;
|
|
||||||
|
|
||||||
Connection(KeyManager keyManager, ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory) {
|
|
||||||
this.keyManager = keyManager;
|
|
||||||
this.connectionRegistry = connectionRegistry;
|
|
||||||
this.streamReaderFactory = streamReaderFactory;
|
|
||||||
this.streamWriterFactory = streamWriterFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
StreamContext recogniseTag(TransportConnectionReader reader,
|
|
||||||
TransportId transportId) {
|
|
||||||
StreamContext ctx;
|
|
||||||
try {
|
|
||||||
byte[] tag = readTag(reader.getInputStream());
|
|
||||||
return keyManager.getStreamContext(transportId, tag);
|
|
||||||
} catch (IOException | DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] readTag(InputStream in) throws IOException {
|
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
|
||||||
read(in, tag);
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disposeOnError(TransportConnectionReader reader, boolean recognised) {
|
|
||||||
try {
|
|
||||||
reader.dispose(true, recognised);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void disposeOnError(TransportConnectionWriter writer) {
|
|
||||||
try {
|
|
||||||
writer.dispose(true);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class ConnectionManagerImpl implements ConnectionManager {
|
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
|
||||||
private final KeyManager keyManager;
|
|
||||||
private final StreamReaderFactory streamReaderFactory;
|
|
||||||
private final StreamWriterFactory streamWriterFactory;
|
|
||||||
private final SyncSessionFactory syncSessionFactory;
|
|
||||||
private final HandshakeManager handshakeManager;
|
|
||||||
private final ContactExchangeManager contactExchangeManager;
|
|
||||||
private final ConnectionRegistry connectionRegistry;
|
|
||||||
private final TransportPropertyManager transportPropertyManager;
|
|
||||||
private final SecureRandom secureRandom;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ConnectionManagerImpl(@IoExecutor Executor ioExecutor,
|
|
||||||
KeyManager keyManager, StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
HandshakeManager handshakeManager,
|
|
||||||
ContactExchangeManager contactExchangeManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
SecureRandom secureRandom) {
|
|
||||||
this.ioExecutor = ioExecutor;
|
|
||||||
this.keyManager = keyManager;
|
|
||||||
this.streamReaderFactory = streamReaderFactory;
|
|
||||||
this.streamWriterFactory = streamWriterFactory;
|
|
||||||
this.syncSessionFactory = syncSessionFactory;
|
|
||||||
this.handshakeManager = handshakeManager;
|
|
||||||
this.contactExchangeManager = contactExchangeManager;
|
|
||||||
this.connectionRegistry = connectionRegistry;
|
|
||||||
this.transportPropertyManager = transportPropertyManager;
|
|
||||||
this.secureRandom = secureRandom;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageIncomingConnection(TransportId t,
|
|
||||||
TransportConnectionReader r) {
|
|
||||||
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
syncSessionFactory, transportPropertyManager, t, r));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageIncomingConnection(TransportId t,
|
|
||||||
DuplexTransportConnection d) {
|
|
||||||
ioExecutor.execute(new IncomingDuplexSyncConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
syncSessionFactory, transportPropertyManager, ioExecutor,
|
|
||||||
t, d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageIncomingConnection(PendingContactId p, TransportId t,
|
|
||||||
DuplexTransportConnection d) {
|
|
||||||
ioExecutor.execute(new IncomingHandshakeConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
handshakeManager, contactExchangeManager, this, p, t, d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageOutgoingConnection(ContactId c, TransportId t,
|
|
||||||
TransportConnectionWriter w) {
|
|
||||||
ioExecutor.execute(new OutgoingSimplexSyncConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
syncSessionFactory, transportPropertyManager, c, t, w));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageOutgoingConnection(ContactId c, TransportId t,
|
|
||||||
DuplexTransportConnection d) {
|
|
||||||
ioExecutor.execute(new OutgoingDuplexSyncConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
syncSessionFactory, transportPropertyManager, ioExecutor,
|
|
||||||
secureRandom, c, t, d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageOutgoingConnection(PendingContactId p, TransportId t,
|
|
||||||
DuplexTransportConnection d) {
|
|
||||||
ioExecutor.execute(new OutgoingHandshakeConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
handshakeManager, contactExchangeManager, this, p, t, d));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class ConnectionModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
ConnectionManager provideConnectionManager(
|
|
||||||
ConnectionManagerImpl connectionManager) {
|
|
||||||
return connectionManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
ConnectionRegistry provideConnectionRegistry(
|
|
||||||
ConnectionRegistryImpl connectionRegistry) {
|
|
||||||
return connectionRegistry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,270 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.connection.InterruptibleConnection;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class ConnectionRegistryImpl implements ConnectionRegistry {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(ConnectionRegistryImpl.class.getName());
|
|
||||||
|
|
||||||
private final EventBus eventBus;
|
|
||||||
private final Map<TransportId, List<TransportId>> transportPrefs;
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private final Map<ContactId, List<ConnectionRecord>> contactConnections;
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private final Set<PendingContactId> connectedPendingContacts;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ConnectionRegistryImpl(EventBus eventBus, PluginConfig pluginConfig) {
|
|
||||||
this.eventBus = eventBus;
|
|
||||||
transportPrefs = pluginConfig.getTransportPreferences();
|
|
||||||
contactConnections = new HashMap<>();
|
|
||||||
connectedPendingContacts = new HashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, boolean incoming) {
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
if (incoming) LOG.info("Incoming connection registered: " + t);
|
|
||||||
else LOG.info("Outgoing connection registered: " + t);
|
|
||||||
}
|
|
||||||
boolean firstConnection;
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ConnectionRecord> recs = contactConnections.get(c);
|
|
||||||
if (recs == null) {
|
|
||||||
recs = new ArrayList<>();
|
|
||||||
contactConnections.put(c, recs);
|
|
||||||
}
|
|
||||||
firstConnection = recs.isEmpty();
|
|
||||||
recs.add(new ConnectionRecord(t, conn));
|
|
||||||
}
|
|
||||||
eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming));
|
|
||||||
if (firstConnection) {
|
|
||||||
LOG.info("Contact connected");
|
|
||||||
eventBus.broadcast(new ContactConnectedEvent(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPriority(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, Priority priority) {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Setting connection priority: " + t);
|
|
||||||
List<InterruptibleConnection> toInterrupt;
|
|
||||||
boolean interruptNewConnection = false;
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ConnectionRecord> recs = contactConnections.get(c);
|
|
||||||
if (recs == null) throw new IllegalArgumentException();
|
|
||||||
toInterrupt = new ArrayList<>(recs.size());
|
|
||||||
for (ConnectionRecord rec : recs) {
|
|
||||||
if (rec.conn == conn) {
|
|
||||||
// Store the priority of this connection
|
|
||||||
rec.priority = priority;
|
|
||||||
} else if (rec.priority != null) {
|
|
||||||
int compare = compareConnections(t, priority,
|
|
||||||
rec.transportId, rec.priority);
|
|
||||||
if (compare == -1) {
|
|
||||||
// The old connection is better than the new one
|
|
||||||
interruptNewConnection = true;
|
|
||||||
} else if (compare == 1 && !rec.interrupted) {
|
|
||||||
// The new connection is better than the old one
|
|
||||||
toInterrupt.add(rec.conn);
|
|
||||||
rec.interrupted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (interruptNewConnection) {
|
|
||||||
LOG.info("Interrupting new connection");
|
|
||||||
conn.interruptOutgoingSession();
|
|
||||||
}
|
|
||||||
for (InterruptibleConnection old : toInterrupt) {
|
|
||||||
LOG.info("Interrupting old connection");
|
|
||||||
old.interruptOutgoingSession();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int compareConnections(TransportId tA, Priority pA, TransportId tB,
|
|
||||||
Priority pB) {
|
|
||||||
if (getBetterTransports(tA).contains(tB)) return -1;
|
|
||||||
if (getBetterTransports(tB).contains(tA)) return 1;
|
|
||||||
return tA.equals(tB) ? Bytes.compare(pA.getNonce(), pB.getNonce()) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TransportId> getBetterTransports(TransportId t) {
|
|
||||||
List<TransportId> better = transportPrefs.get(t);
|
|
||||||
return better == null ? emptyList() : better;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unregisterConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, boolean incoming, boolean exception) {
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
if (incoming) LOG.info("Incoming connection unregistered: " + t);
|
|
||||||
else LOG.info("Outgoing connection unregistered: " + t);
|
|
||||||
}
|
|
||||||
boolean lastConnection;
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ConnectionRecord> recs = contactConnections.get(c);
|
|
||||||
if (recs == null || !recs.remove(new ConnectionRecord(t, conn)))
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
lastConnection = recs.isEmpty();
|
|
||||||
}
|
|
||||||
eventBus.broadcast(
|
|
||||||
new ConnectionClosedEvent(c, t, incoming, exception));
|
|
||||||
if (lastConnection) {
|
|
||||||
LOG.info("Contact disconnected");
|
|
||||||
eventBus.broadcast(new ContactDisconnectedEvent(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<ContactId> getConnectedContacts(TransportId t) {
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ContactId> contactIds = new ArrayList<>();
|
|
||||||
for (Entry<ContactId, List<ConnectionRecord>> e :
|
|
||||||
contactConnections.entrySet()) {
|
|
||||||
for (ConnectionRecord rec : e.getValue()) {
|
|
||||||
if (rec.transportId.equals(t)) {
|
|
||||||
contactIds.add(e.getKey());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info(contactIds.size() + " contacts connected: " + t);
|
|
||||||
}
|
|
||||||
return contactIds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<ContactId> getConnectedOrBetterContacts(TransportId t) {
|
|
||||||
synchronized (lock) {
|
|
||||||
List<TransportId> better = getBetterTransports(t);
|
|
||||||
List<ContactId> contactIds = new ArrayList<>();
|
|
||||||
for (Entry<ContactId, List<ConnectionRecord>> e :
|
|
||||||
contactConnections.entrySet()) {
|
|
||||||
for (ConnectionRecord rec : e.getValue()) {
|
|
||||||
if (rec.transportId.equals(t) ||
|
|
||||||
better.contains(rec.transportId)) {
|
|
||||||
contactIds.add(e.getKey());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info(contactIds.size()
|
|
||||||
+ " contacts connected or better: " + t);
|
|
||||||
}
|
|
||||||
return contactIds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConnected(ContactId c, TransportId t) {
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ConnectionRecord> recs = contactConnections.get(c);
|
|
||||||
if (recs == null) return false;
|
|
||||||
for (ConnectionRecord rec : recs) {
|
|
||||||
if (rec.transportId.equals(t)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConnected(ContactId c) {
|
|
||||||
synchronized (lock) {
|
|
||||||
return contactConnections.containsKey(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean registerConnection(PendingContactId p) {
|
|
||||||
boolean added;
|
|
||||||
synchronized (lock) {
|
|
||||||
added = connectedPendingContacts.add(p);
|
|
||||||
}
|
|
||||||
if (added) eventBus.broadcast(new RendezvousConnectionOpenedEvent(p));
|
|
||||||
return added;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unregisterConnection(PendingContactId p, boolean success) {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (!connectedPendingContacts.remove(p))
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ConnectionRecord {
|
|
||||||
|
|
||||||
private final TransportId transportId;
|
|
||||||
private final InterruptibleConnection conn;
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private Priority priority = null;
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean interrupted = false;
|
|
||||||
|
|
||||||
private ConnectionRecord(TransportId transportId,
|
|
||||||
InterruptibleConnection conn) {
|
|
||||||
this.transportId = transportId;
|
|
||||||
this.conn = conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (o instanceof ConnectionRecord) {
|
|
||||||
return conn == ((ConnectionRecord) o).conn;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return conn.hashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.connection.InterruptibleConnection;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class DuplexSyncConnection extends SyncConnection
|
|
||||||
implements InterruptibleConnection {
|
|
||||||
|
|
||||||
final Executor ioExecutor;
|
|
||||||
final TransportId transportId;
|
|
||||||
final TransportConnectionReader reader;
|
|
||||||
final TransportConnectionWriter writer;
|
|
||||||
final TransportProperties remote;
|
|
||||||
|
|
||||||
private final Object interruptLock = new Object();
|
|
||||||
|
|
||||||
@GuardedBy("interruptLock")
|
|
||||||
@Nullable
|
|
||||||
private SyncSession outgoingSession = null;
|
|
||||||
@GuardedBy("interruptLock")
|
|
||||||
private boolean interruptWaiting = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void interruptOutgoingSession() {
|
|
||||||
synchronized (interruptLock) {
|
|
||||||
if (outgoingSession == null) interruptWaiting = true;
|
|
||||||
else outgoingSession.interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setOutgoingSession(SyncSession outgoingSession) {
|
|
||||||
synchronized (interruptLock) {
|
|
||||||
this.outgoingSession = outgoingSession;
|
|
||||||
if (interruptWaiting) {
|
|
||||||
outgoingSession.interrupt();
|
|
||||||
interruptWaiting = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DuplexSyncConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
Executor ioExecutor, TransportId transportId,
|
|
||||||
DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, syncSessionFactory,
|
|
||||||
transportPropertyManager);
|
|
||||||
this.ioExecutor = ioExecutor;
|
|
||||||
this.transportId = transportId;
|
|
||||||
reader = connection.getReader();
|
|
||||||
writer = connection.getWriter();
|
|
||||||
remote = connection.getRemoteProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onReadError(boolean recognised) {
|
|
||||||
disposeOnError(reader, recognised);
|
|
||||||
disposeOnError(writer);
|
|
||||||
interruptOutgoingSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onWriteError() {
|
|
||||||
disposeOnError(reader, true);
|
|
||||||
disposeOnError(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncSession createDuplexOutgoingSession(StreamContext ctx,
|
|
||||||
TransportConnectionWriter w, @Nullable Priority priority)
|
|
||||||
throws IOException {
|
|
||||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
|
||||||
w.getOutputStream(), ctx);
|
|
||||||
ContactId c = requireNonNull(ctx.getContactId());
|
|
||||||
return syncSessionFactory.createDuplexOutgoingSession(c,
|
|
||||||
w.getMaxLatency(), w.getMaxIdleTime(), streamWriter, priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class HandshakeConnection extends Connection {
|
|
||||||
|
|
||||||
final HandshakeManager handshakeManager;
|
|
||||||
final ContactExchangeManager contactExchangeManager;
|
|
||||||
final ConnectionManager connectionManager;
|
|
||||||
final PendingContactId pendingContactId;
|
|
||||||
final TransportId transportId;
|
|
||||||
final DuplexTransportConnection connection;
|
|
||||||
final TransportConnectionReader reader;
|
|
||||||
final TransportConnectionWriter writer;
|
|
||||||
|
|
||||||
HandshakeConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
HandshakeManager handshakeManager,
|
|
||||||
ContactExchangeManager contactExchangeManager,
|
|
||||||
ConnectionManager connectionManager,
|
|
||||||
PendingContactId pendingContactId,
|
|
||||||
TransportId transportId, DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory);
|
|
||||||
this.handshakeManager = handshakeManager;
|
|
||||||
this.contactExchangeManager = contactExchangeManager;
|
|
||||||
this.connectionManager = connectionManager;
|
|
||||||
this.pendingContactId = pendingContactId;
|
|
||||||
this.transportId = transportId;
|
|
||||||
this.connection = connection;
|
|
||||||
reader = connection.getReader();
|
|
||||||
writer = connection.getWriter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
StreamContext allocateStreamContext(PendingContactId pendingContactId,
|
|
||||||
TransportId transportId) {
|
|
||||||
try {
|
|
||||||
return keyManager.getStreamContext(pendingContactId, transportId);
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onError(boolean recognised) {
|
|
||||||
disposeOnError(reader, recognised);
|
|
||||||
disposeOnError(writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.PriorityHandler;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class IncomingDuplexSyncConnection extends DuplexSyncConnection
|
|
||||||
implements Runnable {
|
|
||||||
|
|
||||||
IncomingDuplexSyncConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
Executor ioExecutor, TransportId transportId,
|
|
||||||
DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, syncSessionFactory,
|
|
||||||
transportPropertyManager, ioExecutor, transportId, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Read and recognise the tag
|
|
||||||
StreamContext ctx = recogniseTag(reader, transportId);
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.info("Unrecognised tag");
|
|
||||||
onReadError(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ContactId contactId = ctx.getContactId();
|
|
||||||
if (contactId == null) {
|
|
||||||
LOG.warning("Expected contact tag, got rendezvous tag");
|
|
||||||
onReadError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ctx.isHandshakeMode()) {
|
|
||||||
// TODO: Support handshake mode for contacts
|
|
||||||
LOG.warning("Received handshake tag, expected rotation mode");
|
|
||||||
onReadError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connectionRegistry.registerConnection(contactId, transportId,
|
|
||||||
this, true);
|
|
||||||
// Start the outgoing session on another thread
|
|
||||||
ioExecutor.execute(() -> runOutgoingSession(contactId));
|
|
||||||
try {
|
|
||||||
// Store any transport properties discovered from the connection
|
|
||||||
transportPropertyManager.addRemotePropertiesFromConnection(
|
|
||||||
contactId, transportId, remote);
|
|
||||||
// Update the connection registry when we receive our priority
|
|
||||||
PriorityHandler handler = p -> connectionRegistry.setPriority(
|
|
||||||
contactId, transportId, this, p);
|
|
||||||
// Create and run the incoming session
|
|
||||||
createIncomingSession(ctx, reader, handler).run();
|
|
||||||
reader.dispose(false, true);
|
|
||||||
interruptOutgoingSession();
|
|
||||||
connectionRegistry.unregisterConnection(contactId, transportId,
|
|
||||||
this, true, false);
|
|
||||||
} catch (DbException | IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onReadError(true);
|
|
||||||
connectionRegistry.unregisterConnection(contactId, transportId,
|
|
||||||
this, true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runOutgoingSession(ContactId contactId) {
|
|
||||||
// Allocate a stream context
|
|
||||||
StreamContext ctx = allocateStreamContext(contactId, transportId);
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.warning("Could not allocate stream context");
|
|
||||||
onWriteError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Create and run the outgoing session
|
|
||||||
SyncSession out = createDuplexOutgoingSession(ctx, writer, null);
|
|
||||||
setOutgoingSession(out);
|
|
||||||
out.run();
|
|
||||||
writer.dispose(false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onWriteError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class IncomingHandshakeConnection extends HandshakeConnection
|
|
||||||
implements Runnable {
|
|
||||||
|
|
||||||
IncomingHandshakeConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
HandshakeManager handshakeManager,
|
|
||||||
ContactExchangeManager contactExchangeManager,
|
|
||||||
ConnectionManager connectionManager,
|
|
||||||
PendingContactId pendingContactId,
|
|
||||||
TransportId transportId, DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, handshakeManager, contactExchangeManager,
|
|
||||||
connectionManager, pendingContactId, transportId, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Read and recognise the tag
|
|
||||||
StreamContext ctxIn = recogniseTag(reader, transportId);
|
|
||||||
if (ctxIn == null) {
|
|
||||||
LOG.info("Unrecognised tag");
|
|
||||||
onError(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
|
|
||||||
if (inPendingContactId == null) {
|
|
||||||
LOG.warning("Expected rendezvous tag, got contact tag");
|
|
||||||
onError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Allocate the outgoing stream context
|
|
||||||
StreamContext ctxOut =
|
|
||||||
allocateStreamContext(pendingContactId, transportId);
|
|
||||||
if (ctxOut == null) {
|
|
||||||
LOG.warning("Could not allocate stream context");
|
|
||||||
onError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Close the connection if it's redundant
|
|
||||||
if (!connectionRegistry.registerConnection(pendingContactId)) {
|
|
||||||
LOG.info("Redundant rendezvous connection");
|
|
||||||
onError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Handshake and exchange contacts
|
|
||||||
try {
|
|
||||||
InputStream in = streamReaderFactory.createStreamReader(
|
|
||||||
reader.getInputStream(), ctxIn);
|
|
||||||
// Flush the output stream to send the outgoing stream header
|
|
||||||
StreamWriter out = streamWriterFactory.createStreamWriter(
|
|
||||||
writer.getOutputStream(), ctxOut);
|
|
||||||
out.getOutputStream().flush();
|
|
||||||
HandshakeResult result =
|
|
||||||
handshakeManager.handshake(pendingContactId, in, out);
|
|
||||||
contactExchangeManager.exchangeContacts(pendingContactId,
|
|
||||||
connection, result.getMasterKey(), result.isAlice(), false);
|
|
||||||
connectionRegistry.unregisterConnection(pendingContactId, true);
|
|
||||||
// Reuse the connection as a transport connection
|
|
||||||
connectionManager.manageIncomingConnection(transportId, connection);
|
|
||||||
} catch (IOException | DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError(true);
|
|
||||||
connectionRegistry.unregisterConnection(pendingContactId, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.PriorityHandler;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|
||||||
|
|
||||||
private final TransportId transportId;
|
|
||||||
private final TransportConnectionReader reader;
|
|
||||||
|
|
||||||
IncomingSimplexSyncConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
TransportId transportId, TransportConnectionReader reader) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, syncSessionFactory,
|
|
||||||
transportPropertyManager);
|
|
||||||
this.transportId = transportId;
|
|
||||||
this.reader = reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Read and recognise the tag
|
|
||||||
StreamContext ctx = recogniseTag(reader, transportId);
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.info("Unrecognised tag");
|
|
||||||
onError(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ContactId contactId = ctx.getContactId();
|
|
||||||
if (contactId == null) {
|
|
||||||
LOG.warning("Received rendezvous stream, expected contact");
|
|
||||||
onError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ctx.isHandshakeMode()) {
|
|
||||||
// TODO: Support handshake mode for contacts
|
|
||||||
LOG.warning("Received handshake tag, expected rotation mode");
|
|
||||||
onError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// We don't expect to receive a priority for this connection
|
|
||||||
PriorityHandler handler = p ->
|
|
||||||
LOG.info("Ignoring priority for simplex connection");
|
|
||||||
// Create and run the incoming session
|
|
||||||
createIncomingSession(ctx, reader, handler).run();
|
|
||||||
reader.dispose(false, true);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onError(boolean recognised) {
|
|
||||||
disposeOnError(reader, recognised);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.api.sync.PriorityHandler;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class OutgoingDuplexSyncConnection extends DuplexSyncConnection
|
|
||||||
implements Runnable {
|
|
||||||
|
|
||||||
private final SecureRandom secureRandom;
|
|
||||||
private final ContactId contactId;
|
|
||||||
|
|
||||||
OutgoingDuplexSyncConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
Executor ioExecutor, SecureRandom secureRandom, ContactId contactId,
|
|
||||||
TransportId transportId, DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, syncSessionFactory,
|
|
||||||
transportPropertyManager, ioExecutor, transportId, connection);
|
|
||||||
this.secureRandom = secureRandom;
|
|
||||||
this.contactId = contactId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Allocate a stream context
|
|
||||||
StreamContext ctx = allocateStreamContext(contactId, transportId);
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.warning("Could not allocate stream context");
|
|
||||||
onWriteError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ctx.isHandshakeMode()) {
|
|
||||||
// TODO: Support handshake mode for contacts
|
|
||||||
LOG.warning("Cannot use handshake mode stream context");
|
|
||||||
onWriteError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Start the incoming session on another thread
|
|
||||||
Priority priority = generatePriority();
|
|
||||||
ioExecutor.execute(() -> runIncomingSession(priority));
|
|
||||||
try {
|
|
||||||
// Create and run the outgoing session
|
|
||||||
SyncSession out =
|
|
||||||
createDuplexOutgoingSession(ctx, writer, priority);
|
|
||||||
setOutgoingSession(out);
|
|
||||||
out.run();
|
|
||||||
writer.dispose(false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onWriteError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runIncomingSession(Priority priority) {
|
|
||||||
// Read and recognise the tag
|
|
||||||
StreamContext ctx = recogniseTag(reader, transportId);
|
|
||||||
// Unrecognised tags are suspicious in this case
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.warning("Unrecognised tag for returning stream");
|
|
||||||
onReadError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Check that the stream comes from the expected contact
|
|
||||||
ContactId inContactId = ctx.getContactId();
|
|
||||||
if (inContactId == null) {
|
|
||||||
LOG.warning("Expected contact tag, got rendezvous tag");
|
|
||||||
onReadError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!contactId.equals(inContactId)) {
|
|
||||||
LOG.warning("Wrong contact ID for returning stream");
|
|
||||||
onReadError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ctx.isHandshakeMode()) {
|
|
||||||
// TODO: Support handshake mode for contacts
|
|
||||||
LOG.warning("Received handshake tag, expected rotation mode");
|
|
||||||
onReadError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connectionRegistry.registerConnection(contactId, transportId,
|
|
||||||
this, false);
|
|
||||||
connectionRegistry.setPriority(contactId, transportId, this, priority);
|
|
||||||
try {
|
|
||||||
// Store any transport properties discovered from the connection
|
|
||||||
transportPropertyManager.addRemotePropertiesFromConnection(
|
|
||||||
contactId, transportId, remote);
|
|
||||||
// We don't expect to receive a priority for this connection
|
|
||||||
PriorityHandler handler = p ->
|
|
||||||
LOG.info("Ignoring priority for outgoing connection");
|
|
||||||
// Create and run the incoming session
|
|
||||||
createIncomingSession(ctx, reader, handler).run();
|
|
||||||
reader.dispose(false, true);
|
|
||||||
interruptOutgoingSession();
|
|
||||||
connectionRegistry.unregisterConnection(contactId, transportId,
|
|
||||||
this, false, false);
|
|
||||||
} catch (DbException | IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onReadError();
|
|
||||||
connectionRegistry.unregisterConnection(contactId, transportId,
|
|
||||||
this, false, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onReadError() {
|
|
||||||
// 'Recognised' is always true for outgoing connections
|
|
||||||
onReadError(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Priority generatePriority() {
|
|
||||||
byte[] nonce = new byte[PRIORITY_NONCE_BYTES];
|
|
||||||
secureRandom.nextBytes(nonce);
|
|
||||||
return new Priority(nonce);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class OutgoingHandshakeConnection extends HandshakeConnection
|
|
||||||
implements Runnable {
|
|
||||||
|
|
||||||
OutgoingHandshakeConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
HandshakeManager handshakeManager,
|
|
||||||
ContactExchangeManager contactExchangeManager,
|
|
||||||
ConnectionManager connectionManager,
|
|
||||||
PendingContactId pendingContactId,
|
|
||||||
TransportId transportId, DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, handshakeManager, contactExchangeManager,
|
|
||||||
connectionManager, pendingContactId, transportId, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Allocate the outgoing stream context
|
|
||||||
StreamContext ctxOut =
|
|
||||||
allocateStreamContext(pendingContactId, transportId);
|
|
||||||
if (ctxOut == null) {
|
|
||||||
LOG.warning("Could not allocate stream context");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Flush the output stream to send the outgoing stream header
|
|
||||||
StreamWriter out;
|
|
||||||
try {
|
|
||||||
out = streamWriterFactory.createStreamWriter(
|
|
||||||
writer.getOutputStream(), ctxOut);
|
|
||||||
out.getOutputStream().flush();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Read and recognise the tag
|
|
||||||
StreamContext ctxIn = recogniseTag(reader, transportId);
|
|
||||||
// Unrecognised tags are suspicious in this case
|
|
||||||
if (ctxIn == null) {
|
|
||||||
LOG.warning("Unrecognised tag for returning stream");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Check that the stream comes from the expected pending contact
|
|
||||||
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
|
|
||||||
if (inPendingContactId == null) {
|
|
||||||
LOG.warning("Expected rendezvous tag, got contact tag");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!inPendingContactId.equals(pendingContactId)) {
|
|
||||||
LOG.warning("Wrong pending contact ID for returning stream");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Close the connection if it's redundant
|
|
||||||
if (!connectionRegistry.registerConnection(pendingContactId)) {
|
|
||||||
LOG.info("Redundant rendezvous connection");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Handshake and exchange contacts
|
|
||||||
try {
|
|
||||||
InputStream in = streamReaderFactory.createStreamReader(
|
|
||||||
reader.getInputStream(), ctxIn);
|
|
||||||
HandshakeResult result =
|
|
||||||
handshakeManager.handshake(pendingContactId, in, out);
|
|
||||||
Contact contact = contactExchangeManager.exchangeContacts(
|
|
||||||
pendingContactId, connection, result.getMasterKey(),
|
|
||||||
result.isAlice(), false);
|
|
||||||
connectionRegistry.unregisterConnection(pendingContactId, true);
|
|
||||||
// Reuse the connection as a transport connection
|
|
||||||
connectionManager.manageOutgoingConnection(contact.getId(),
|
|
||||||
transportId, connection);
|
|
||||||
} catch (IOException | DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError();
|
|
||||||
connectionRegistry.unregisterConnection(pendingContactId, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onError() {
|
|
||||||
// 'Recognised' is always true for outgoing connections
|
|
||||||
onError(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class OutgoingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|
||||||
|
|
||||||
private final ContactId contactId;
|
|
||||||
private final TransportId transportId;
|
|
||||||
private final TransportConnectionWriter writer;
|
|
||||||
|
|
||||||
OutgoingSimplexSyncConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
ContactId contactId, TransportId transportId,
|
|
||||||
TransportConnectionWriter writer) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, syncSessionFactory,
|
|
||||||
transportPropertyManager);
|
|
||||||
this.contactId = contactId;
|
|
||||||
this.transportId = transportId;
|
|
||||||
this.writer = writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Allocate a stream context
|
|
||||||
StreamContext ctx = allocateStreamContext(contactId, transportId);
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.warning("Could not allocate stream context");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Create and run the outgoing session
|
|
||||||
createSimplexOutgoingSession(ctx, writer).run();
|
|
||||||
writer.dispose(false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onError() {
|
|
||||||
disposeOnError(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
|
|
||||||
TransportConnectionWriter w) throws IOException {
|
|
||||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
|
||||||
w.getOutputStream(), ctx);
|
|
||||||
ContactId c = requireNonNull(ctx.getContactId());
|
|
||||||
return syncSessionFactory.createSimplexOutgoingSession(c,
|
|
||||||
w.getMaxLatency(), streamWriter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.PriorityHandler;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class SyncConnection extends Connection {
|
|
||||||
|
|
||||||
final SyncSessionFactory syncSessionFactory;
|
|
||||||
final TransportPropertyManager transportPropertyManager;
|
|
||||||
|
|
||||||
SyncConnection(KeyManager keyManager, ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory);
|
|
||||||
this.syncSessionFactory = syncSessionFactory;
|
|
||||||
this.transportPropertyManager = transportPropertyManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
StreamContext allocateStreamContext(ContactId contactId,
|
|
||||||
TransportId transportId) {
|
|
||||||
try {
|
|
||||||
return keyManager.getStreamContext(contactId, transportId);
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncSession createIncomingSession(StreamContext ctx,
|
|
||||||
TransportConnectionReader r, PriorityHandler handler)
|
|
||||||
throws IOException {
|
|
||||||
InputStream streamReader = streamReaderFactory.createStreamReader(
|
|
||||||
r.getInputStream(), ctx);
|
|
||||||
ContactId c = requireNonNull(ctx.getContactId());
|
|
||||||
return syncSessionFactory
|
|
||||||
.createIncomingSession(c, streamReader, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class IoModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
TimeoutMonitor provideTimeoutMonitor(TimeoutMonitorImpl timeoutMonitor) {
|
|
||||||
return timeoutMonitor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class TimeoutInputStream extends InputStream {
|
|
||||||
|
|
||||||
private final Clock clock;
|
|
||||||
private final InputStream in;
|
|
||||||
private final long timeoutMs;
|
|
||||||
private final CloseListener listener;
|
|
||||||
private final Object lock = new Object();
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private long readStartedMs = -1;
|
|
||||||
|
|
||||||
TimeoutInputStream(Clock clock, InputStream in, long timeoutMs,
|
|
||||||
CloseListener listener) {
|
|
||||||
this.clock = clock;
|
|
||||||
this.in = in;
|
|
||||||
this.timeoutMs = timeoutMs;
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
synchronized (lock) {
|
|
||||||
readStartedMs = clock.currentTimeMillis();
|
|
||||||
}
|
|
||||||
int input = in.read();
|
|
||||||
synchronized (lock) {
|
|
||||||
readStartedMs = -1;
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b) throws IOException {
|
|
||||||
return read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
|
||||||
synchronized (lock) {
|
|
||||||
readStartedMs = clock.currentTimeMillis();
|
|
||||||
}
|
|
||||||
int read = in.read(b, off, len);
|
|
||||||
synchronized (lock) {
|
|
||||||
readStartedMs = -1;
|
|
||||||
}
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
try {
|
|
||||||
in.close();
|
|
||||||
} finally {
|
|
||||||
listener.onClose(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int available() throws IOException {
|
|
||||||
return in.available();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mark(int readlimit) {
|
|
||||||
in.mark(readlimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean markSupported() {
|
|
||||||
return in.markSupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() throws IOException {
|
|
||||||
in.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long skip(long n) throws IOException {
|
|
||||||
return in.skip(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasTimedOut() {
|
|
||||||
synchronized (lock) {
|
|
||||||
return readStartedMs != -1 &&
|
|
||||||
clock.currentTimeMillis() - readStartedMs > timeoutMs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CloseListener {
|
|
||||||
|
|
||||||
void onClose(TimeoutInputStream closed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
import org.briarproject.bramble.api.system.Scheduler;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
class TimeoutMonitorImpl implements TimeoutMonitor {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(TimeoutMonitorImpl.class.getName());
|
|
||||||
|
|
||||||
private static final long CHECK_INTERVAL_MS = SECONDS.toMillis(10);
|
|
||||||
|
|
||||||
private final ScheduledExecutorService scheduler;
|
|
||||||
private final Executor ioExecutor;
|
|
||||||
private final Clock clock;
|
|
||||||
private final Object lock = new Object();
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private final List<TimeoutInputStream> streams = new ArrayList<>();
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private Future<?> task = null;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
TimeoutMonitorImpl(@Scheduler ScheduledExecutorService scheduler,
|
|
||||||
@IoExecutor Executor ioExecutor, Clock clock) {
|
|
||||||
this.scheduler = scheduler;
|
|
||||||
this.ioExecutor = ioExecutor;
|
|
||||||
this.clock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream createTimeoutInputStream(InputStream in,
|
|
||||||
long timeoutMs) {
|
|
||||||
TimeoutInputStream stream = new TimeoutInputStream(clock, in,
|
|
||||||
timeoutMs, this::removeStream);
|
|
||||||
synchronized (lock) {
|
|
||||||
if (streams.isEmpty()) {
|
|
||||||
task = scheduler.scheduleWithFixedDelay(this::checkTimeouts,
|
|
||||||
CHECK_INTERVAL_MS, CHECK_INTERVAL_MS, MILLISECONDS);
|
|
||||||
}
|
|
||||||
streams.add(stream);
|
|
||||||
}
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeStream(TimeoutInputStream stream) {
|
|
||||||
Future<?> toCancel = null;
|
|
||||||
synchronized (lock) {
|
|
||||||
if (streams.remove(stream) && streams.isEmpty()) {
|
|
||||||
toCancel = task;
|
|
||||||
task = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (toCancel != null) toCancel.cancel(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Scheduler
|
|
||||||
private void checkTimeouts() {
|
|
||||||
ioExecutor.execute(() -> {
|
|
||||||
List<TimeoutInputStream> snapshot;
|
|
||||||
synchronized (lock) {
|
|
||||||
snapshot = new ArrayList<>(streams);
|
|
||||||
}
|
|
||||||
for (TimeoutInputStream stream : snapshot) {
|
|
||||||
if (stream.hasTimedOut()) {
|
|
||||||
LOG.info("Input stream has timed out");
|
|
||||||
try {
|
|
||||||
stream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, INFO, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,709 @@
|
|||||||
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.HandshakeManager;
|
||||||
|
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||||
|
import org.briarproject.bramble.api.sync.SyncSession;
|
||||||
|
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
||||||
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
|
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||||
|
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||||
|
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.IoUtils.read;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class ConnectionManagerImpl implements ConnectionManager {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(ConnectionManagerImpl.class.getName());
|
||||||
|
|
||||||
|
private final Executor ioExecutor;
|
||||||
|
private final KeyManager keyManager;
|
||||||
|
private final StreamReaderFactory streamReaderFactory;
|
||||||
|
private final StreamWriterFactory streamWriterFactory;
|
||||||
|
private final SyncSessionFactory syncSessionFactory;
|
||||||
|
private final HandshakeManager handshakeManager;
|
||||||
|
private final ContactExchangeManager contactExchangeManager;
|
||||||
|
private final ConnectionRegistry connectionRegistry;
|
||||||
|
private final TransportPropertyManager transportPropertyManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ConnectionManagerImpl(@IoExecutor Executor ioExecutor,
|
||||||
|
KeyManager keyManager, StreamReaderFactory streamReaderFactory,
|
||||||
|
StreamWriterFactory streamWriterFactory,
|
||||||
|
SyncSessionFactory syncSessionFactory,
|
||||||
|
HandshakeManager handshakeManager,
|
||||||
|
ContactExchangeManager contactExchangeManager,
|
||||||
|
ConnectionRegistry connectionRegistry,
|
||||||
|
TransportPropertyManager transportPropertyManager) {
|
||||||
|
this.ioExecutor = ioExecutor;
|
||||||
|
this.keyManager = keyManager;
|
||||||
|
this.streamReaderFactory = streamReaderFactory;
|
||||||
|
this.streamWriterFactory = streamWriterFactory;
|
||||||
|
this.syncSessionFactory = syncSessionFactory;
|
||||||
|
this.handshakeManager = handshakeManager;
|
||||||
|
this.contactExchangeManager = contactExchangeManager;
|
||||||
|
this.connectionRegistry = connectionRegistry;
|
||||||
|
this.transportPropertyManager = transportPropertyManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageIncomingConnection(TransportId t,
|
||||||
|
TransportConnectionReader r) {
|
||||||
|
ioExecutor.execute(new ManageIncomingSimplexConnection(t, r));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageIncomingConnection(TransportId t,
|
||||||
|
DuplexTransportConnection d) {
|
||||||
|
ioExecutor.execute(new ManageIncomingDuplexConnection(t, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageIncomingConnection(PendingContactId p, TransportId t,
|
||||||
|
DuplexTransportConnection d) {
|
||||||
|
ioExecutor.execute(new ManageIncomingHandshakeConnection(p, t, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageOutgoingConnection(ContactId c, TransportId t,
|
||||||
|
TransportConnectionWriter w) {
|
||||||
|
ioExecutor.execute(new ManageOutgoingSimplexConnection(c, t, w));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageOutgoingConnection(ContactId c, TransportId t,
|
||||||
|
DuplexTransportConnection d) {
|
||||||
|
ioExecutor.execute(new ManageOutgoingDuplexConnection(c, t, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageOutgoingConnection(PendingContactId p, TransportId t,
|
||||||
|
DuplexTransportConnection d) {
|
||||||
|
ioExecutor.execute(new ManageOutgoingHandshakeConnection(p, t, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readTag(InputStream in) throws IOException {
|
||||||
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
|
read(in, tag);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SyncSession createIncomingSession(StreamContext ctx,
|
||||||
|
TransportConnectionReader r) throws IOException {
|
||||||
|
InputStream streamReader = streamReaderFactory.createStreamReader(
|
||||||
|
r.getInputStream(), ctx);
|
||||||
|
ContactId c = requireNonNull(ctx.getContactId());
|
||||||
|
return syncSessionFactory.createIncomingSession(c, streamReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
|
||||||
|
TransportConnectionWriter w) throws IOException {
|
||||||
|
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||||
|
w.getOutputStream(), ctx);
|
||||||
|
ContactId c = requireNonNull(ctx.getContactId());
|
||||||
|
return syncSessionFactory.createSimplexOutgoingSession(c,
|
||||||
|
w.getMaxLatency(), streamWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SyncSession createDuplexOutgoingSession(StreamContext ctx,
|
||||||
|
TransportConnectionWriter w) throws IOException {
|
||||||
|
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||||
|
w.getOutputStream(), ctx);
|
||||||
|
ContactId c = requireNonNull(ctx.getContactId());
|
||||||
|
return syncSessionFactory.createDuplexOutgoingSession(c,
|
||||||
|
w.getMaxLatency(), w.getMaxIdleTime(), streamWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disposeOnError(TransportConnectionReader reader,
|
||||||
|
boolean recognised) {
|
||||||
|
try {
|
||||||
|
reader.dispose(true, recognised);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disposeOnError(TransportConnectionWriter writer) {
|
||||||
|
try {
|
||||||
|
writer.dispose(true);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageIncomingSimplexConnection implements Runnable {
|
||||||
|
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final TransportConnectionReader reader;
|
||||||
|
|
||||||
|
private ManageIncomingSimplexConnection(TransportId transportId,
|
||||||
|
TransportConnectionReader reader) {
|
||||||
|
this.transportId = transportId;
|
||||||
|
this.reader = reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Read and recognise the tag
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
|
ctx = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.info("Unrecognised tag");
|
||||||
|
onError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ContactId contactId = ctx.getContactId();
|
||||||
|
if (contactId == null) {
|
||||||
|
LOG.warning("Received rendezvous stream, expected contact");
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx.isHandshakeMode()) {
|
||||||
|
// TODO: Support handshake mode for contacts
|
||||||
|
LOG.warning("Received handshake tag, expected rotation mode");
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectionRegistry.registerConnection(contactId, transportId, true);
|
||||||
|
try {
|
||||||
|
// Create and run the incoming session
|
||||||
|
createIncomingSession(ctx, reader).run();
|
||||||
|
reader.dispose(false, true);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError(true);
|
||||||
|
} finally {
|
||||||
|
connectionRegistry.unregisterConnection(contactId, transportId,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError(boolean recognised) {
|
||||||
|
disposeOnError(reader, recognised);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageOutgoingSimplexConnection implements Runnable {
|
||||||
|
|
||||||
|
private final ContactId contactId;
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final TransportConnectionWriter writer;
|
||||||
|
|
||||||
|
private ManageOutgoingSimplexConnection(ContactId contactId,
|
||||||
|
TransportId transportId, TransportConnectionWriter writer) {
|
||||||
|
this.contactId = contactId;
|
||||||
|
this.transportId = transportId;
|
||||||
|
this.writer = writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Allocate a stream context
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
ctx = keyManager.getStreamContext(contactId, transportId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.warning("Could not allocate stream context");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectionRegistry.registerConnection(contactId, transportId,
|
||||||
|
false);
|
||||||
|
try {
|
||||||
|
// Create and run the outgoing session
|
||||||
|
createSimplexOutgoingSession(ctx, writer).run();
|
||||||
|
writer.dispose(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
} finally {
|
||||||
|
connectionRegistry.unregisterConnection(contactId, transportId,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError() {
|
||||||
|
disposeOnError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageIncomingDuplexConnection implements Runnable {
|
||||||
|
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final TransportConnectionReader reader;
|
||||||
|
private final TransportConnectionWriter writer;
|
||||||
|
private final TransportProperties remote;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private volatile SyncSession outgoingSession = null;
|
||||||
|
|
||||||
|
private ManageIncomingDuplexConnection(TransportId transportId,
|
||||||
|
DuplexTransportConnection connection) {
|
||||||
|
this.transportId = transportId;
|
||||||
|
reader = connection.getReader();
|
||||||
|
writer = connection.getWriter();
|
||||||
|
remote = connection.getRemoteProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Read and recognise the tag
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
|
ctx = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onReadError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.info("Unrecognised tag");
|
||||||
|
onReadError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ContactId contactId = ctx.getContactId();
|
||||||
|
if (contactId == null) {
|
||||||
|
LOG.warning("Expected contact tag, got rendezvous tag");
|
||||||
|
onReadError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx.isHandshakeMode()) {
|
||||||
|
// TODO: Support handshake mode for contacts
|
||||||
|
LOG.warning("Received handshake tag, expected rotation mode");
|
||||||
|
onReadError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectionRegistry.registerConnection(contactId, transportId, true);
|
||||||
|
// Start the outgoing session on another thread
|
||||||
|
ioExecutor.execute(() -> runOutgoingSession(contactId));
|
||||||
|
try {
|
||||||
|
// Store any transport properties discovered from the connection
|
||||||
|
transportPropertyManager.addRemotePropertiesFromConnection(
|
||||||
|
contactId, transportId, remote);
|
||||||
|
// Create and run the incoming session
|
||||||
|
createIncomingSession(ctx, reader).run();
|
||||||
|
reader.dispose(false, true);
|
||||||
|
// Interrupt the outgoing session so it finishes cleanly
|
||||||
|
SyncSession out = outgoingSession;
|
||||||
|
if (out != null) out.interrupt();
|
||||||
|
} catch (DbException | IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onReadError(true);
|
||||||
|
} finally {
|
||||||
|
connectionRegistry.unregisterConnection(contactId, transportId,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runOutgoingSession(ContactId contactId) {
|
||||||
|
// Allocate a stream context
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
ctx = keyManager.getStreamContext(contactId, transportId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onWriteError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.warning("Could not allocate stream context");
|
||||||
|
onWriteError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Create and run the outgoing session
|
||||||
|
SyncSession out = createDuplexOutgoingSession(ctx, writer);
|
||||||
|
outgoingSession = out;
|
||||||
|
out.run();
|
||||||
|
writer.dispose(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onWriteError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onReadError(boolean recognised) {
|
||||||
|
disposeOnError(reader, recognised);
|
||||||
|
disposeOnError(writer);
|
||||||
|
// Interrupt the outgoing session so it finishes
|
||||||
|
SyncSession out = outgoingSession;
|
||||||
|
if (out != null) out.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onWriteError() {
|
||||||
|
disposeOnError(reader, true);
|
||||||
|
disposeOnError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageOutgoingDuplexConnection implements Runnable {
|
||||||
|
|
||||||
|
private final ContactId contactId;
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final TransportConnectionReader reader;
|
||||||
|
private final TransportConnectionWriter writer;
|
||||||
|
private final TransportProperties remote;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private volatile SyncSession outgoingSession = null;
|
||||||
|
|
||||||
|
private ManageOutgoingDuplexConnection(ContactId contactId,
|
||||||
|
TransportId transportId, DuplexTransportConnection connection) {
|
||||||
|
this.contactId = contactId;
|
||||||
|
this.transportId = transportId;
|
||||||
|
reader = connection.getReader();
|
||||||
|
writer = connection.getWriter();
|
||||||
|
remote = connection.getRemoteProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Allocate a stream context
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
ctx = keyManager.getStreamContext(contactId, transportId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onWriteError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.warning("Could not allocate stream context");
|
||||||
|
onWriteError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx.isHandshakeMode()) {
|
||||||
|
// TODO: Support handshake mode for contacts
|
||||||
|
LOG.warning("Cannot use handshake mode stream context");
|
||||||
|
onWriteError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Start the incoming session on another thread
|
||||||
|
ioExecutor.execute(this::runIncomingSession);
|
||||||
|
try {
|
||||||
|
// Create and run the outgoing session
|
||||||
|
SyncSession out = createDuplexOutgoingSession(ctx, writer);
|
||||||
|
outgoingSession = out;
|
||||||
|
out.run();
|
||||||
|
writer.dispose(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onWriteError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runIncomingSession() {
|
||||||
|
// Read and recognise the tag
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
|
ctx = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onReadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Unrecognised tags are suspicious in this case
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.warning("Unrecognised tag for returning stream");
|
||||||
|
onReadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check that the stream comes from the expected contact
|
||||||
|
ContactId inContactId = ctx.getContactId();
|
||||||
|
if (inContactId == null) {
|
||||||
|
LOG.warning("Expected contact tag, got rendezvous tag");
|
||||||
|
onReadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!contactId.equals(inContactId)) {
|
||||||
|
LOG.warning("Wrong contact ID for returning stream");
|
||||||
|
onReadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx.isHandshakeMode()) {
|
||||||
|
// TODO: Support handshake mode for contacts
|
||||||
|
LOG.warning("Received handshake tag, expected rotation mode");
|
||||||
|
onReadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectionRegistry.registerConnection(contactId, transportId,
|
||||||
|
false);
|
||||||
|
try {
|
||||||
|
// Store any transport properties discovered from the connection
|
||||||
|
transportPropertyManager.addRemotePropertiesFromConnection(
|
||||||
|
contactId, transportId, remote);
|
||||||
|
// Create and run the incoming session
|
||||||
|
createIncomingSession(ctx, reader).run();
|
||||||
|
reader.dispose(false, true);
|
||||||
|
// Interrupt the outgoing session so it finishes cleanly
|
||||||
|
SyncSession out = outgoingSession;
|
||||||
|
if (out != null) out.interrupt();
|
||||||
|
} catch (DbException | IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onReadError();
|
||||||
|
} finally {
|
||||||
|
connectionRegistry.unregisterConnection(contactId, transportId,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onReadError() {
|
||||||
|
// 'Recognised' is always true for outgoing connections
|
||||||
|
disposeOnError(reader, true);
|
||||||
|
disposeOnError(writer);
|
||||||
|
// Interrupt the outgoing session so it finishes
|
||||||
|
SyncSession out = outgoingSession;
|
||||||
|
if (out != null) out.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onWriteError() {
|
||||||
|
disposeOnError(reader, true);
|
||||||
|
disposeOnError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageIncomingHandshakeConnection implements Runnable {
|
||||||
|
|
||||||
|
private final PendingContactId pendingContactId;
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final DuplexTransportConnection connection;
|
||||||
|
private final TransportConnectionReader reader;
|
||||||
|
private final TransportConnectionWriter writer;
|
||||||
|
|
||||||
|
private ManageIncomingHandshakeConnection(
|
||||||
|
PendingContactId pendingContactId, TransportId transportId,
|
||||||
|
DuplexTransportConnection connection) {
|
||||||
|
this.pendingContactId = pendingContactId;
|
||||||
|
this.transportId = transportId;
|
||||||
|
this.connection = connection;
|
||||||
|
reader = connection.getReader();
|
||||||
|
writer = connection.getWriter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Read and recognise the tag
|
||||||
|
StreamContext ctxIn;
|
||||||
|
try {
|
||||||
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
|
ctxIn = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctxIn == null) {
|
||||||
|
LOG.info("Unrecognised tag");
|
||||||
|
onError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
|
||||||
|
if (inPendingContactId == null) {
|
||||||
|
LOG.warning("Expected rendezvous tag, got contact tag");
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Allocate the outgoing stream context
|
||||||
|
StreamContext ctxOut;
|
||||||
|
try {
|
||||||
|
ctxOut = keyManager.getStreamContext(pendingContactId,
|
||||||
|
transportId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctxOut == null) {
|
||||||
|
LOG.warning("Could not allocate stream context");
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Close the connection if it's redundant
|
||||||
|
if (!connectionRegistry.registerConnection(pendingContactId)) {
|
||||||
|
LOG.info("Redundant rendezvous connection");
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Handshake and exchange contacts
|
||||||
|
try {
|
||||||
|
InputStream in = streamReaderFactory.createStreamReader(
|
||||||
|
reader.getInputStream(), ctxIn);
|
||||||
|
// Flush the output stream to send the outgoing stream header
|
||||||
|
StreamWriter out = streamWriterFactory.createStreamWriter(
|
||||||
|
writer.getOutputStream(), ctxOut);
|
||||||
|
out.getOutputStream().flush();
|
||||||
|
HandshakeResult result = handshakeManager.handshake(
|
||||||
|
pendingContactId, in, out);
|
||||||
|
Contact contact = contactExchangeManager.exchangeContacts(
|
||||||
|
pendingContactId, connection, result.getMasterKey(),
|
||||||
|
result.isAlice(), false);
|
||||||
|
connectionRegistry.unregisterConnection(pendingContactId, true);
|
||||||
|
// Reuse the connection as a transport connection
|
||||||
|
manageOutgoingConnection(contact.getId(), transportId,
|
||||||
|
connection);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError(true);
|
||||||
|
connectionRegistry.unregisterConnection(pendingContactId,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError(boolean recognised) {
|
||||||
|
disposeOnError(reader, recognised);
|
||||||
|
disposeOnError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageOutgoingHandshakeConnection implements Runnable {
|
||||||
|
|
||||||
|
private final PendingContactId pendingContactId;
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final DuplexTransportConnection connection;
|
||||||
|
private final TransportConnectionReader reader;
|
||||||
|
private final TransportConnectionWriter writer;
|
||||||
|
|
||||||
|
private ManageOutgoingHandshakeConnection(
|
||||||
|
PendingContactId pendingContactId, TransportId transportId,
|
||||||
|
DuplexTransportConnection connection) {
|
||||||
|
this.pendingContactId = pendingContactId;
|
||||||
|
this.transportId = transportId;
|
||||||
|
this.connection = connection;
|
||||||
|
reader = connection.getReader();
|
||||||
|
writer = connection.getWriter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Allocate the outgoing stream context
|
||||||
|
StreamContext ctxOut;
|
||||||
|
try {
|
||||||
|
ctxOut = keyManager.getStreamContext(pendingContactId,
|
||||||
|
transportId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctxOut == null) {
|
||||||
|
LOG.warning("Could not allocate stream context");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Flush the output stream to send the outgoing stream header
|
||||||
|
StreamWriter out;
|
||||||
|
try {
|
||||||
|
out = streamWriterFactory.createStreamWriter(
|
||||||
|
writer.getOutputStream(), ctxOut);
|
||||||
|
out.getOutputStream().flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Read and recognise the tag
|
||||||
|
StreamContext ctxIn;
|
||||||
|
try {
|
||||||
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
|
ctxIn = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Unrecognised tags are suspicious in this case
|
||||||
|
if (ctxIn == null) {
|
||||||
|
LOG.warning("Unrecognised tag for returning stream");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check that the stream comes from the expected pending contact
|
||||||
|
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
|
||||||
|
if (inPendingContactId == null) {
|
||||||
|
LOG.warning("Expected rendezvous tag, got contact tag");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!inPendingContactId.equals(pendingContactId)) {
|
||||||
|
LOG.warning("Wrong pending contact ID for returning stream");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Close the connection if it's redundant
|
||||||
|
if (!connectionRegistry.registerConnection(pendingContactId)) {
|
||||||
|
LOG.info("Redundant rendezvous connection");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Handshake and exchange contacts
|
||||||
|
try {
|
||||||
|
InputStream in = streamReaderFactory.createStreamReader(
|
||||||
|
reader.getInputStream(), ctxIn);
|
||||||
|
HandshakeResult result = handshakeManager.handshake(
|
||||||
|
pendingContactId, in, out);
|
||||||
|
Contact contact = contactExchangeManager.exchangeContacts(
|
||||||
|
pendingContactId, connection, result.getMasterKey(),
|
||||||
|
result.isAlice(), false);
|
||||||
|
connectionRegistry.unregisterConnection(pendingContactId, true);
|
||||||
|
// Reuse the connection as a transport connection
|
||||||
|
manageOutgoingConnection(contact.getId(), transportId,
|
||||||
|
connection);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
connectionRegistry.unregisterConnection(pendingContactId,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError() {
|
||||||
|
// 'Recognised' is always true for outgoing connections
|
||||||
|
disposeOnError(reader, true);
|
||||||
|
disposeOnError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Multiset;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.system.Scheduler;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.DISCONNECTED;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.RECENTLY_CONNECTED;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
class ConnectionRegistryImpl implements ConnectionRegistry {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(ConnectionRegistryImpl.class.getName());
|
||||||
|
|
||||||
|
private static final long RECENTLY_CONNECTED_MS = MINUTES.toMillis(1);
|
||||||
|
private static final long EXPIRY_INTERVAL_MS = SECONDS.toMillis(10);
|
||||||
|
|
||||||
|
private final EventBus eventBus;
|
||||||
|
private final Clock clock;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final Map<TransportId, Multiset<ContactId>> contactConnections;
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final Map<ContactId, Counter> contactCounts;
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final Set<PendingContactId> connectedPendingContacts;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ConnectionRegistryImpl(EventBus eventBus, Clock clock,
|
||||||
|
@Scheduler ScheduledExecutorService scheduler) {
|
||||||
|
this.eventBus = eventBus;
|
||||||
|
this.clock = clock;
|
||||||
|
contactConnections = new HashMap<>();
|
||||||
|
contactCounts = new HashMap<>();
|
||||||
|
connectedPendingContacts = new HashSet<>();
|
||||||
|
scheduler.scheduleWithFixedDelay(this::expireRecentConnections,
|
||||||
|
EXPIRY_INTERVAL_MS, EXPIRY_INTERVAL_MS, MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerConnection(ContactId c, TransportId t,
|
||||||
|
boolean incoming) {
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
if (incoming) LOG.info("Incoming connection registered: " + t);
|
||||||
|
else LOG.info("Outgoing connection registered: " + t);
|
||||||
|
}
|
||||||
|
boolean firstConnection = false;
|
||||||
|
synchronized (lock) {
|
||||||
|
Multiset<ContactId> m = contactConnections.get(t);
|
||||||
|
if (m == null) {
|
||||||
|
m = new Multiset<>();
|
||||||
|
contactConnections.put(t, m);
|
||||||
|
}
|
||||||
|
m.add(c);
|
||||||
|
|
||||||
|
Counter counter = contactCounts.get(c);
|
||||||
|
if (counter == null) {
|
||||||
|
counter = new Counter();
|
||||||
|
contactCounts.put(c, counter);
|
||||||
|
}
|
||||||
|
if (counter.connections == 0) {
|
||||||
|
counter.disconnectedTime = 0;
|
||||||
|
firstConnection = true;
|
||||||
|
}
|
||||||
|
counter.connections++;
|
||||||
|
}
|
||||||
|
eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming));
|
||||||
|
if (firstConnection) {
|
||||||
|
LOG.info("Contact connected");
|
||||||
|
eventBus.broadcast(new ConnectionStatusChangedEvent(c, CONNECTED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterConnection(ContactId c, TransportId t,
|
||||||
|
boolean incoming) {
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
if (incoming) LOG.info("Incoming connection unregistered: " + t);
|
||||||
|
else LOG.info("Outgoing connection unregistered: " + t);
|
||||||
|
}
|
||||||
|
boolean lastConnection = false;
|
||||||
|
synchronized (lock) {
|
||||||
|
Multiset<ContactId> m = contactConnections.get(t);
|
||||||
|
if (m == null || !m.contains(c))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
m.remove(c);
|
||||||
|
|
||||||
|
Counter counter = contactCounts.get(c);
|
||||||
|
if (counter == null || counter.connections == 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
counter.connections--;
|
||||||
|
if (counter.connections == 0) {
|
||||||
|
counter.disconnectedTime = clock.currentTimeMillis();
|
||||||
|
lastConnection = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventBus.broadcast(new ConnectionClosedEvent(c, t, incoming));
|
||||||
|
if (lastConnection) {
|
||||||
|
LOG.info("Contact disconnected");
|
||||||
|
eventBus.broadcast(
|
||||||
|
new ConnectionStatusChangedEvent(c, RECENTLY_CONNECTED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ContactId> getConnectedContacts(TransportId t) {
|
||||||
|
synchronized (lock) {
|
||||||
|
Multiset<ContactId> m = contactConnections.get(t);
|
||||||
|
if (m == null) return emptyList();
|
||||||
|
List<ContactId> ids = new ArrayList<>(m.keySet());
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info(ids.size() + " contacts connected: " + t);
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnected(ContactId c, TransportId t) {
|
||||||
|
synchronized (lock) {
|
||||||
|
Multiset<ContactId> m = contactConnections.get(t);
|
||||||
|
return m != null && m.contains(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionStatus getConnectionStatus(ContactId c) {
|
||||||
|
synchronized (lock) {
|
||||||
|
Counter counter = contactCounts.get(c);
|
||||||
|
if (counter == null) return DISCONNECTED;
|
||||||
|
return counter.connections > 0 ? CONNECTED : RECENTLY_CONNECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean registerConnection(PendingContactId p) {
|
||||||
|
boolean added;
|
||||||
|
synchronized (lock) {
|
||||||
|
added = connectedPendingContacts.add(p);
|
||||||
|
}
|
||||||
|
if (added) eventBus.broadcast(new RendezvousConnectionOpenedEvent(p));
|
||||||
|
return added;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterConnection(PendingContactId p, boolean success) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!connectedPendingContacts.remove(p))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduler
|
||||||
|
private void expireRecentConnections() {
|
||||||
|
long now = clock.currentTimeMillis();
|
||||||
|
List<ContactId> disconnected = new ArrayList<>();
|
||||||
|
synchronized (lock) {
|
||||||
|
Iterator<Entry<ContactId, Counter>> it =
|
||||||
|
contactCounts.entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Entry<ContactId, Counter> e = it.next();
|
||||||
|
if (e.getValue().isExpired(now)) {
|
||||||
|
disconnected.add(e.getKey());
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ContactId c : disconnected) {
|
||||||
|
eventBus.broadcast(
|
||||||
|
new ConnectionStatusChangedEvent(c, DISCONNECTED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Counter {
|
||||||
|
|
||||||
|
private int connections = 0;
|
||||||
|
private long disconnectedTime = 0;
|
||||||
|
|
||||||
|
private boolean isExpired(long now) {
|
||||||
|
return connections == 0 &&
|
||||||
|
now - disconnectedTime > RECENTLY_CONNECTED_MS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.Service;
|
import org.briarproject.bramble.api.lifecycle.Service;
|
||||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package org.briarproject.bramble.plugin;
|
|||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
|
|
||||||
@@ -27,6 +29,20 @@ public class PluginModule {
|
|||||||
return new BackoffFactoryImpl();
|
return new BackoffFactoryImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ConnectionManager provideConnectionManager(
|
||||||
|
ConnectionManagerImpl connectionManager) {
|
||||||
|
return connectionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ConnectionRegistry provideConnectionRegistry(
|
||||||
|
ConnectionRegistryImpl connectionRegistry) {
|
||||||
|
return connectionRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
PluginManager providePluginManager(LifecycleManager lifecycleManager,
|
PluginManager providePluginManager(LifecycleManager lifecycleManager,
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Pair;
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -11,6 +9,8 @@ import org.briarproject.bramble.api.event.EventListener;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
@@ -98,8 +98,8 @@ class PollerImpl implements Poller, EventListener {
|
|||||||
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
|
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
|
||||||
// Reschedule polling, the polling interval may have decreased
|
// Reschedule polling, the polling interval may have decreased
|
||||||
reschedule(c.getTransportId());
|
reschedule(c.getTransportId());
|
||||||
// If an outgoing connection failed, try to reconnect
|
if (!c.isIncoming()) {
|
||||||
if (!c.isIncoming() && c.isException()) {
|
// Connect to the disconnected contact
|
||||||
connectToContact(c.getContactId(), c.getTransportId());
|
connectToContact(c.getContactId(), c.getTransportId());
|
||||||
}
|
}
|
||||||
} else if (e instanceof ConnectionOpenedEvent) {
|
} else if (e instanceof ConnectionOpenedEvent) {
|
||||||
@@ -215,7 +215,7 @@ class PollerImpl implements Poller, EventListener {
|
|||||||
Map<ContactId, TransportProperties> remote =
|
Map<ContactId, TransportProperties> remote =
|
||||||
transportPropertyManager.getRemoteProperties(t);
|
transportPropertyManager.getRemoteProperties(t);
|
||||||
Collection<ContactId> connected =
|
Collection<ContactId> connected =
|
||||||
connectionRegistry.getConnectedOrBetterContacts(t);
|
connectionRegistry.getConnectedContacts(t);
|
||||||
Collection<Pair<TransportProperties, ConnectionHandler>>
|
Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||||
properties = new ArrayList<>();
|
properties = new ArrayList<>();
|
||||||
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.Pair;
|
|||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
||||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
|
||||||
@@ -61,13 +60,12 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
getLogger(BluetoothPlugin.class.getName());
|
getLogger(BluetoothPlugin.class.getName());
|
||||||
|
|
||||||
final BluetoothConnectionLimiter connectionLimiter;
|
final BluetoothConnectionLimiter connectionLimiter;
|
||||||
final TimeoutMonitor timeoutMonitor;
|
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
private final Backoff backoff;
|
private final Backoff backoff;
|
||||||
private final PluginCallback callback;
|
private final PluginCallback callback;
|
||||||
private final int maxLatency, maxIdleTime;
|
private final int maxLatency;
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
private volatile boolean running = false, contactConnections = false;
|
private volatile boolean running = false, contactConnections = false;
|
||||||
@@ -107,17 +105,14 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
abstract DuplexTransportConnection discoverAndConnect(String uuid);
|
abstract DuplexTransportConnection discoverAndConnect(String uuid);
|
||||||
|
|
||||||
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
||||||
TimeoutMonitor timeoutMonitor, Executor ioExecutor,
|
Executor ioExecutor, SecureRandom secureRandom,
|
||||||
SecureRandom secureRandom, Backoff backoff,
|
Backoff backoff, PluginCallback callback, int maxLatency) {
|
||||||
PluginCallback callback, int maxLatency, int maxIdleTime) {
|
|
||||||
this.connectionLimiter = connectionLimiter;
|
this.connectionLimiter = connectionLimiter;
|
||||||
this.timeoutMonitor = timeoutMonitor;
|
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.secureRandom = secureRandom;
|
this.secureRandom = secureRandom;
|
||||||
this.backoff = backoff;
|
this.backoff = backoff;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.maxLatency = maxLatency;
|
this.maxLatency = maxLatency;
|
||||||
this.maxIdleTime = maxIdleTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onAdapterEnabled() {
|
void onAdapterEnabled() {
|
||||||
@@ -146,7 +141,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMaxIdleTime() {
|
public int getMaxIdleTime() {
|
||||||
return maxIdleTime;
|
// Bluetooth detects dead connections so we don't need keepalives
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -231,11 +227,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
|
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG.info("Connection received");
|
backoff.reset();
|
||||||
if (connectionLimiter.contactConnectionOpened(conn)) {
|
if (connectionLimiter.contactConnectionOpened(conn))
|
||||||
backoff.reset();
|
|
||||||
callback.handleConnection(conn);
|
callback.handleConnection(conn);
|
||||||
}
|
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,10 +273,13 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
String uuid = p.get(PROP_UUID);
|
String uuid = p.get(PROP_UUID);
|
||||||
if (isNullOrEmpty(uuid)) return;
|
if (isNullOrEmpty(uuid)) return;
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
|
if (!isRunning() || !shouldAllowContactConnections()) return;
|
||||||
|
if (!connectionLimiter.canOpenContactConnection()) return;
|
||||||
DuplexTransportConnection d = createConnection(p);
|
DuplexTransportConnection d = createConnection(p);
|
||||||
if (d != null) {
|
if (d != null) {
|
||||||
backoff.reset();
|
backoff.reset();
|
||||||
h.handleConnection(d);
|
if (connectionLimiter.contactConnectionOpened(d))
|
||||||
|
h.handleConnection(d);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -328,6 +325,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
if (isNullOrEmpty(uuid)) return null;
|
if (isNullOrEmpty(uuid)) return null;
|
||||||
DuplexTransportConnection conn = connect(address, uuid);
|
DuplexTransportConnection conn = connect(address, uuid);
|
||||||
if (conn == null) return null;
|
if (conn == null) return null;
|
||||||
|
// TODO: Why don't we reset the backoff here?
|
||||||
return connectionLimiter.contactConnectionOpened(conn) ? conn : null;
|
return connectionLimiter.contactConnectionOpened(conn) ? conn : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.briarproject.bramble.rendezvous;
|
|||||||
|
|
||||||
import org.briarproject.bramble.PoliteExecutor;
|
import org.briarproject.bramble.PoliteExecutor;
|
||||||
import org.briarproject.bramble.api.Pair;
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactState;
|
import org.briarproject.bramble.api.contact.PendingContactState;
|
||||||
@@ -24,6 +23,7 @@ import org.briarproject.bramble.api.lifecycle.Service;
|
|||||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.sync.Ack;
|
import org.briarproject.bramble.api.sync.Ack;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.Offer;
|
import org.briarproject.bramble.api.sync.Offer;
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.api.sync.Request;
|
import org.briarproject.bramble.api.sync.Request;
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
import org.briarproject.bramble.api.sync.SyncSession;
|
||||||
@@ -36,7 +35,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
@@ -76,8 +74,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
|||||||
private final int maxLatency, maxIdleTime;
|
private final int maxLatency, maxIdleTime;
|
||||||
private final StreamWriter streamWriter;
|
private final StreamWriter streamWriter;
|
||||||
private final SyncRecordWriter recordWriter;
|
private final SyncRecordWriter recordWriter;
|
||||||
@Nullable
|
|
||||||
private final Priority priority;
|
|
||||||
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
|
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
|
||||||
|
|
||||||
private final AtomicBoolean generateAckQueued = new AtomicBoolean(false);
|
private final AtomicBoolean generateAckQueued = new AtomicBoolean(false);
|
||||||
@@ -92,7 +88,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
|||||||
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
|
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
|
||||||
EventBus eventBus, Clock clock, ContactId contactId, int maxLatency,
|
EventBus eventBus, Clock clock, ContactId contactId, int maxLatency,
|
||||||
int maxIdleTime, StreamWriter streamWriter,
|
int maxIdleTime, StreamWriter streamWriter,
|
||||||
SyncRecordWriter recordWriter, @Nullable Priority priority) {
|
SyncRecordWriter recordWriter) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
@@ -102,7 +98,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
|||||||
this.maxIdleTime = maxIdleTime;
|
this.maxIdleTime = maxIdleTime;
|
||||||
this.streamWriter = streamWriter;
|
this.streamWriter = streamWriter;
|
||||||
this.recordWriter = recordWriter;
|
this.recordWriter = recordWriter;
|
||||||
this.priority = priority;
|
|
||||||
writerTasks = new LinkedBlockingQueue<>();
|
writerTasks = new LinkedBlockingQueue<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,8 +108,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
|||||||
try {
|
try {
|
||||||
// Send our supported protocol versions
|
// Send our supported protocol versions
|
||||||
recordWriter.writeVersions(new Versions(SUPPORTED_VERSIONS));
|
recordWriter.writeVersions(new Versions(SUPPORTED_VERSIONS));
|
||||||
// Send our connection priority, if this is an outgoing connection
|
|
||||||
if (priority != null) recordWriter.writePriority(priority);
|
|
||||||
// Start a query for each type of record
|
// Start a query for each type of record
|
||||||
generateAck();
|
generateAck();
|
||||||
generateBatch();
|
generateBatch();
|
||||||
@@ -166,9 +159,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
|||||||
dataToFlush = true;
|
dataToFlush = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Write any records that were already in the queue
|
|
||||||
ThrowingRunnable<IOException> task;
|
|
||||||
while ((task = writerTasks.poll()) != null) task.run();
|
|
||||||
streamWriter.sendEndOfStream();
|
streamWriter.sendEndOfStream();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
LOG.info("Interrupted while waiting for a record to write");
|
LOG.info("Interrupted while waiting for a record to write");
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.sync.Ack;
|
import org.briarproject.bramble.api.sync.Ack;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.Offer;
|
import org.briarproject.bramble.api.sync.Offer;
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.api.sync.PriorityHandler;
|
|
||||||
import org.briarproject.bramble.api.sync.Request;
|
import org.briarproject.bramble.api.sync.Request;
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
import org.briarproject.bramble.api.sync.SyncSession;
|
||||||
@@ -49,19 +47,17 @@ class IncomingSession implements SyncSession, EventListener {
|
|||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final SyncRecordReader recordReader;
|
private final SyncRecordReader recordReader;
|
||||||
private final PriorityHandler priorityHandler;
|
|
||||||
|
|
||||||
private volatile boolean interrupted = false;
|
private volatile boolean interrupted = false;
|
||||||
|
|
||||||
IncomingSession(DatabaseComponent db, Executor dbExecutor,
|
IncomingSession(DatabaseComponent db, Executor dbExecutor,
|
||||||
EventBus eventBus, ContactId contactId,
|
EventBus eventBus, ContactId contactId,
|
||||||
SyncRecordReader recordReader, PriorityHandler priorityHandler) {
|
SyncRecordReader recordReader) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
this.recordReader = recordReader;
|
this.recordReader = recordReader;
|
||||||
this.priorityHandler = priorityHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IoExecutor
|
@IoExecutor
|
||||||
@@ -90,9 +86,6 @@ class IncomingSession implements SyncSession, EventListener {
|
|||||||
} else if (recordReader.hasVersions()) {
|
} else if (recordReader.hasVersions()) {
|
||||||
Versions v = recordReader.readVersions();
|
Versions v = recordReader.readVersions();
|
||||||
dbExecutor.execute(new ReceiveVersions(v));
|
dbExecutor.execute(new ReceiveVersions(v));
|
||||||
} else if (recordReader.hasPriority()) {
|
|
||||||
Priority p = recordReader.readPriority();
|
|
||||||
priorityHandler.handle(p);
|
|
||||||
} else {
|
} else {
|
||||||
// unknown records are ignored in RecordReader#eof()
|
// unknown records are ignored in RecordReader#eof()
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
|
|||||||
@@ -95,9 +95,6 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
|
|||||||
if (task == CLOSE) break;
|
if (task == CLOSE) break;
|
||||||
task.run();
|
task.run();
|
||||||
}
|
}
|
||||||
// Write any records that were already in the queue
|
|
||||||
ThrowingRunnable<IOException> task;
|
|
||||||
while ((task = writerTasks.poll()) != null) task.run();
|
|
||||||
streamWriter.sendEndOfStream();
|
streamWriter.sendEndOfStream();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
LOG.info("Interrupted while waiting for a record to write");
|
LOG.info("Interrupted while waiting for a record to write");
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.sync.Message;
|
|||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.Offer;
|
import org.briarproject.bramble.api.sync.Offer;
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.api.sync.Request;
|
import org.briarproject.bramble.api.sync.Request;
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
||||||
import org.briarproject.bramble.api.sync.Versions;
|
import org.briarproject.bramble.api.sync.Versions;
|
||||||
@@ -27,12 +26,10 @@ import javax.annotation.concurrent.NotThreadSafe;
|
|||||||
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
|
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
|
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@@ -51,7 +48,7 @@ class SyncRecordReaderImpl implements SyncRecordReader {
|
|||||||
|
|
||||||
private static boolean isKnownRecordType(byte type) {
|
private static boolean isKnownRecordType(byte type) {
|
||||||
return type == ACK || type == MESSAGE || type == OFFER ||
|
return type == ACK || type == MESSAGE || type == OFFER ||
|
||||||
type == REQUEST || type == VERSIONS || type == PRIORITY;
|
type == REQUEST || type == VERSIONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MessageFactory messageFactory;
|
private final MessageFactory messageFactory;
|
||||||
@@ -177,23 +174,4 @@ class SyncRecordReaderImpl implements SyncRecordReader {
|
|||||||
nextRecord = null;
|
nextRecord = null;
|
||||||
return supported;
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPriority() throws IOException {
|
|
||||||
return !eof() && getNextRecordType() == PRIORITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Priority readPriority() throws IOException {
|
|
||||||
if (!hasPriority()) throw new FormatException();
|
|
||||||
return new Priority(readNonce());
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] readNonce() throws IOException {
|
|
||||||
if (nextRecord == null) throw new AssertionError();
|
|
||||||
byte[] payload = nextRecord.getPayload();
|
|
||||||
if (payload.length != PRIORITY_NONCE_BYTES) throw new FormatException();
|
|
||||||
nextRecord = null;
|
|
||||||
return payload;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.sync.Message;
|
|||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.Offer;
|
import org.briarproject.bramble.api.sync.Offer;
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.api.sync.Request;
|
import org.briarproject.bramble.api.sync.Request;
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
||||||
import org.briarproject.bramble.api.sync.Versions;
|
import org.briarproject.bramble.api.sync.Versions;
|
||||||
@@ -21,7 +20,6 @@ import javax.annotation.concurrent.NotThreadSafe;
|
|||||||
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
|
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
|
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||||
@@ -75,12 +73,6 @@ class SyncRecordWriterImpl implements SyncRecordWriter {
|
|||||||
writeRecord(VERSIONS);
|
writeRecord(VERSIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writePriority(Priority p) throws IOException {
|
|
||||||
writer.writeRecord(
|
|
||||||
new Record(PROTOCOL_VERSION, PRIORITY, p.getNonce()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.api.sync.PriorityHandler;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordReaderFactory;
|
import org.briarproject.bramble.api.sync.SyncRecordReaderFactory;
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
||||||
@@ -20,7 +18,6 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -49,12 +46,10 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SyncSession createIncomingSession(ContactId c, InputStream in,
|
public SyncSession createIncomingSession(ContactId c, InputStream in) {
|
||||||
PriorityHandler handler) {
|
|
||||||
SyncRecordReader recordReader =
|
SyncRecordReader recordReader =
|
||||||
recordReaderFactory.createRecordReader(in);
|
recordReaderFactory.createRecordReader(in);
|
||||||
return new IncomingSession(db, dbExecutor, eventBus, c, recordReader,
|
return new IncomingSession(db, dbExecutor, eventBus, c, recordReader);
|
||||||
handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -69,12 +64,11 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
|
public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
|
||||||
int maxIdleTime, StreamWriter streamWriter,
|
int maxIdleTime, StreamWriter streamWriter) {
|
||||||
@Nullable Priority priority) {
|
|
||||||
OutputStream out = streamWriter.getOutputStream();
|
OutputStream out = streamWriter.getOutputStream();
|
||||||
SyncRecordWriter recordWriter =
|
SyncRecordWriter recordWriter =
|
||||||
recordWriterFactory.createRecordWriter(out);
|
recordWriterFactory.createRecordWriter(out);
|
||||||
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c,
|
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c,
|
||||||
maxLatency, maxIdleTime, streamWriter, recordWriter, priority);
|
maxLatency, maxIdleTime, streamWriter, recordWriter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,600 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.connection.InterruptibleConnection;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
|
||||||
import org.jmock.Expectations;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
import static java.util.Collections.emptyMap;
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.Collections.singletonMap;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
public class ConnectionRegistryImplTest extends BrambleMockTestCase {
|
|
||||||
|
|
||||||
private final EventBus eventBus = context.mock(EventBus.class);
|
|
||||||
private final PluginConfig pluginConfig = context.mock(PluginConfig.class);
|
|
||||||
private final InterruptibleConnection conn1 =
|
|
||||||
context.mock(InterruptibleConnection.class, "conn1");
|
|
||||||
private final InterruptibleConnection conn2 =
|
|
||||||
context.mock(InterruptibleConnection.class, "conn2");
|
|
||||||
private final InterruptibleConnection conn3 =
|
|
||||||
context.mock(InterruptibleConnection.class, "conn3");
|
|
||||||
|
|
||||||
private final ContactId contactId1 = getContactId();
|
|
||||||
private final ContactId contactId2 = getContactId();
|
|
||||||
private final TransportId transportId1 = getTransportId();
|
|
||||||
private final TransportId transportId2 = getTransportId();
|
|
||||||
private final TransportId transportId3 = getTransportId();
|
|
||||||
private final PendingContactId pendingContactId =
|
|
||||||
new PendingContactId(getRandomId());
|
|
||||||
|
|
||||||
private final Priority low =
|
|
||||||
new Priority(fromHexString("00000000000000000000000000000000"));
|
|
||||||
private final Priority high =
|
|
||||||
new Priority(fromHexString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRegisterMultipleConnections() {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(pluginConfig).getTransportPreferences();
|
|
||||||
will(returnValue(emptyMap()));
|
|
||||||
}});
|
|
||||||
|
|
||||||
ConnectionRegistry c =
|
|
||||||
new ConnectionRegistryImpl(eventBus, pluginConfig);
|
|
||||||
|
|
||||||
// The registry should be empty
|
|
||||||
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
assertEquals(emptyList(), c.getConnectedContacts(transportId3));
|
|
||||||
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId3));
|
|
||||||
|
|
||||||
// Check that a registered connection shows up - this should
|
|
||||||
// broadcast a ConnectionOpenedEvent and a ContactConnectedEvent
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn1, true);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// Register another connection with the same contact and transport -
|
|
||||||
// this should broadcast a ConnectionOpenedEvent and lookup should be
|
|
||||||
// unaffected
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn2, true);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// Unregister one of the connections - this should broadcast a
|
|
||||||
// ConnectionClosedEvent and lookup should be unaffected
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.unregisterConnection(contactId1, transportId1, conn1, true, false);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// Unregister the other connection - this should broadcast a
|
|
||||||
// ConnectionClosedEvent and a ContactDisconnectedEvent
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
|
|
||||||
oneOf(eventBus).broadcast(with(any(
|
|
||||||
ContactDisconnectedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.unregisterConnection(contactId1, transportId1, conn2, true, false);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// Try to unregister the connection again - exception should be thrown
|
|
||||||
try {
|
|
||||||
c.unregisterConnection(contactId1, transportId1, conn2,
|
|
||||||
true, false);
|
|
||||||
fail();
|
|
||||||
} catch (IllegalArgumentException expected) {
|
|
||||||
// Expected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRegisterMultipleContacts() {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(pluginConfig).getTransportPreferences();
|
|
||||||
will(returnValue(emptyMap()));
|
|
||||||
}});
|
|
||||||
|
|
||||||
ConnectionRegistry c =
|
|
||||||
new ConnectionRegistryImpl(eventBus, pluginConfig);
|
|
||||||
|
|
||||||
// Register two contacts with one transport, then one of the contacts
|
|
||||||
// with a second transport - this should broadcast three
|
|
||||||
// ConnectionOpenedEvents and two ContactConnectedEvents
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
exactly(3).of(eventBus).broadcast(with(any(
|
|
||||||
ConnectionOpenedEvent.class)));
|
|
||||||
exactly(2).of(eventBus).broadcast(with(any(
|
|
||||||
ContactConnectedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn1, true);
|
|
||||||
c.registerConnection(contactId2, transportId1, conn2, true);
|
|
||||||
c.registerConnection(contactId2, transportId2, conn3, true);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
Collection<ContactId> connected = c.getConnectedContacts(transportId1);
|
|
||||||
assertEquals(2, connected.size());
|
|
||||||
assertTrue(connected.contains(contactId1));
|
|
||||||
assertTrue(connected.contains(contactId2));
|
|
||||||
|
|
||||||
connected = c.getConnectedOrBetterContacts(transportId1);
|
|
||||||
assertEquals(2, connected.size());
|
|
||||||
assertTrue(connected.contains(contactId1));
|
|
||||||
assertTrue(connected.contains(contactId2));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId2),
|
|
||||||
c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(singletonList(contactId2),
|
|
||||||
c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testConnectionsAreNotInterruptedUnlessPriorityIsSet() {
|
|
||||||
// Prefer transport 2 to transport 1
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(pluginConfig).getTransportPreferences();
|
|
||||||
will(returnValue(
|
|
||||||
singletonMap(transportId1, singletonList(transportId2))));
|
|
||||||
}});
|
|
||||||
|
|
||||||
ConnectionRegistry c =
|
|
||||||
new ConnectionRegistryImpl(eventBus, pluginConfig);
|
|
||||||
|
|
||||||
// Connect via transport 1 (worse than 2) and set priority to low
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn1, true);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
// Connect via transport 2 (better than 1) and set priority to high -
|
|
||||||
// the old connection should not be interrupted, despite using a worse
|
|
||||||
// transport, to remain compatible with old peers
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId2, conn2, true);
|
|
||||||
c.setPriority(contactId1, transportId2, conn2, high);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
// Connect via transport 3 (no preference) and set priority to high -
|
|
||||||
// again, no interruptions are expected
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId3, conn3, true);
|
|
||||||
c.setPriority(contactId1, transportId3, conn3, high);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId3));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId3));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNewConnectionIsInterruptedIfOldConnectionUsesBetterTransport() {
|
|
||||||
// Prefer transport 1 to transport 2
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(pluginConfig).getTransportPreferences();
|
|
||||||
will(returnValue(
|
|
||||||
singletonMap(transportId2, singletonList(transportId1))));
|
|
||||||
}});
|
|
||||||
|
|
||||||
ConnectionRegistry c =
|
|
||||||
new ConnectionRegistryImpl(eventBus, pluginConfig);
|
|
||||||
|
|
||||||
// Connect via transport 1 (better than 2) and set priority to low
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn1, true);
|
|
||||||
c.setPriority(contactId1, transportId1, conn1, low);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// The contact is not connected via transport 2 but is connected via a
|
|
||||||
// better transport
|
|
||||||
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
// Connect via transport 2 (worse than 1) and set priority to high -
|
|
||||||
// the new connection should be interrupted because it uses a worse
|
|
||||||
// transport
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(conn2).interruptOutgoingSession();
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId2, conn2, true);
|
|
||||||
c.setPriority(contactId1, transportId2, conn2, high);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
// Connect via transport 3 (no preference) and set priority to low -
|
|
||||||
// no further interruptions
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId3, conn3, true);
|
|
||||||
c.setPriority(contactId1, transportId3, conn3, low);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId3));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId3));
|
|
||||||
|
|
||||||
// Unregister the interrupted connection (transport 2)
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.unregisterConnection(contactId1, transportId2, conn2, true, false);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// The contact is not connected via transport 2 but is connected via a
|
|
||||||
// better transport
|
|
||||||
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId3));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId3));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOldConnectionIsInterruptedIfNewConnectionUsesBetterTransport() {
|
|
||||||
// Prefer transport 2 to transport 1
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(pluginConfig).getTransportPreferences();
|
|
||||||
will(returnValue(
|
|
||||||
singletonMap(transportId1, singletonList(transportId2))));
|
|
||||||
}});
|
|
||||||
|
|
||||||
ConnectionRegistry c =
|
|
||||||
new ConnectionRegistryImpl(eventBus, pluginConfig);
|
|
||||||
|
|
||||||
// Connect via transport 1 (worse than 2) and set priority to high
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn1, true);
|
|
||||||
c.setPriority(contactId1, transportId1, conn1, high);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
// Connect via transport 2 (better than 1) and set priority to low -
|
|
||||||
// the old connection should be interrupted because it uses a worse
|
|
||||||
// transport
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(conn1).interruptOutgoingSession();
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId2, conn2, true);
|
|
||||||
c.setPriority(contactId1, transportId2, conn2, low);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
// Connect via transport 3 (no preference) and set priority to high -
|
|
||||||
// no further interruptions
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId3, conn3, true);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId3));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId3));
|
|
||||||
|
|
||||||
// Unregister the interrupted connection (transport 1)
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.unregisterConnection(contactId1, transportId1, conn1, true, false);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
// The contact is not connected via transport 1 but is connected via a
|
|
||||||
// better transport
|
|
||||||
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId2));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId2));
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId3));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId3));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNewConnectionIsInterruptedIfOldConnectionHasHigherPriority() {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(pluginConfig).getTransportPreferences();
|
|
||||||
will(returnValue(emptyMap()));
|
|
||||||
}});
|
|
||||||
|
|
||||||
ConnectionRegistry c =
|
|
||||||
new ConnectionRegistryImpl(eventBus, pluginConfig);
|
|
||||||
|
|
||||||
// Register a connection with high priority
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn1, true);
|
|
||||||
c.setPriority(contactId1, transportId1, conn1, high);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// Register another connection via the same transport (no priority yet)
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn2, true);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// Set the priority of the second connection to low - the second
|
|
||||||
// connection should be interrupted
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(conn2).interruptOutgoingSession();
|
|
||||||
}});
|
|
||||||
c.setPriority(contactId1, transportId1, conn2, low);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// Register a third connection with low priority - it should also be
|
|
||||||
// interrupted
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(conn3).interruptOutgoingSession();
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn3, true);
|
|
||||||
c.setPriority(contactId1, transportId1, conn3, low);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOldConnectionIsInterruptedIfNewConnectionHasHigherPriority() {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(pluginConfig).getTransportPreferences();
|
|
||||||
will(returnValue(emptyMap()));
|
|
||||||
}});
|
|
||||||
|
|
||||||
ConnectionRegistry c =
|
|
||||||
new ConnectionRegistryImpl(eventBus, pluginConfig);
|
|
||||||
|
|
||||||
// Register a connection with low priority
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn1, true);
|
|
||||||
c.setPriority(contactId1, transportId1, conn1, low);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// Register another connection via the same transport (no priority yet)
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.registerConnection(contactId1, transportId1, conn2, true);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
|
|
||||||
// Set the priority of the second connection to high - the first
|
|
||||||
// connection should be interrupted
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(conn1).interruptOutgoingSession();
|
|
||||||
}});
|
|
||||||
c.setPriority(contactId1, transportId1, conn2, high);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedContacts(transportId1));
|
|
||||||
assertEquals(singletonList(contactId1),
|
|
||||||
c.getConnectedOrBetterContacts(transportId1));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRegisterAndUnregisterPendingContacts() {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(pluginConfig).getTransportPreferences();
|
|
||||||
will(returnValue(emptyMap()));
|
|
||||||
}});
|
|
||||||
|
|
||||||
ConnectionRegistry c =
|
|
||||||
new ConnectionRegistryImpl(eventBus, pluginConfig);
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(
|
|
||||||
RendezvousConnectionOpenedEvent.class)));
|
|
||||||
}});
|
|
||||||
assertTrue(c.registerConnection(pendingContactId));
|
|
||||||
assertFalse(c.registerConnection(pendingContactId)); // Redundant
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(any(
|
|
||||||
RendezvousConnectionClosedEvent.class)));
|
|
||||||
}});
|
|
||||||
c.unregisterConnection(pendingContactId, true);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
try {
|
|
||||||
c.unregisterConnection(pendingContactId, true);
|
|
||||||
fail();
|
|
||||||
} catch (IllegalArgumentException expected) {
|
|
||||||
// Expected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,13 +2,13 @@ package org.briarproject.bramble.contact;
|
|||||||
|
|
||||||
import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
||||||
import org.briarproject.bramble.BrambleCoreModule;
|
import org.briarproject.bramble.BrambleCoreModule;
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|||||||
@@ -1,143 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.briarproject.bramble.test.SettableClock;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
public class TimeoutInputStreamTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
private static final long TIMEOUT_MS = MINUTES.toMillis(1);
|
|
||||||
|
|
||||||
private final long now = System.currentTimeMillis();
|
|
||||||
|
|
||||||
private AtomicLong time;
|
|
||||||
private UnresponsiveInputStream in;
|
|
||||||
private AtomicBoolean listenerCalled;
|
|
||||||
private TimeoutInputStream stream;
|
|
||||||
private CountDownLatch readReturned;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
time = new AtomicLong(now);
|
|
||||||
in = new UnresponsiveInputStream();
|
|
||||||
listenerCalled = new AtomicBoolean(false);
|
|
||||||
stream = new TimeoutInputStream(new SettableClock(time), in,
|
|
||||||
TIMEOUT_MS, stream -> listenerCalled.set(true));
|
|
||||||
readReturned = new CountDownLatch(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTimeoutIsReportedIfReadDoesNotReturn() throws Exception {
|
|
||||||
startReading();
|
|
||||||
try {
|
|
||||||
// The stream should not report a timeout
|
|
||||||
assertFalse(stream.hasTimedOut());
|
|
||||||
|
|
||||||
// Time passes
|
|
||||||
time.set(now + TIMEOUT_MS);
|
|
||||||
|
|
||||||
// The stream still shouldn't report a timeout
|
|
||||||
assertFalse(stream.hasTimedOut());
|
|
||||||
|
|
||||||
// Time passes
|
|
||||||
time.set(now + TIMEOUT_MS + 1);
|
|
||||||
|
|
||||||
// The stream should report a timeout
|
|
||||||
assertTrue(stream.hasTimedOut());
|
|
||||||
|
|
||||||
// The listener should not have been called yet
|
|
||||||
assertFalse(listenerCalled.get());
|
|
||||||
|
|
||||||
// Close the stream
|
|
||||||
stream.close();
|
|
||||||
|
|
||||||
// The listener should have been called
|
|
||||||
assertTrue(listenerCalled.get());
|
|
||||||
} finally {
|
|
||||||
// Allow the read to return
|
|
||||||
in.readFinished.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTimeoutIsNotReportedIfReadReturns() throws Exception {
|
|
||||||
startReading();
|
|
||||||
try {
|
|
||||||
// The stream should not report a timeout
|
|
||||||
assertFalse(stream.hasTimedOut());
|
|
||||||
|
|
||||||
// Time passes
|
|
||||||
time.set(now + TIMEOUT_MS);
|
|
||||||
|
|
||||||
// The stream still shouldn't report a timeout
|
|
||||||
assertFalse(stream.hasTimedOut());
|
|
||||||
|
|
||||||
// Allow the read to finish and wait for it to return
|
|
||||||
in.readFinished.countDown();
|
|
||||||
readReturned.await(10, SECONDS);
|
|
||||||
|
|
||||||
// Time passes
|
|
||||||
time.set(now + TIMEOUT_MS + 1);
|
|
||||||
|
|
||||||
// The stream should not report a timeout as the read has returned
|
|
||||||
assertFalse(stream.hasTimedOut());
|
|
||||||
|
|
||||||
// The listener should not have been called yet
|
|
||||||
assertFalse(listenerCalled.get());
|
|
||||||
|
|
||||||
// Close the stream
|
|
||||||
stream.close();
|
|
||||||
|
|
||||||
// The listener should have been called
|
|
||||||
assertTrue(listenerCalled.get());
|
|
||||||
} finally {
|
|
||||||
// Allow the read to return in case an assertion was thrown
|
|
||||||
in.readFinished.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startReading() throws Exception {
|
|
||||||
// Start a background thread to read from the unresponsive stream
|
|
||||||
new Thread(() -> {
|
|
||||||
try {
|
|
||||||
assertEquals(123, stream.read());
|
|
||||||
readReturned.countDown();
|
|
||||||
} catch (IOException e) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
// Wait for the background thread to start reading
|
|
||||||
assertTrue(in.readStarted.await(10, SECONDS));
|
|
||||||
}
|
|
||||||
|
|
||||||
private class UnresponsiveInputStream extends InputStream {
|
|
||||||
|
|
||||||
private final CountDownLatch readStarted = new CountDownLatch(1);
|
|
||||||
private final CountDownLatch readFinished = new CountDownLatch(1);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
readStarted.countDown();
|
|
||||||
try {
|
|
||||||
readFinished.await();
|
|
||||||
return 123;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class ConnectionRegistryImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private final EventBus eventBus = context.mock(EventBus.class);
|
||||||
|
private final Clock clock = context.mock(Clock.class);
|
||||||
|
private final ScheduledExecutorService scheduler =
|
||||||
|
context.mock(ScheduledExecutorService.class);
|
||||||
|
|
||||||
|
private final ContactId contactId = getContactId();
|
||||||
|
private final ContactId contactId1 = getContactId();
|
||||||
|
private final TransportId transportId = getTransportId();
|
||||||
|
private final TransportId transportId1 = getTransportId();
|
||||||
|
private final PendingContactId pendingContactId =
|
||||||
|
new PendingContactId(getRandomId());
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegisterAndUnregister() {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(scheduler).scheduleWithFixedDelay(with(any(Runnable.class)),
|
||||||
|
with(10_000L), with(10_000L), with(MILLISECONDS));
|
||||||
|
}});
|
||||||
|
|
||||||
|
ConnectionRegistry c = new ConnectionRegistryImpl(eventBus, clock,
|
||||||
|
scheduler);
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// The registry should be empty
|
||||||
|
assertEquals(emptyList(), c.getConnectedContacts(transportId));
|
||||||
|
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
|
||||||
|
|
||||||
|
// Check that a registered connection shows up - this should
|
||||||
|
// broadcast a ConnectionOpenedEvent and a ConnectionStatusChangedEvent
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
||||||
|
oneOf(eventBus).broadcast(with(any(
|
||||||
|
ConnectionStatusChangedEvent.class)));
|
||||||
|
}});
|
||||||
|
c.registerConnection(contactId, transportId, true);
|
||||||
|
assertEquals(singletonList(contactId),
|
||||||
|
c.getConnectedContacts(transportId));
|
||||||
|
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Register an identical connection - this should broadcast a
|
||||||
|
// ConnectionOpenedEvent and lookup should be unaffected
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
|
||||||
|
}});
|
||||||
|
c.registerConnection(contactId, transportId, true);
|
||||||
|
assertEquals(singletonList(contactId),
|
||||||
|
c.getConnectedContacts(transportId));
|
||||||
|
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Unregister one of the connections - this should broadcast a
|
||||||
|
// ConnectionClosedEvent and lookup should be unaffected
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
|
||||||
|
}});
|
||||||
|
c.unregisterConnection(contactId, transportId, true);
|
||||||
|
assertEquals(singletonList(contactId),
|
||||||
|
c.getConnectedContacts(transportId));
|
||||||
|
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Unregister the other connection - this should broadcast a
|
||||||
|
// ConnectionClosedEvent and a ConnectionStatusChangedEvent
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(System.currentTimeMillis()));
|
||||||
|
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
|
||||||
|
oneOf(eventBus).broadcast(with(any(
|
||||||
|
ConnectionStatusChangedEvent.class)));
|
||||||
|
}});
|
||||||
|
c.unregisterConnection(contactId, transportId, true);
|
||||||
|
assertEquals(emptyList(), c.getConnectedContacts(transportId));
|
||||||
|
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Try to unregister the connection again - exception should be thrown
|
||||||
|
try {
|
||||||
|
c.unregisterConnection(contactId, transportId, true);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register both contacts with one transport, one contact with both -
|
||||||
|
// this should broadcast three ConnectionOpenedEvents and two
|
||||||
|
// ConnectionStatusChangedEvents
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
exactly(3).of(eventBus).broadcast(with(any(
|
||||||
|
ConnectionOpenedEvent.class)));
|
||||||
|
exactly(2).of(eventBus).broadcast(with(any(
|
||||||
|
ConnectionStatusChangedEvent.class)));
|
||||||
|
}});
|
||||||
|
c.registerConnection(contactId, transportId, true);
|
||||||
|
c.registerConnection(contactId1, transportId, true);
|
||||||
|
c.registerConnection(contactId1, transportId1, true);
|
||||||
|
Collection<ContactId> connected = c.getConnectedContacts(transportId);
|
||||||
|
assertEquals(2, connected.size());
|
||||||
|
assertTrue(connected.contains(contactId));
|
||||||
|
assertTrue(connected.contains(contactId1));
|
||||||
|
assertEquals(singletonList(contactId1),
|
||||||
|
c.getConnectedContacts(transportId1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegisterAndUnregisterPendingContacts() {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(scheduler).scheduleWithFixedDelay(with(any(Runnable.class)),
|
||||||
|
with(10_000L), with(10_000L), with(MILLISECONDS));
|
||||||
|
}});
|
||||||
|
|
||||||
|
ConnectionRegistry c = new ConnectionRegistryImpl(eventBus, clock,
|
||||||
|
scheduler);
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).broadcast(with(any(
|
||||||
|
RendezvousConnectionOpenedEvent.class)));
|
||||||
|
}});
|
||||||
|
assertTrue(c.registerConnection(pendingContactId));
|
||||||
|
assertFalse(c.registerConnection(pendingContactId)); // Redundant
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).broadcast(with(any(
|
||||||
|
RendezvousConnectionClosedEvent.class)));
|
||||||
|
}});
|
||||||
|
c.unregisterConnection(pendingContactId, true);
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
try {
|
||||||
|
c.unregisterConnection(pendingContactId, true);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
import org.briarproject.bramble.api.plugin.PluginException;
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
||||||
@@ -157,21 +157,7 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRescheduleOnOutgoingConnectionClosed() {
|
public void testRescheduleAndReconnectOnConnectionClosed()
|
||||||
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(plugin).getId();
|
|
||||||
will(returnValue(transportId));
|
|
||||||
}});
|
|
||||||
expectReschedule(plugin);
|
|
||||||
|
|
||||||
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
|
|
||||||
false, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRescheduleAndReconnectOnOutgoingConnectionFailed()
|
|
||||||
throws Exception {
|
throws Exception {
|
||||||
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
|
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
|
||||||
DuplexTransportConnection duplexConnection =
|
DuplexTransportConnection duplexConnection =
|
||||||
@@ -180,40 +166,45 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
allowing(plugin).getId();
|
allowing(plugin).getId();
|
||||||
will(returnValue(transportId));
|
will(returnValue(transportId));
|
||||||
|
// reschedule()
|
||||||
|
// Get the plugin
|
||||||
|
oneOf(pluginManager).getPlugin(transportId);
|
||||||
|
will(returnValue(plugin));
|
||||||
|
// The plugin supports polling
|
||||||
|
oneOf(plugin).shouldPoll();
|
||||||
|
will(returnValue(true));
|
||||||
|
// Get the plugin
|
||||||
|
oneOf(pluginManager).getPlugin(transportId);
|
||||||
|
will(returnValue(plugin));
|
||||||
|
// The plugin supports polling
|
||||||
|
oneOf(plugin).shouldPoll();
|
||||||
|
will(returnValue(true));
|
||||||
|
// Schedule the next poll
|
||||||
|
oneOf(plugin).getPollingInterval();
|
||||||
|
will(returnValue(pollingInterval));
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(now));
|
||||||
|
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
||||||
|
with((long) pollingInterval), with(MILLISECONDS));
|
||||||
|
will(returnValue(future));
|
||||||
|
// connectToContact()
|
||||||
|
// Check whether the contact is already connected
|
||||||
|
oneOf(connectionRegistry).isConnected(contactId, transportId);
|
||||||
|
will(returnValue(false));
|
||||||
|
// Get the transport properties
|
||||||
|
oneOf(transportPropertyManager).getRemoteProperties(contactId,
|
||||||
|
transportId);
|
||||||
|
will(returnValue(properties));
|
||||||
|
// Connect to the contact
|
||||||
|
oneOf(plugin).createConnection(properties);
|
||||||
|
will(returnValue(duplexConnection));
|
||||||
|
// Pass the connection to the connection manager
|
||||||
|
oneOf(connectionManager).manageOutgoingConnection(contactId,
|
||||||
|
transportId, duplexConnection);
|
||||||
}});
|
}});
|
||||||
expectReschedule(plugin);
|
|
||||||
expectReconnect(plugin, duplexConnection);
|
|
||||||
|
|
||||||
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
|
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
|
||||||
false, true));
|
false));
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRescheduleOnIncomingConnectionClosed() {
|
|
||||||
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(plugin).getId();
|
|
||||||
will(returnValue(transportId));
|
|
||||||
}});
|
|
||||||
expectReschedule(plugin);
|
|
||||||
|
|
||||||
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
|
|
||||||
true, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRescheduleOnIncomingConnectionFailed() {
|
|
||||||
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(plugin).getId();
|
|
||||||
will(returnValue(transportId));
|
|
||||||
}});
|
|
||||||
expectReschedule(plugin);
|
|
||||||
|
|
||||||
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
|
|
||||||
true, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -363,7 +354,7 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
// Get the transport properties and connected contacts
|
// Get the transport properties and connected contacts
|
||||||
oneOf(transportPropertyManager).getRemoteProperties(transportId);
|
oneOf(transportPropertyManager).getRemoteProperties(transportId);
|
||||||
will(returnValue(singletonMap(contactId, properties)));
|
will(returnValue(singletonMap(contactId, properties)));
|
||||||
oneOf(connectionRegistry).getConnectedOrBetterContacts(transportId);
|
oneOf(connectionRegistry).getConnectedContacts(transportId);
|
||||||
will(returnValue(emptyList()));
|
will(returnValue(emptyList()));
|
||||||
// Poll the plugin
|
// Poll the plugin
|
||||||
oneOf(plugin).poll(with(collectionOf(
|
oneOf(plugin).poll(with(collectionOf(
|
||||||
@@ -406,7 +397,7 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
// Get the transport properties and connected contacts
|
// Get the transport properties and connected contacts
|
||||||
oneOf(transportPropertyManager).getRemoteProperties(transportId);
|
oneOf(transportPropertyManager).getRemoteProperties(transportId);
|
||||||
will(returnValue(singletonMap(contactId, properties)));
|
will(returnValue(singletonMap(contactId, properties)));
|
||||||
oneOf(connectionRegistry).getConnectedOrBetterContacts(transportId);
|
oneOf(connectionRegistry).getConnectedContacts(transportId);
|
||||||
will(returnValue(singletonList(contactId)));
|
will(returnValue(singletonList(contactId)));
|
||||||
// All contacts are connected, so don't poll the plugin
|
// All contacts are connected, so don't poll the plugin
|
||||||
}});
|
}});
|
||||||
@@ -440,48 +431,4 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
poller.eventOccurred(new TransportEnabledEvent(transportId));
|
poller.eventOccurred(new TransportEnabledEvent(transportId));
|
||||||
poller.eventOccurred(new TransportDisabledEvent(transportId));
|
poller.eventOccurred(new TransportDisabledEvent(transportId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expectReschedule(Plugin plugin) {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
// Get the plugin
|
|
||||||
oneOf(pluginManager).getPlugin(transportId);
|
|
||||||
will(returnValue(plugin));
|
|
||||||
// The plugin supports polling
|
|
||||||
oneOf(plugin).shouldPoll();
|
|
||||||
will(returnValue(true));
|
|
||||||
// Schedule the next poll
|
|
||||||
oneOf(plugin).getPollingInterval();
|
|
||||||
will(returnValue(pollingInterval));
|
|
||||||
oneOf(clock).currentTimeMillis();
|
|
||||||
will(returnValue(now));
|
|
||||||
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
|
||||||
with((long) pollingInterval), with(MILLISECONDS));
|
|
||||||
will(returnValue(future));
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void expectReconnect(DuplexPlugin plugin,
|
|
||||||
DuplexTransportConnection duplexConnection) throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
// Get the plugin
|
|
||||||
oneOf(pluginManager).getPlugin(transportId);
|
|
||||||
will(returnValue(plugin));
|
|
||||||
// The plugin supports polling
|
|
||||||
oneOf(plugin).shouldPoll();
|
|
||||||
will(returnValue(true));
|
|
||||||
// Check whether the contact is already connected
|
|
||||||
oneOf(connectionRegistry).isConnected(contactId, transportId);
|
|
||||||
will(returnValue(false));
|
|
||||||
// Get the transport properties
|
|
||||||
oneOf(transportPropertyManager).getRemoteProperties(contactId,
|
|
||||||
transportId);
|
|
||||||
will(returnValue(properties));
|
|
||||||
// Connect to the contact
|
|
||||||
oneOf(plugin).createConnection(properties);
|
|
||||||
will(returnValue(duplexConnection));
|
|
||||||
// Pass the connection to the connection manager
|
|
||||||
oneOf(connectionManager).manageOutgoingConnection(contactId,
|
|
||||||
transportId, duplexConnection);
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.rendezvous;
|
package org.briarproject.bramble.rendezvous;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactState;
|
import org.briarproject.bramble.api.contact.PendingContactState;
|
||||||
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
|
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
|
||||||
@@ -14,6 +13,7 @@ import org.briarproject.bramble.api.db.Transaction;
|
|||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.record.RecordReader;
|
|||||||
import org.briarproject.bramble.api.sync.Ack;
|
import org.briarproject.bramble.api.sync.Ack;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.Offer;
|
import org.briarproject.bramble.api.sync.Offer;
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.api.sync.Request;
|
import org.briarproject.bramble.api.sync.Request;
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
||||||
import org.briarproject.bramble.api.sync.Versions;
|
import org.briarproject.bramble.api.sync.Versions;
|
||||||
@@ -25,14 +24,11 @@ import javax.annotation.Nullable;
|
|||||||
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
|
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
|
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -123,31 +119,6 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
|||||||
reader.readVersions();
|
reader.readVersions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
|
||||||
public void testFormatExceptionIfPriorityNonceIsTooSmall()
|
|
||||||
throws Exception {
|
|
||||||
expectReadRecord(createPriority(PRIORITY_NONCE_BYTES - 1));
|
|
||||||
|
|
||||||
reader.readPriority();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
|
||||||
public void testFormatExceptionIfPriorityNonceIsTooLarge()
|
|
||||||
throws Exception {
|
|
||||||
expectReadRecord(createPriority(PRIORITY_NONCE_BYTES + 1));
|
|
||||||
|
|
||||||
reader.readPriority();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNoFormatExceptionIfPriorityNonceIsCorrectSize()
|
|
||||||
throws Exception {
|
|
||||||
expectReadRecord(createPriority(PRIORITY_NONCE_BYTES));
|
|
||||||
|
|
||||||
Priority priority = reader.readPriority();
|
|
||||||
assertEquals(PRIORITY_NONCE_BYTES, priority.getNonce().length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEofReturnsTrueWhenAtEndOfStream() throws Exception {
|
public void testEofReturnsTrueWhenAtEndOfStream() throws Exception {
|
||||||
expectReadRecord(createAck());
|
expectReadRecord(createAck());
|
||||||
@@ -202,11 +173,6 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
|||||||
return new Record(PROTOCOL_VERSION, VERSIONS, payload);
|
return new Record(PROTOCOL_VERSION, VERSIONS, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Record createPriority(int nonceBytes) {
|
|
||||||
byte[] payload = getRandomBytes(nonceBytes);
|
|
||||||
return new Record(PROTOCOL_VERSION, PRIORITY, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createPayload() throws Exception {
|
private byte[] createPayload() throws Exception {
|
||||||
ByteArrayOutputStream payload = new ByteArrayOutputStream();
|
ByteArrayOutputStream payload = new ByteArrayOutputStream();
|
||||||
while (payload.size() + UniqueId.LENGTH <= MAX_RECORD_PAYLOAD_BYTES) {
|
while (payload.size() + UniqueId.LENGTH <= MAX_RECORD_PAYLOAD_BYTES) {
|
||||||
|
|||||||
@@ -10,15 +10,12 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
|||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
import static java.util.Collections.emptyMap;
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
|
|
||||||
@@ -88,12 +85,6 @@ public class TestPluginConfigModule {
|
|||||||
public boolean shouldPoll() {
|
public boolean shouldPoll() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<TransportId, List<TransportId>> getTransportPreferences() {
|
|
||||||
return emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
return pluginConfig;
|
return pluginConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
|
||||||
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory;
|
import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory;
|
||||||
@@ -20,8 +16,6 @@ import org.briarproject.bramble.plugin.tcp.WanTcpPluginFactory;
|
|||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@@ -29,8 +23,6 @@ import dagger.Provides;
|
|||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.Collections.singletonMap;
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class DesktopPluginModule extends PluginModule {
|
public class DesktopPluginModule extends PluginModule {
|
||||||
@@ -39,10 +31,10 @@ public class DesktopPluginModule extends PluginModule {
|
|||||||
PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor,
|
PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor,
|
||||||
SecureRandom random, BackoffFactory backoffFactory,
|
SecureRandom random, BackoffFactory backoffFactory,
|
||||||
ReliabilityLayerFactory reliabilityFactory,
|
ReliabilityLayerFactory reliabilityFactory,
|
||||||
ShutdownManager shutdownManager, EventBus eventBus,
|
ShutdownManager shutdownManager, EventBus eventBus) {
|
||||||
TimeoutMonitor timeoutMonitor) {
|
DuplexPluginFactory bluetooth =
|
||||||
DuplexPluginFactory bluetooth = new JavaBluetoothPluginFactory(
|
new JavaBluetoothPluginFactory(ioExecutor, random, eventBus,
|
||||||
ioExecutor, random, eventBus, timeoutMonitor, backoffFactory);
|
backoffFactory);
|
||||||
DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
|
DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
|
||||||
reliabilityFactory);
|
reliabilityFactory);
|
||||||
DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor,
|
DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor,
|
||||||
@@ -68,13 +60,6 @@ public class DesktopPluginModule extends PluginModule {
|
|||||||
public boolean shouldPoll() {
|
public boolean shouldPoll() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<TransportId, List<TransportId>> getTransportPreferences() {
|
|
||||||
// Prefer LAN to Bluetooth
|
|
||||||
return singletonMap(BluetoothConstants.ID,
|
|
||||||
singletonList(LanTcpConstants.ID));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
return pluginConfig;
|
return pluginConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.plugin.bluetooth;
|
package org.briarproject.bramble.plugin.bluetooth;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
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;
|
||||||
@@ -35,11 +34,10 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
|
|||||||
private volatile LocalDevice localDevice = null;
|
private volatile LocalDevice localDevice = null;
|
||||||
|
|
||||||
JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager,
|
JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager,
|
||||||
TimeoutMonitor timeoutMonitor, Executor ioExecutor,
|
Executor ioExecutor, SecureRandom secureRandom,
|
||||||
SecureRandom secureRandom, Backoff backoff,
|
Backoff backoff, PluginCallback callback, int maxLatency) {
|
||||||
PluginCallback callback, int maxLatency, int maxIdleTime) {
|
super(connectionManager, ioExecutor, secureRandom, backoff, callback,
|
||||||
super(connectionManager, timeoutMonitor, ioExecutor, secureRandom,
|
maxLatency);
|
||||||
backoff, callback, maxLatency, maxIdleTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -121,9 +119,7 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
|
|||||||
return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
|
return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
|
||||||
}
|
}
|
||||||
|
|
||||||
private DuplexTransportConnection wrapSocket(StreamConnection s)
|
private DuplexTransportConnection wrapSocket(StreamConnection s) {
|
||||||
throws IOException {
|
return new JavaBluetoothTransportConnection(this, connectionLimiter, s);
|
||||||
return new JavaBluetoothTransportConnection(this, connectionLimiter,
|
|
||||||
timeoutMonitor, s);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble.plugin.bluetooth;
|
package org.briarproject.bramble.plugin.bluetooth;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
@@ -22,25 +21,22 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
|||||||
public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
|
public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
|
||||||
|
|
||||||
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
||||||
private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
|
|
||||||
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
|
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
|
||||||
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
||||||
private static final double BACKOFF_BASE = 1.2;
|
private static final double BACKOFF_BASE = 1.2;
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
private final EventBus eventBus;
|
|
||||||
private final TimeoutMonitor timeoutMonitor;
|
|
||||||
private final BackoffFactory backoffFactory;
|
private final BackoffFactory backoffFactory;
|
||||||
|
private final EventBus eventBus;
|
||||||
|
|
||||||
public JavaBluetoothPluginFactory(Executor ioExecutor,
|
public JavaBluetoothPluginFactory(Executor ioExecutor,
|
||||||
SecureRandom secureRandom, EventBus eventBus,
|
SecureRandom secureRandom, EventBus eventBus,
|
||||||
TimeoutMonitor timeoutMonitor, BackoffFactory backoffFactory) {
|
BackoffFactory backoffFactory) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.secureRandom = secureRandom;
|
this.secureRandom = secureRandom;
|
||||||
this.eventBus = eventBus;
|
|
||||||
this.timeoutMonitor = timeoutMonitor;
|
|
||||||
this.backoffFactory = backoffFactory;
|
this.backoffFactory = backoffFactory;
|
||||||
|
this.eventBus = eventBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,8 +56,7 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
|
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
|
||||||
timeoutMonitor, ioExecutor, secureRandom, backoff, callback,
|
ioExecutor, secureRandom, backoff, callback, MAX_LATENCY);
|
||||||
MAX_LATENCY, MAX_IDLE_TIME);
|
|
||||||
eventBus.addListener(plugin);
|
eventBus.addListener(plugin);
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.plugin.bluetooth;
|
package org.briarproject.bramble.plugin.bluetooth;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
||||||
@@ -15,24 +14,20 @@ import javax.microedition.io.StreamConnection;
|
|||||||
class JavaBluetoothTransportConnection
|
class JavaBluetoothTransportConnection
|
||||||
extends AbstractDuplexTransportConnection {
|
extends AbstractDuplexTransportConnection {
|
||||||
|
|
||||||
private final BluetoothConnectionLimiter connectionLimiter;
|
private final BluetoothConnectionLimiter connectionManager;
|
||||||
private final StreamConnection stream;
|
private final StreamConnection stream;
|
||||||
private final InputStream in;
|
|
||||||
|
|
||||||
JavaBluetoothTransportConnection(Plugin plugin,
|
JavaBluetoothTransportConnection(Plugin plugin,
|
||||||
BluetoothConnectionLimiter connectionLimiter,
|
BluetoothConnectionLimiter connectionManager,
|
||||||
TimeoutMonitor timeoutMonitor,
|
StreamConnection stream) {
|
||||||
StreamConnection stream) throws IOException {
|
|
||||||
super(plugin);
|
super(plugin);
|
||||||
this.connectionLimiter = connectionLimiter;
|
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
in = timeoutMonitor.createTimeoutInputStream(
|
this.connectionManager = connectionManager;
|
||||||
stream.openInputStream(), plugin.getMaxIdleTime() * 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InputStream getInputStream() {
|
protected InputStream getInputStream() throws IOException {
|
||||||
return in;
|
return stream.openInputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -45,7 +40,7 @@ class JavaBluetoothTransportConnection
|
|||||||
try {
|
try {
|
||||||
stream.close();
|
stream.close();
|
||||||
} finally {
|
} finally {
|
||||||
connectionLimiter.connectionClosed(this);
|
connectionManager.connectionClosed(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package org.briarproject.briar.android;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.briar.api.test.TestDataCreator;
|
import org.briarproject.briar.api.test.TestDataCreator;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
|
|||||||
@@ -120,8 +120,7 @@ public class SetupDataTest extends ScreenshotTest {
|
|||||||
|
|
||||||
// TODO add messages
|
// TODO add messages
|
||||||
|
|
||||||
connectionRegistry.registerConnection(bob.getId(), ID, () -> {
|
connectionRegistry.registerConnection(bob.getId(), ID, true);
|
||||||
}, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.briarproject.bramble.BrambleCoreModule;
|
|||||||
import org.briarproject.bramble.account.BriarAccountModule;
|
import org.briarproject.bramble.account.BriarAccountModule;
|
||||||
import org.briarproject.bramble.api.FeatureFlags;
|
import org.briarproject.bramble.api.FeatureFlags;
|
||||||
import org.briarproject.bramble.api.account.AccountManager;
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
@@ -20,6 +19,7 @@ import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
|||||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
|||||||
@@ -14,16 +14,12 @@ import org.briarproject.bramble.api.crypto.KeyStrengthener;
|
|||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
|
||||||
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
import org.briarproject.bramble.api.reporting.DevConfig;
|
||||||
@@ -51,8 +47,6 @@ import java.io.File;
|
|||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
@@ -67,8 +61,6 @@ import static android.content.Context.MODE_PRIVATE;
|
|||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.Collections.singletonMap;
|
|
||||||
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_ONION_ADDRESS;
|
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_ONION_ADDRESS;
|
||||||
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_PUBLIC_KEY_HEX;
|
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_PUBLIC_KEY_HEX;
|
||||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||||
@@ -130,12 +122,11 @@ public class AppModule {
|
|||||||
LocationUtils locationUtils, EventBus eventBus,
|
LocationUtils locationUtils, EventBus eventBus,
|
||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager, Clock clock,
|
BatteryManager batteryManager, Clock clock) {
|
||||||
TimeoutMonitor timeoutMonitor) {
|
|
||||||
Context appContext = app.getApplicationContext();
|
Context appContext = app.getApplicationContext();
|
||||||
DuplexPluginFactory bluetooth = new AndroidBluetoothPluginFactory(
|
DuplexPluginFactory bluetooth =
|
||||||
ioExecutor, androidExecutor, appContext, random, eventBus,
|
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
|
||||||
clock, timeoutMonitor, backoffFactory);
|
appContext, random, eventBus, clock, backoffFactory);
|
||||||
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
|
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
|
||||||
scheduler, appContext, networkManager, locationUtils, eventBus,
|
scheduler, appContext, networkManager, locationUtils, eventBus,
|
||||||
torSocketFactory, backoffFactory, resourceProvider,
|
torSocketFactory, backoffFactory, resourceProvider,
|
||||||
@@ -160,13 +151,6 @@ public class AppModule {
|
|||||||
public boolean shouldPoll() {
|
public boolean shouldPoll() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<TransportId, List<TransportId>> getTransportPreferences() {
|
|
||||||
// Prefer LAN to Bluetooth
|
|
||||||
return singletonMap(BluetoothConstants.ID,
|
|
||||||
singletonList(LanTcpConstants.ID));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
return pluginConfig;
|
return pluginConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,35 +2,38 @@ package org.briarproject.briar.android.contact;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
|
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.DISCONNECTED;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ContactItem {
|
public class ContactItem {
|
||||||
|
|
||||||
private final Contact contact;
|
private final Contact contact;
|
||||||
private boolean connected;
|
private ConnectionStatus status;
|
||||||
|
|
||||||
public ContactItem(Contact contact) {
|
public ContactItem(Contact contact) {
|
||||||
this(contact, false);
|
this(contact, DISCONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContactItem(Contact contact, boolean connected) {
|
public ContactItem(Contact contact, ConnectionStatus status) {
|
||||||
this.contact = contact;
|
this.contact = contact;
|
||||||
this.connected = connected;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Contact getContact() {
|
public Contact getContact() {
|
||||||
return contact;
|
return contact;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isConnected() {
|
ConnectionStatus getConnectionStatus() {
|
||||||
return connected;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setConnected(boolean connected) {
|
void setConnectionStatus(ConnectionStatus status) {
|
||||||
this.connected = connected;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
||||||
|
|
||||||
@@ -16,6 +17,8 @@ import androidx.annotation.UiThread;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import im.delight.android.identicons.IdenticonDrawable;
|
import im.delight.android.identicons.IdenticonDrawable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.RECENTLY_CONNECTED;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -27,7 +30,7 @@ public class ContactItemViewHolder<I extends ContactItem>
|
|||||||
protected final ImageView avatar;
|
protected final ImageView avatar;
|
||||||
protected final TextView name;
|
protected final TextView name;
|
||||||
@Nullable
|
@Nullable
|
||||||
protected final ImageView bulb;
|
private final ImageView bulb;
|
||||||
|
|
||||||
public ContactItemViewHolder(View v) {
|
public ContactItemViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
@@ -47,10 +50,13 @@ public class ContactItemViewHolder<I extends ContactItem>
|
|||||||
|
|
||||||
if (bulb != null) {
|
if (bulb != null) {
|
||||||
// online/offline
|
// online/offline
|
||||||
if (item.isConnected()) {
|
ConnectionStatus status = item.getConnectionStatus();
|
||||||
bulb.setImageResource(R.drawable.contact_connected);
|
if (status == CONNECTED) {
|
||||||
|
bulb.setImageResource(R.drawable.ic_connected);
|
||||||
|
} else if (status == RECENTLY_CONNECTED) {
|
||||||
|
bulb.setImageResource(R.drawable.ic_recently_connected);
|
||||||
} else {
|
} else {
|
||||||
bulb.setImageResource(R.drawable.contact_disconnected);
|
bulb.setImageResource(R.drawable.ic_disconnected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class ContactListAdapter extends
|
|||||||
if (c1.getTimestamp() != c2.getTimestamp()) {
|
if (c1.getTimestamp() != c2.getTimestamp()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return c1.isConnected() == c2.isConnected();
|
return c1.getConnectionStatus() == c2.getConnectionStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import android.widget.TextView;
|
|||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
@@ -25,8 +24,9 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
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.event.ContactConnectedEvent;
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
||||||
@@ -53,7 +53,6 @@ import javax.inject.Inject;
|
|||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.app.ActivityOptionsCompat;
|
import androidx.core.app.ActivityOptionsCompat;
|
||||||
import androidx.core.util.Pair;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import io.github.kobakei.materialfabspeeddial.FabSpeedDial;
|
import io.github.kobakei.materialfabspeeddial.FabSpeedDial;
|
||||||
import io.github.kobakei.materialfabspeeddial.FabSpeedDial.OnMenuItemClickListener;
|
import io.github.kobakei.materialfabspeeddial.FabSpeedDial.OnMenuItemClickListener;
|
||||||
@@ -137,26 +136,15 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
|||||||
ContactId contactId = item.getContact().getId();
|
ContactId contactId = item.getContact().getId();
|
||||||
i.putExtra(CONTACT_ID, contactId.getInt());
|
i.putExtra(CONTACT_ID, contactId.getInt());
|
||||||
|
|
||||||
|
Bundle options = null;
|
||||||
|
// work-around for android bug #224270
|
||||||
if (SDK_INT >= 23 && !isSamsung7()) {
|
if (SDK_INT >= 23 && !isSamsung7()) {
|
||||||
ContactListItemViewHolder holder =
|
options = makeTransitionOptions(view);
|
||||||
(ContactListItemViewHolder) list
|
}
|
||||||
.getRecyclerView()
|
if (options == null) {
|
||||||
.findViewHolderForAdapterPosition(
|
|
||||||
adapter.findItemPosition(item));
|
|
||||||
Pair<View, String> avatar =
|
|
||||||
Pair.create(holder.avatar,
|
|
||||||
getTransitionName(holder.avatar));
|
|
||||||
Pair<View, String> bulb =
|
|
||||||
Pair.create(holder.bulb,
|
|
||||||
getTransitionName(holder.bulb));
|
|
||||||
ActivityOptionsCompat options =
|
|
||||||
makeSceneTransitionAnimation(getActivity(),
|
|
||||||
avatar, bulb);
|
|
||||||
ActivityCompat.startActivity(getActivity(), i,
|
|
||||||
options.toBundle());
|
|
||||||
} else {
|
|
||||||
// work-around for android bug #224270
|
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
|
} else {
|
||||||
|
ActivityCompat.startActivity(getActivity(), i, options);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
adapter = new ContactListAdapter(requireContext(),
|
adapter = new ContactListAdapter(requireContext(),
|
||||||
@@ -171,6 +159,15 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
|||||||
return contentView;
|
return contentView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Bundle makeTransitionOptions(View view) {
|
||||||
|
View avatar = view.findViewById(R.id.avatarView);
|
||||||
|
String name = requireNonNull(getTransitionName(avatar));
|
||||||
|
ActivityOptionsCompat options = makeSceneTransitionAnimation(
|
||||||
|
requireActivity(), view, name);
|
||||||
|
return options.toBundle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMenuItemClick(FloatingActionButton fab, @Nullable TextView v,
|
public void onMenuItemClick(FloatingActionButton fab, @Nullable TextView v,
|
||||||
int itemId) {
|
int itemId) {
|
||||||
@@ -232,9 +229,9 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
|||||||
ContactId id = c.getId();
|
ContactId id = c.getId();
|
||||||
GroupCount count =
|
GroupCount count =
|
||||||
conversationManager.getGroupCount(id);
|
conversationManager.getGroupCount(id);
|
||||||
boolean connected =
|
ConnectionStatus status = connectionRegistry
|
||||||
connectionRegistry.isConnected(c.getId());
|
.getConnectionStatus(c.getId());
|
||||||
contacts.add(new ContactListItem(c, connected, count));
|
contacts.add(new ContactListItem(c, status, count));
|
||||||
} catch (NoSuchContactException e) {
|
} catch (NoSuchContactException e) {
|
||||||
// Continue
|
// Continue
|
||||||
}
|
}
|
||||||
@@ -265,10 +262,9 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
|||||||
if (e instanceof ContactAddedEvent) {
|
if (e instanceof ContactAddedEvent) {
|
||||||
LOG.info("Contact added, reloading");
|
LOG.info("Contact added, reloading");
|
||||||
loadContacts();
|
loadContacts();
|
||||||
} else if (e instanceof ContactConnectedEvent) {
|
} else if (e instanceof ConnectionStatusChangedEvent) {
|
||||||
setConnected(((ContactConnectedEvent) e).getContactId(), true);
|
ConnectionStatusChangedEvent c = (ConnectionStatusChangedEvent) e;
|
||||||
} else if (e instanceof ContactDisconnectedEvent) {
|
setConnectionStatus(c.getContactId(), c.getConnectionStatus());
|
||||||
setConnected(((ContactDisconnectedEvent) e).getContactId(), false);
|
|
||||||
} else if (e instanceof ContactRemovedEvent) {
|
} else if (e instanceof ContactRemovedEvent) {
|
||||||
LOG.info("Contact removed, removing item");
|
LOG.info("Contact removed, removing item");
|
||||||
removeItem(((ContactRemovedEvent) e).getContactId());
|
removeItem(((ContactRemovedEvent) e).getContactId());
|
||||||
@@ -304,12 +300,12 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void setConnected(ContactId c, boolean connected) {
|
private void setConnectionStatus(ContactId c, ConnectionStatus status) {
|
||||||
adapter.incrementRevision();
|
adapter.incrementRevision();
|
||||||
int position = adapter.findItemPosition(c);
|
int position = adapter.findItemPosition(c);
|
||||||
ContactListItem item = adapter.getItemAt(position);
|
ContactListItem item = adapter.getItemAt(position);
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
item.setConnected(connected);
|
item.setConnectionStatus(status);
|
||||||
adapter.updateItemAt(position, item);
|
adapter.updateItemAt(position, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.android.contact;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||||
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
||||||
|
|
||||||
@@ -15,9 +16,9 @@ public class ContactListItem extends ContactItem {
|
|||||||
private long timestamp;
|
private long timestamp;
|
||||||
private int unread;
|
private int unread;
|
||||||
|
|
||||||
public ContactListItem(Contact contact, boolean connected,
|
public ContactListItem(Contact contact, ConnectionStatus status,
|
||||||
GroupCount count) {
|
GroupCount count) {
|
||||||
super(contact, connected);
|
super(contact, status);
|
||||||
this.empty = count.getMsgCount() == 0;
|
this.empty = count.getMsgCount() == 0;
|
||||||
this.unread = count.getUnreadCount();
|
this.unread = count.getUnreadCount();
|
||||||
this.timestamp = count.getLatestMsgTime();
|
this.timestamp = count.getLatestMsgTime();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.briarproject.bramble.api.contact.ContactId;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@@ -15,8 +14,11 @@ import javax.annotation.Nullable;
|
|||||||
|
|
||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
|
import static android.view.View.INVISIBLE;
|
||||||
|
import static android.view.View.VISIBLE;
|
||||||
import static androidx.core.view.ViewCompat.setTransitionName;
|
import static androidx.core.view.ViewCompat.setTransitionName;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -39,10 +41,11 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
|
|||||||
// unread count
|
// unread count
|
||||||
int unreadCount = item.getUnreadCount();
|
int unreadCount = item.getUnreadCount();
|
||||||
if (unreadCount > 0) {
|
if (unreadCount > 0) {
|
||||||
unread.setText(String.format(Locale.getDefault(), "%d", unreadCount));
|
unread.setText(String.format(Locale.getDefault(), "%d",
|
||||||
unread.setVisibility(View.VISIBLE);
|
unreadCount));
|
||||||
|
unread.setVisibility(VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
unread.setVisibility(View.INVISIBLE);
|
unread.setVisibility(INVISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// date of last message
|
// date of last message
|
||||||
@@ -54,8 +57,7 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ContactId c = item.getContact().getId();
|
ContactId c = item.getContact().getId();
|
||||||
setTransitionName(avatar, UiUtils.getAvatarTransitionName(c));
|
setTransitionName(avatar, getAvatarTransitionName(c));
|
||||||
setTransitionName(bulb, UiUtils.getBulbTransitionName(c));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package org.briarproject.briar.android.controller;
|
package org.briarproject.briar.android.controller;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -18,6 +17,8 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class SharingControllerImpl implements SharingController, EventListener {
|
public class SharingControllerImpl implements SharingController, EventListener {
|
||||||
|
|
||||||
@@ -60,15 +61,14 @@ public class SharingControllerImpl implements SharingController, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof ContactConnectedEvent) {
|
if (e instanceof ConnectionStatusChangedEvent) {
|
||||||
setConnected(((ContactConnectedEvent) e).getContactId());
|
ConnectionStatusChangedEvent c = (ConnectionStatusChangedEvent) e;
|
||||||
} else if (e instanceof ContactDisconnectedEvent) {
|
setConnectionStatus(c.getContactId());
|
||||||
setConnected(((ContactDisconnectedEvent) e).getContactId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void setConnected(ContactId c) {
|
private void setConnectionStatus(ContactId c) {
|
||||||
if (listener == null) throw new IllegalStateException();
|
if (listener == null) throw new IllegalStateException();
|
||||||
if (contacts.contains(c)) {
|
if (contacts.contains(c)) {
|
||||||
int online = getOnlineCount();
|
int online = getOnlineCount();
|
||||||
@@ -95,7 +95,9 @@ public class SharingControllerImpl implements SharingController, EventListener {
|
|||||||
public int getOnlineCount() {
|
public int getOnlineCount() {
|
||||||
int online = 0;
|
int online = 0;
|
||||||
for (ContactId c : contacts) {
|
for (ContactId c : contacts) {
|
||||||
if (connectionRegistry.isConnected(c)) online++;
|
if (connectionRegistry.getConnectionStatus(c) == CONNECTED) {
|
||||||
|
online++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return online;
|
return online;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import android.view.Menu;
|
|||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@@ -21,7 +20,6 @@ import com.google.android.material.snackbar.Snackbar;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.FeatureFlags;
|
import org.briarproject.bramble.api.FeatureFlags;
|
||||||
import org.briarproject.bramble.api.Pair;
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||||
@@ -34,8 +32,9 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
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.event.ContactConnectedEvent;
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
|
||||||
import org.briarproject.bramble.api.sync.ClientId;
|
import org.briarproject.bramble.api.sync.ClientId;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
||||||
@@ -125,6 +124,8 @@ import static java.util.Objects.requireNonNull;
|
|||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.RECENTLY_CONNECTED;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
@@ -138,7 +139,6 @@ import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHME
|
|||||||
import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
|
import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
|
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
|
|
||||||
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
|
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
|
||||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
|
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
|
||||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
|
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
|
||||||
@@ -198,8 +198,8 @@ public class ConversationActivity extends BriarActivity
|
|||||||
private ConversationAdapter adapter;
|
private ConversationAdapter adapter;
|
||||||
private Toolbar toolbar;
|
private Toolbar toolbar;
|
||||||
private CircleImageView toolbarAvatar;
|
private CircleImageView toolbarAvatar;
|
||||||
private ImageView toolbarStatus;
|
|
||||||
private TextView toolbarTitle;
|
private TextView toolbarTitle;
|
||||||
|
private TextView toolbarStatus;
|
||||||
private BriarRecyclerView list;
|
private BriarRecyclerView list;
|
||||||
private LinearLayoutManager layoutManager;
|
private LinearLayoutManager layoutManager;
|
||||||
private TextInputView textInputView;
|
private TextInputView textInputView;
|
||||||
@@ -237,8 +237,8 @@ public class ConversationActivity extends BriarActivity
|
|||||||
// Custom Toolbar
|
// Custom Toolbar
|
||||||
toolbar = requireNonNull(setUpCustomToolbar(true));
|
toolbar = requireNonNull(setUpCustomToolbar(true));
|
||||||
toolbarAvatar = toolbar.findViewById(R.id.contactAvatar);
|
toolbarAvatar = toolbar.findViewById(R.id.contactAvatar);
|
||||||
toolbarStatus = toolbar.findViewById(R.id.contactStatus);
|
|
||||||
toolbarTitle = toolbar.findViewById(R.id.contactName);
|
toolbarTitle = toolbar.findViewById(R.id.contactName);
|
||||||
|
toolbarStatus = toolbar.findViewById(R.id.contactStatus);
|
||||||
|
|
||||||
observeOnce(viewModel.getContactAuthorId(), this, authorId -> {
|
observeOnce(viewModel.getContactAuthorId(), this, authorId -> {
|
||||||
requireNonNull(authorId);
|
requireNonNull(authorId);
|
||||||
@@ -257,7 +257,6 @@ public class ConversationActivity extends BriarActivity
|
|||||||
this::onAddedPrivateMessage);
|
this::onAddedPrivateMessage);
|
||||||
|
|
||||||
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
|
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
|
||||||
setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
|
|
||||||
|
|
||||||
visitor = new ConversationVisitor(this, this, this,
|
visitor = new ConversationVisitor(this, this, this,
|
||||||
viewModel.getContactDisplayName());
|
viewModel.getContactDisplayName());
|
||||||
@@ -499,14 +498,14 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void displayContactOnlineStatus() {
|
private void displayContactOnlineStatus() {
|
||||||
if (connectionRegistry.isConnected(contactId)) {
|
ConnectionStatus status =
|
||||||
toolbarStatus.setImageDrawable(ContextCompat.getDrawable(
|
connectionRegistry.getConnectionStatus(contactId);
|
||||||
ConversationActivity.this, R.drawable.contact_online));
|
if (status == CONNECTED) {
|
||||||
toolbarStatus.setContentDescription(getString(R.string.online));
|
toolbarStatus.setText(R.string.online);
|
||||||
|
} else if (status == RECENTLY_CONNECTED) {
|
||||||
|
toolbarStatus.setText(R.string.recently_online);
|
||||||
} else {
|
} else {
|
||||||
toolbarStatus.setImageDrawable(ContextCompat.getDrawable(
|
toolbarStatus.setText(R.string.offline);
|
||||||
ConversationActivity.this, R.drawable.contact_offline));
|
|
||||||
toolbarStatus.setContentDescription(getString(R.string.offline));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -729,16 +728,10 @@ public class ConversationActivity extends BriarActivity
|
|||||||
LOG.info("Messages acked");
|
LOG.info("Messages acked");
|
||||||
markMessages(m.getMessageIds(), true, true);
|
markMessages(m.getMessageIds(), true, true);
|
||||||
}
|
}
|
||||||
} else if (e instanceof ContactConnectedEvent) {
|
} else if (e instanceof ConnectionStatusChangedEvent) {
|
||||||
ContactConnectedEvent c = (ContactConnectedEvent) e;
|
ConnectionStatusChangedEvent c = (ConnectionStatusChangedEvent) e;
|
||||||
if (c.getContactId().equals(contactId)) {
|
if (c.getContactId().equals(contactId)) {
|
||||||
LOG.info("Contact connected");
|
LOG.info("Connection status changed");
|
||||||
displayContactOnlineStatus();
|
|
||||||
}
|
|
||||||
} else if (e instanceof ContactDisconnectedEvent) {
|
|
||||||
ContactDisconnectedEvent c = (ContactDisconnectedEvent) e;
|
|
||||||
if (c.getContactId().equals(contactId)) {
|
|
||||||
LOG.info("Contact disconnected");
|
|
||||||
displayContactOnlineStatus();
|
displayContactOnlineStatus();
|
||||||
}
|
}
|
||||||
} else if (e instanceof ClientVersionUpdatedEvent) {
|
} else if (e instanceof ClientVersionUpdatedEvent) {
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
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.ConnectionRegistry;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
||||||
@@ -73,7 +74,8 @@ public class ContactChooserFragment extends BaseFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
public View onCreateView(LayoutInflater inflater,
|
||||||
|
@Nullable ViewGroup container,
|
||||||
@Nullable Bundle savedInstanceState) {
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
|
||||||
View contentView = inflater.inflate(R.layout.list, container, false);
|
View contentView = inflater.inflate(R.layout.list, container, false);
|
||||||
@@ -127,9 +129,9 @@ public class ContactChooserFragment extends BaseFragment {
|
|||||||
ContactId id = c.getId();
|
ContactId id = c.getId();
|
||||||
GroupCount count =
|
GroupCount count =
|
||||||
conversationManager.getGroupCount(id);
|
conversationManager.getGroupCount(id);
|
||||||
boolean connected =
|
ConnectionStatus status = connectionRegistry
|
||||||
connectionRegistry.isConnected(c.getId());
|
.getConnectionStatus(c.getId());
|
||||||
contacts.add(new ContactListItem(c, connected, count));
|
contacts.add(new ContactListItem(c, status, count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
displayContacts(contacts);
|
displayContacts(contacts);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.keyagreement;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
@@ -11,6 +10,7 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public class GroupMemberListActivity extends BriarActivity
|
|||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO ContactConnectedEvent and ContactDisconnectedEvent
|
// TODO ConnectionStatusChangedEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package org.briarproject.briar.android.privategroup.memberlist;
|
package org.briarproject.briar.android.privategroup.memberlist;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.briar.android.controller.DbControllerImpl;
|
import org.briarproject.briar.android.controller.DbControllerImpl;
|
||||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||||
@@ -19,6 +20,7 @@ import java.util.logging.Logger;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.DISCONNECTED;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
class GroupMemberListControllerImpl extends DbControllerImpl
|
class GroupMemberListControllerImpl extends DbControllerImpl
|
||||||
@@ -50,10 +52,11 @@ class GroupMemberListControllerImpl extends DbControllerImpl
|
|||||||
privateGroupManager.getMembers(groupId);
|
privateGroupManager.getMembers(groupId);
|
||||||
for (GroupMember m : members) {
|
for (GroupMember m : members) {
|
||||||
ContactId c = m.getContactId();
|
ContactId c = m.getContactId();
|
||||||
boolean online = false;
|
ConnectionStatus status = DISCONNECTED;
|
||||||
if (c != null)
|
if (c != null) {
|
||||||
online = connectionRegistry.isConnected(c);
|
status = connectionRegistry.getConnectionStatus(c);
|
||||||
items.add(new MemberListItem(m, online));
|
}
|
||||||
|
items.add(new MemberListItem(m, status));
|
||||||
}
|
}
|
||||||
handler.onResult(items);
|
handler.onResult(items);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class MemberListAdapter extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean areContentsTheSame(MemberListItem m1, MemberListItem m2) {
|
public boolean areContentsTheSame(MemberListItem m1, MemberListItem m2) {
|
||||||
if (m1.isOnline() != m2.isOnline()) return false;
|
if (m1.getConnectionStatus() != m2.getConnectionStatus()) return false;
|
||||||
if (m1.getContactId() != m2.getContactId()) return false;
|
if (m1.getContactId() != m2.getContactId()) return false;
|
||||||
if (m1.getStatus() != m2.getStatus()) return false;
|
if (m1.getStatus() != m2.getStatus()) return false;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.identity.Author;
|
|||||||
import org.briarproject.bramble.api.identity.AuthorInfo;
|
import org.briarproject.bramble.api.identity.AuthorInfo;
|
||||||
import org.briarproject.bramble.api.identity.AuthorInfo.Status;
|
import org.briarproject.bramble.api.identity.AuthorInfo.Status;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
import org.briarproject.briar.api.privategroup.GroupMember;
|
import org.briarproject.briar.api.privategroup.GroupMember;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -15,11 +16,11 @@ import javax.annotation.concurrent.NotThreadSafe;
|
|||||||
class MemberListItem {
|
class MemberListItem {
|
||||||
|
|
||||||
private final GroupMember groupMember;
|
private final GroupMember groupMember;
|
||||||
private boolean online;
|
private ConnectionStatus status;
|
||||||
|
|
||||||
MemberListItem(GroupMember groupMember, boolean online) {
|
MemberListItem(GroupMember groupMember, ConnectionStatus status) {
|
||||||
this.groupMember = groupMember;
|
this.groupMember = groupMember;
|
||||||
this.online = online;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
Author getMember() {
|
Author getMember() {
|
||||||
@@ -43,12 +44,12 @@ class MemberListItem {
|
|||||||
return groupMember.getContactId();
|
return groupMember.getContactId();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isOnline() {
|
ConnectionStatus getConnectionStatus() {
|
||||||
return online;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOnline(boolean online) {
|
void setConnectionStatus(ConnectionStatus status) {
|
||||||
this.online = online;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.view.AuthorView;
|
import org.briarproject.briar.android.view.AuthorView;
|
||||||
|
|
||||||
@@ -14,6 +15,8 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.RECENTLY_CONNECTED;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -38,10 +41,13 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
|
|||||||
// online status of visible contacts
|
// online status of visible contacts
|
||||||
if (item.getContactId() != null) {
|
if (item.getContactId() != null) {
|
||||||
bulb.setVisibility(VISIBLE);
|
bulb.setVisibility(VISIBLE);
|
||||||
if (item.isOnline()) {
|
ConnectionStatus status = item.getConnectionStatus();
|
||||||
bulb.setImageResource(R.drawable.contact_connected);
|
if (status == CONNECTED) {
|
||||||
|
bulb.setImageResource(R.drawable.ic_connected);
|
||||||
|
} else if (status == RECENTLY_CONNECTED) {
|
||||||
|
bulb.setImageResource(R.drawable.ic_recently_connected);
|
||||||
} else {
|
} else {
|
||||||
bulb.setImageResource(R.drawable.contact_disconnected);
|
bulb.setImageResource(R.drawable.ic_disconnected);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bulb.setVisibility(GONE);
|
bulb.setVisibility(GONE);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import android.os.Bundle;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -14,6 +13,8 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
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.ConnectionRegistry;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
|
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
@@ -104,7 +105,7 @@ abstract class SharingStatusActivity extends BriarActivity
|
|||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO ContactConnectedEvent and ContactDisconnectedEvent
|
// TODO ConnectionStatusChangedEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -134,8 +135,9 @@ abstract class SharingStatusActivity extends BriarActivity
|
|||||||
try {
|
try {
|
||||||
List<ContactItem> contactItems = new ArrayList<>();
|
List<ContactItem> contactItems = new ArrayList<>();
|
||||||
for (Contact c : getSharedWith()) {
|
for (Contact c : getSharedWith()) {
|
||||||
boolean online = connectionRegistry.isConnected(c.getId());
|
ConnectionStatus status =
|
||||||
ContactItem item = new ContactItem(c, online);
|
connectionRegistry.getConnectionStatus(c.getId());
|
||||||
|
ContactItem item = new ContactItem(c, status);
|
||||||
contactItems.add(item);
|
contactItems.add(item);
|
||||||
}
|
}
|
||||||
displaySharedWith(contactItems);
|
displaySharedWith(contactItems);
|
||||||
|
|||||||
@@ -238,10 +238,6 @@ public class UiUtils {
|
|||||||
return "avatar" + c.getInt();
|
return "avatar" + c.getInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getBulbTransitionName(ContactId c) {
|
|
||||||
return "bulb" + c.getInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OnClickListener getGoToSettingsListener(Context context) {
|
public static OnClickListener getGoToSettingsListener(Context context) {
|
||||||
return (dialog, which) -> {
|
return (dialog, which) -> {
|
||||||
Intent i = new Intent();
|
Intent i = new Intent();
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24">
|
|
||||||
|
|
||||||
<path
|
|
||||||
android:fillColor="#abffffff"
|
|
||||||
android:pathData="M12,2 C6.48,2,2,6.48,2,12 S6.48,22,12,22 S22,17.52,22,12 S17.52,2,12,2 Z M12,20
|
|
||||||
C7.58,20,4,16.42,4,12 S7.58,4,12,4 S20,7.58,20,12 S16.42,20,12,20 Z"/>
|
|
||||||
|
|
||||||
<path
|
|
||||||
android:fillColor="#95d220"
|
|
||||||
android:pathData="M10.8972,19.9503 C6.5514,19.3493,3.43091,15.2154,4.0625,10.896
|
|
||||||
C4.55452,7.53099,7.09451,4.8236,10.394,4.14714
|
|
||||||
C14.2569,3.35517,18.1698,5.54347,19.5236,9.25295
|
|
||||||
C20.0698,10.7495,20.1616,12.4612,19.777,13.9758
|
|
||||||
C19.5457,14.8864,18.8106,16.3388,18.2072,17.0771
|
|
||||||
C16.4904,19.1779,13.581,20.3215,10.8973,19.9503 Z"
|
|
||||||
android:strokeLineCap="round"
|
|
||||||
android:strokeLineJoin="round"
|
|
||||||
android:strokeWidth="0.76779664"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#abffffff"
|
|
||||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
|
||||||
</vector>
|
|
||||||
13
briar-android/src/main/res/drawable-night/ic_connected.xml
Normal file
13
briar-android/src/main/res/drawable-night/ic_connected.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||||
|
android:strokeAlpha="1"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#95d220"
|
||||||
|
android:strokeColor="#abffffff"
|
||||||
|
android:fillAlpha="1"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||||
|
android:strokeAlpha="1"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#abffffff"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:fillAlpha="1"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,12m-7,0a7,7 0,1 1,14 0a7,7 0,1 1,-14 0"
|
||||||
|
android:strokeAlpha="1"
|
||||||
|
android:strokeWidth="4.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#95d220"
|
||||||
|
android:fillAlpha="1"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||||
|
android:strokeAlpha="1"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#abffffff"
|
||||||
|
android:fillAlpha="1"/>
|
||||||
|
</vector>
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:alpha="0.56"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24">
|
|
||||||
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M12,2 C6.48,2,2,6.48,2,12 S6.48,22,12,22 S22,17.52,22,12 S17.52,2,12,2 Z M12,20
|
|
||||||
C7.58,20,4,16.42,4,12 S7.58,4,12,4 S20,7.58,20,12 S16.42,20,12,20 Z"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M0,0 L24,0 L24,24 L0,24 Z"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#95d220"
|
|
||||||
android:pathData="M10.8972,19.9503 C6.5514,19.3493,3.43091,15.2154,4.0625,10.896
|
|
||||||
C4.55452,7.53099,7.09451,4.8236,10.394,4.14714
|
|
||||||
C14.2569,3.35517,18.1698,5.54347,19.5236,9.25295
|
|
||||||
C20.0698,10.7495,20.1616,12.4612,19.777,13.9758
|
|
||||||
C19.5457,14.8864,18.8106,16.3388,18.2072,17.0771
|
|
||||||
C16.4904,19.1779,13.581,20.3215,10.8973,19.9503 Z"
|
|
||||||
android:strokeLineCap="round"
|
|
||||||
android:strokeLineJoin="round"
|
|
||||||
android:strokeWidth="0.76779664"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:alpha="0.56"
|
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:viewportWidth="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
|
||||||
</vector>
|
|
||||||
13
briar-android/src/main/res/drawable/ic_connected.xml
Normal file
13
briar-android/src/main/res/drawable/ic_connected.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||||
|
android:strokeAlpha="1"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#95d220"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:fillAlpha="0.56"/>
|
||||||
|
</vector>
|
||||||
14
briar-android/src/main/res/drawable/ic_disconnected.xml
Normal file
14
briar-android/src/main/res/drawable/ic_disconnected.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||||
|
android:strokeAlpha="1"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:fillAlpha="1"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,12m-7,0a7,7 0,1 1,14 0a7,7 0,1 1,-14 0"
|
||||||
|
android:strokeAlpha="0.56"
|
||||||
|
android:strokeWidth="4.5"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:strokeColor="#95d220"
|
||||||
|
android:fillAlpha="1"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||||
|
android:strokeAlpha="1"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:fillAlpha="1"/>
|
||||||
|
</vector>
|
||||||
@@ -18,27 +18,50 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<LinearLayout
|
<RelativeLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<include layout="@layout/contact_avatar_status" />
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
|
android:id="@+id/contactAvatar"
|
||||||
|
style="@style/BriarAvatar"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
app:civ_border_color="@color/action_bar_text"
|
||||||
|
tools:src="@mipmap/ic_launcher_round" />
|
||||||
|
|
||||||
<com.vanniktech.emoji.EmojiTextView
|
<com.vanniktech.emoji.EmojiTextView
|
||||||
android:id="@+id/contactName"
|
android:id="@+id/contactName"
|
||||||
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
|
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginStart="@dimen/margin_medium"
|
android:layout_marginStart="@dimen/margin_medium"
|
||||||
android:layout_marginLeft="@dimen/margin_medium"
|
android:layout_marginLeft="@dimen/margin_medium"
|
||||||
|
android:layout_toEndOf="@id/contactAvatar"
|
||||||
|
android:layout_toRightOf="@id/contactAvatar"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textColor="@color/action_bar_text"
|
android:textColor="@color/action_bar_text"
|
||||||
tools:text="Contact Name of someone who chose a long name" />
|
tools:text="Contact Name of someone who chose a long name" />
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:id="@+id/contactStatus"
|
||||||
|
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/contactName"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_toEndOf="@id/contactAvatar"
|
||||||
|
android:layout_toRightOf="@id/contactAvatar"
|
||||||
|
android:textColor="@color/briar_text_secondary_inverse"
|
||||||
|
tools:text="Recently online" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
tools:showIn="@layout/activity_conversation">
|
|
||||||
|
|
||||||
<de.hdodenhof.circleimageview.CircleImageView
|
|
||||||
android:id="@+id/contactAvatar"
|
|
||||||
style="@style/BriarAvatar"
|
|
||||||
android:layout_width="30dp"
|
|
||||||
android:layout_height="30dp"
|
|
||||||
app:civ_border_color="@color/action_bar_text"
|
|
||||||
tools:src="@mipmap/ic_launcher_round" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/contactStatus"
|
|
||||||
android:layout_width="15dp"
|
|
||||||
android:layout_height="15dp"
|
|
||||||
android:layout_gravity="bottom|end|right"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
tools:ignore="ContentDescription"
|
|
||||||
tools:src="@drawable/contact_online" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
tools:src="@drawable/contact_connected" />
|
tools:src="@drawable/ic_recently_connected" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/divider"
|
android:id="@+id/divider"
|
||||||
|
|||||||
@@ -40,6 +40,6 @@
|
|||||||
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
tools:src="@drawable/contact_connected" />
|
tools:src="@drawable/ic_connected" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
tools:src="@drawable/contact_connected" />
|
tools:src="@drawable/ic_connected" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/creatorView"
|
android:id="@+id/creatorView"
|
||||||
|
|||||||
@@ -110,6 +110,7 @@
|
|||||||
<string name="decline">Decline</string>
|
<string name="decline">Decline</string>
|
||||||
<string name="options">Options</string>
|
<string name="options">Options</string>
|
||||||
<string name="online">Online</string>
|
<string name="online">Online</string>
|
||||||
|
<string name="recently_online">Recently online</string>
|
||||||
<string name="offline">Offline</string>
|
<string name="offline">Offline</string>
|
||||||
<string name="send">Send</string>
|
<string name="send">Send</string>
|
||||||
<string name="allow">Allow</string>
|
<string name="allow">Allow</string>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user