mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 13:49:53 +01:00
Merge branch '1232-polling-events' into 'master'
Show "connecting" state for pending contacts See merge request briar/briar!1129
This commit is contained in:
@@ -3,7 +3,7 @@ package org.briarproject.bramble.api.contact;
|
|||||||
public enum PendingContactState {
|
public enum PendingContactState {
|
||||||
|
|
||||||
WAITING_FOR_CONNECTION,
|
WAITING_FOR_CONNECTION,
|
||||||
CONNECTED,
|
CONNECTING,
|
||||||
ADDING_CONTACT,
|
ADDING_CONTACT,
|
||||||
FAILED
|
FAILED
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.briarproject.bramble.api.rendezvous;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for the poller that makes rendezvous connections to pending
|
||||||
|
* contacts.
|
||||||
|
*/
|
||||||
|
public interface RendezvousPoller {
|
||||||
|
|
||||||
|
long getLastPollTime(PendingContactId p);
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.rendezvous.event;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
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 rendezvous with a pending contact fails.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class RendezvousFailedEvent extends Event {
|
|
||||||
|
|
||||||
private final PendingContactId pendingContactId;
|
|
||||||
|
|
||||||
public RendezvousFailedEvent(PendingContactId pendingContactId) {
|
|
||||||
this.pendingContactId = pendingContactId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PendingContactId getPendingContactId() {
|
|
||||||
return pendingContactId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package org.briarproject.bramble.api.rendezvous.event;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that is broadcast when a transport plugin is polled for connections
|
||||||
|
* to one or more pending contacts.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class RendezvousPollEvent extends Event {
|
||||||
|
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final Collection<PendingContactId> pendingContacts;
|
||||||
|
|
||||||
|
public RendezvousPollEvent(TransportId transportId,
|
||||||
|
Collection<PendingContactId> pendingContacts) {
|
||||||
|
this.transportId = transportId;
|
||||||
|
this.pendingContacts = pendingContacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransportId getTransportId() {
|
||||||
|
return transportId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<PendingContactId> getPendingContacts() {
|
||||||
|
return pendingContacts;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,6 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
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.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
@@ -25,25 +24,20 @@ import org.briarproject.bramble.api.identity.AuthorInfo;
|
|||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousFailedEvent;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.ADDING_CONTACT;
|
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
|
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
||||||
@@ -60,23 +54,20 @@ class ContactManagerImpl implements ContactManager, EventListener {
|
|||||||
private final KeyManager keyManager;
|
private final KeyManager keyManager;
|
||||||
private final IdentityManager identityManager;
|
private final IdentityManager identityManager;
|
||||||
private final PendingContactFactory pendingContactFactory;
|
private final PendingContactFactory pendingContactFactory;
|
||||||
private final EventBus eventBus;
|
|
||||||
|
|
||||||
private final List<ContactHook> hooks = new CopyOnWriteArrayList<>();
|
private final List<ContactHook> hooks = new CopyOnWriteArrayList<>();
|
||||||
private final ConcurrentMap<PendingContactId, PendingContactState> states =
|
private final Map<PendingContactId, PendingContactState> states =
|
||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ContactManagerImpl(DatabaseComponent db,
|
ContactManagerImpl(DatabaseComponent db,
|
||||||
KeyManager keyManager,
|
KeyManager keyManager,
|
||||||
IdentityManager identityManager,
|
IdentityManager identityManager,
|
||||||
PendingContactFactory pendingContactFactory,
|
PendingContactFactory pendingContactFactory) {
|
||||||
EventBus eventBus) {
|
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.keyManager = keyManager;
|
this.keyManager = keyManager;
|
||||||
this.identityManager = identityManager;
|
this.identityManager = identityManager;
|
||||||
this.pendingContactFactory = pendingContactFactory;
|
this.pendingContactFactory = pendingContactFactory;
|
||||||
this.eventBus = eventBus;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,7 +147,7 @@ class ContactManagerImpl implements ContactManager, EventListener {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
}
|
}
|
||||||
setState(p.getId(), WAITING_FOR_CONNECTION);
|
states.put(p.getId(), WAITING_FOR_CONNECTION);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,46 +277,10 @@ class ContactManagerImpl implements ContactManager, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof RendezvousConnectionOpenedEvent) {
|
if (e instanceof PendingContactStateChangedEvent) {
|
||||||
RendezvousConnectionOpenedEvent r =
|
PendingContactStateChangedEvent p =
|
||||||
(RendezvousConnectionOpenedEvent) e;
|
(PendingContactStateChangedEvent) e;
|
||||||
setStateConnected(r.getPendingContactId());
|
states.put(p.getId(), p.getPendingContactState());
|
||||||
} else if (e instanceof RendezvousConnectionClosedEvent) {
|
|
||||||
RendezvousConnectionClosedEvent r =
|
|
||||||
(RendezvousConnectionClosedEvent) e;
|
|
||||||
// We're only interested in failures - if the rendezvous succeeds
|
|
||||||
// the pending contact will be removed
|
|
||||||
if (!r.isSuccess()) setStateDisconnected(r.getPendingContactId());
|
|
||||||
} else if (e instanceof RendezvousFailedEvent) {
|
|
||||||
RendezvousFailedEvent r = (RendezvousFailedEvent) e;
|
|
||||||
setState(r.getPendingContactId(), FAILED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the state of the given pending contact and broadcasts an event.
|
|
||||||
*/
|
|
||||||
private void setState(PendingContactId p, PendingContactState state) {
|
|
||||||
states.put(p, state);
|
|
||||||
eventBus.broadcast(new PendingContactStateChangedEvent(p, state));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setStateConnected(PendingContactId p) {
|
|
||||||
// Set the state to ADDING_CONTACT if there's no current state or the
|
|
||||||
// current state is WAITING_FOR_CONNECTION
|
|
||||||
if (states.putIfAbsent(p, ADDING_CONTACT) == null ||
|
|
||||||
states.replace(p, WAITING_FOR_CONNECTION, ADDING_CONTACT)) {
|
|
||||||
eventBus.broadcast(new PendingContactStateChangedEvent(p,
|
|
||||||
ADDING_CONTACT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setStateDisconnected(PendingContactId p) {
|
|
||||||
// Set the state to WAITING_FOR_CONNECTION if the current state is
|
|
||||||
// ADDING_CONTACT
|
|
||||||
if (states.replace(p, ADDING_CONTACT, WAITING_FOR_CONNECTION)) {
|
|
||||||
eventBus.broadcast(new PendingContactStateChangedEvent(p,
|
|
||||||
WAITING_FOR_CONNECTION));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.rendezvous;
|
|||||||
|
|
||||||
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.rendezvous.RendezvousPoller;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package org.briarproject.bramble.rendezvous;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Empty interface for injecting the rendezvous poller.
|
|
||||||
*/
|
|
||||||
interface RendezvousPoller {
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,10 @@ import org.briarproject.bramble.PoliteExecutor;
|
|||||||
import org.briarproject.bramble.api.Pair;
|
import org.briarproject.bramble.api.Pair;
|
||||||
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.event.PendingContactAddedEvent;
|
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
|
||||||
|
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
@@ -34,7 +36,10 @@ import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
|
|||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
||||||
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousFailedEvent;
|
import org.briarproject.bramble.api.rendezvous.RendezvousPoller;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousPollEvent;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.Scheduler;
|
import org.briarproject.bramble.api.system.Scheduler;
|
||||||
|
|
||||||
@@ -45,6 +50,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@@ -58,6 +64,9 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|||||||
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.contact.PendingContactState.ADDING_CONTACT;
|
||||||
|
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
|
||||||
|
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNull;
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNull;
|
||||||
import static org.briarproject.bramble.rendezvous.RendezvousConstants.POLLING_INTERVAL_MS;
|
import static org.briarproject.bramble.rendezvous.RendezvousConstants.POLLING_INTERVAL_MS;
|
||||||
import static org.briarproject.bramble.rendezvous.RendezvousConstants.RENDEZVOUS_TIMEOUT_MS;
|
import static org.briarproject.bramble.rendezvous.RendezvousConstants.RENDEZVOUS_TIMEOUT_MS;
|
||||||
@@ -81,6 +90,9 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
|
|||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
|
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
private final Map<PendingContactId, Long> lastPollTimes =
|
||||||
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// Executor that runs one task at a time
|
// Executor that runs one task at a time
|
||||||
private final Executor worker;
|
private final Executor worker;
|
||||||
// The following fields are only accessed on the worker
|
// The following fields are only accessed on the worker
|
||||||
@@ -113,6 +125,12 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
|
|||||||
worker = new PoliteExecutor("RendezvousPoller", ioExecutor, 1);
|
worker = new PoliteExecutor("RendezvousPoller", ioExecutor, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLastPollTime(PendingContactId p) {
|
||||||
|
Long time = lastPollTimes.get(p);
|
||||||
|
return time == null ? 0 : time;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startService() throws ServiceException {
|
public void startService() throws ServiceException {
|
||||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||||
@@ -140,8 +158,10 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
|
|||||||
private void addPendingContact(PendingContact p) {
|
private void addPendingContact(PendingContact p) {
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
long expiry = p.getTimestamp() + RENDEZVOUS_TIMEOUT_MS;
|
long expiry = p.getTimestamp() + RENDEZVOUS_TIMEOUT_MS;
|
||||||
if (expiry <= now) {
|
if (expiry > now) {
|
||||||
eventBus.broadcast(new RendezvousFailedEvent(p.getId()));
|
broadcastState(p.getId(), WAITING_FOR_CONNECTION);
|
||||||
|
} else {
|
||||||
|
broadcastState(p.getId(), FAILED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -168,6 +188,10 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void broadcastState(PendingContactId p, PendingContactState state) {
|
||||||
|
eventBus.broadcast(new PendingContactStateChangedEvent(p, state));
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private RendezvousEndpoint createEndpoint(DuplexPlugin plugin,
|
private RendezvousEndpoint createEndpoint(DuplexPlugin plugin,
|
||||||
PendingContactId p, CryptoState cs) {
|
PendingContactId p, CryptoState cs) {
|
||||||
@@ -195,7 +219,7 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
|
|||||||
}
|
}
|
||||||
for (PendingContactId p : expired) {
|
for (PendingContactId p : expired) {
|
||||||
removePendingContact(p);
|
removePendingContact(p);
|
||||||
eventBus.broadcast(new RendezvousFailedEvent(p));
|
broadcastState(p, FAILED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +227,7 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
|
|||||||
private void removePendingContact(PendingContactId p) {
|
private void removePendingContact(PendingContactId p) {
|
||||||
// We can come here twice if a pending contact expires and is removed
|
// We can come here twice if a pending contact expires and is removed
|
||||||
if (cryptoStates.remove(p) == null) return;
|
if (cryptoStates.remove(p) == null) return;
|
||||||
|
lastPollTimes.remove(p);
|
||||||
for (PluginState ps : pluginStates.values()) {
|
for (PluginState ps : pluginStates.values()) {
|
||||||
RendezvousEndpoint endpoint = ps.endpoints.remove(p);
|
RendezvousEndpoint endpoint = ps.endpoints.remove(p);
|
||||||
if (endpoint != null) tryToClose(endpoint, LOG, INFO);
|
if (endpoint != null) tryToClose(endpoint, LOG, INFO);
|
||||||
@@ -211,16 +236,22 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
|
|||||||
|
|
||||||
// Worker
|
// Worker
|
||||||
private void poll(PluginState ps) {
|
private void poll(PluginState ps) {
|
||||||
|
if (ps.endpoints.isEmpty()) return;
|
||||||
|
TransportId t = ps.plugin.getId();
|
||||||
List<Pair<TransportProperties, ConnectionHandler>> properties =
|
List<Pair<TransportProperties, ConnectionHandler>> properties =
|
||||||
new ArrayList<>();
|
new ArrayList<>();
|
||||||
for (Entry<PendingContactId, RendezvousEndpoint> e :
|
for (Entry<PendingContactId, RendezvousEndpoint> e :
|
||||||
ps.endpoints.entrySet()) {
|
ps.endpoints.entrySet()) {
|
||||||
TransportProperties props =
|
TransportProperties props =
|
||||||
e.getValue().getRemoteTransportProperties();
|
e.getValue().getRemoteTransportProperties();
|
||||||
Handler h = new Handler(e.getKey(), ps.plugin.getId(), false);
|
Handler h = new Handler(e.getKey(), t, false);
|
||||||
properties.add(new Pair<>(props, h));
|
properties.add(new Pair<>(props, h));
|
||||||
}
|
}
|
||||||
if (!properties.isEmpty()) ps.plugin.poll(properties);
|
List<PendingContactId> polled = new ArrayList<>(ps.endpoints.keySet());
|
||||||
|
long now = clock.currentTimeMillis();
|
||||||
|
for (PendingContactId p : polled) lastPollTimes.put(p, now);
|
||||||
|
eventBus.broadcast(new RendezvousPollEvent(t, polled));
|
||||||
|
ps.plugin.poll(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -241,6 +272,14 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
|
|||||||
} else if (e instanceof TransportDisabledEvent) {
|
} else if (e instanceof TransportDisabledEvent) {
|
||||||
TransportDisabledEvent t = (TransportDisabledEvent) e;
|
TransportDisabledEvent t = (TransportDisabledEvent) e;
|
||||||
removeTransportAsync(t.getTransportId());
|
removeTransportAsync(t.getTransportId());
|
||||||
|
} else if (e instanceof RendezvousConnectionOpenedEvent) {
|
||||||
|
RendezvousConnectionOpenedEvent r =
|
||||||
|
(RendezvousConnectionOpenedEvent) e;
|
||||||
|
connectionOpenedAsync(r.getPendingContactId());
|
||||||
|
} else if (e instanceof RendezvousConnectionClosedEvent) {
|
||||||
|
RendezvousConnectionClosedEvent r =
|
||||||
|
(RendezvousConnectionClosedEvent) e;
|
||||||
|
if (!r.isSuccess()) connectionFailedAsync(r.getPendingContactId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,9 +296,13 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
|
|||||||
for (PluginState ps : pluginStates.values()) {
|
for (PluginState ps : pluginStates.values()) {
|
||||||
RendezvousEndpoint endpoint = ps.endpoints.get(p);
|
RendezvousEndpoint endpoint = ps.endpoints.get(p);
|
||||||
if (endpoint != null) {
|
if (endpoint != null) {
|
||||||
|
TransportId t = ps.plugin.getId();
|
||||||
TransportProperties props =
|
TransportProperties props =
|
||||||
endpoint.getRemoteTransportProperties();
|
endpoint.getRemoteTransportProperties();
|
||||||
Handler h = new Handler(p, ps.plugin.getId(), false);
|
Handler h = new Handler(p, t, false);
|
||||||
|
lastPollTimes.put(p, clock.currentTimeMillis());
|
||||||
|
eventBus.broadcast(
|
||||||
|
new RendezvousPollEvent(t, singletonList(p)));
|
||||||
ps.plugin.poll(singletonList(new Pair<>(props, h)));
|
ps.plugin.poll(singletonList(new Pair<>(props, h)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,6 +350,29 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventExecutor
|
||||||
|
private void connectionOpenedAsync(PendingContactId p) {
|
||||||
|
worker.execute(() -> connectionOpened(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Worker
|
||||||
|
private void connectionOpened(PendingContactId p) {
|
||||||
|
// Check that the pending contact hasn't expired
|
||||||
|
if (cryptoStates.containsKey(p)) broadcastState(p, ADDING_CONTACT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventExecutor
|
||||||
|
private void connectionFailedAsync(PendingContactId p) {
|
||||||
|
worker.execute(() -> connectionFailed(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Worker
|
||||||
|
private void connectionFailed(PendingContactId p) {
|
||||||
|
// Check that the pending contact hasn't expired
|
||||||
|
if (cryptoStates.containsKey(p))
|
||||||
|
broadcastState(p, WAITING_FOR_CONNECTION);
|
||||||
|
}
|
||||||
|
|
||||||
private static class PluginState {
|
private static class PluginState {
|
||||||
|
|
||||||
private final DuplexPlugin plugin;
|
private final DuplexPlugin plugin;
|
||||||
|
|||||||
@@ -5,26 +5,20 @@ 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.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.PendingContactStateChangedEvent;
|
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.AuthorInfo;
|
import org.briarproject.bramble.api.identity.AuthorInfo;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousFailedEvent;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
import org.briarproject.bramble.test.PredicateMatcher;
|
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -35,8 +29,6 @@ import java.util.Random;
|
|||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.ADDING_CONTACT;
|
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
|
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
||||||
@@ -65,7 +57,6 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
context.mock(IdentityManager.class);
|
context.mock(IdentityManager.class);
|
||||||
private final PendingContactFactory pendingContactFactory =
|
private final PendingContactFactory pendingContactFactory =
|
||||||
context.mock(PendingContactFactory.class);
|
context.mock(PendingContactFactory.class);
|
||||||
private final EventBus eventBus = context.mock(EventBus.class);
|
|
||||||
|
|
||||||
private final Author remote = getAuthor();
|
private final Author remote = getAuthor();
|
||||||
private final LocalAuthor localAuthor = getLocalAuthor();
|
private final LocalAuthor localAuthor = getLocalAuthor();
|
||||||
@@ -85,7 +76,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
contactManager = new ContactManagerImpl(db, keyManager,
|
contactManager = new ContactManagerImpl(db, keyManager,
|
||||||
identityManager, pendingContactFactory, eventBus);
|
identityManager, pendingContactFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -328,143 +319,4 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
assertEquals(WAITING_FOR_CONNECTION, pair.getSecond());
|
assertEquals(WAITING_FOR_CONNECTION, pair.getSecond());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPendingContactExpiresBeforeConnection() {
|
|
||||||
// The pending contact expires - the FAILED state is broadcast
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
|
||||||
PendingContactStateChangedEvent.class, e ->
|
|
||||||
e.getPendingContactState() == FAILED)));
|
|
||||||
}});
|
|
||||||
contactManager.eventOccurred(new RendezvousFailedEvent(
|
|
||||||
pendingContact.getId()));
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
// A rendezvous connection is opened - no state is broadcast
|
|
||||||
contactManager.eventOccurred(new RendezvousConnectionOpenedEvent(
|
|
||||||
pendingContact.getId()));
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
// The rendezvous connection fails - no state is broadcast
|
|
||||||
contactManager.eventOccurred(new RendezvousConnectionClosedEvent(
|
|
||||||
pendingContact.getId(), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPendingContactExpiresDuringFailedConnection() {
|
|
||||||
// A rendezvous connection is opened - the ADDING_CONTACT state is
|
|
||||||
// broadcast
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
|
||||||
PendingContactStateChangedEvent.class, e ->
|
|
||||||
e.getPendingContactState() == ADDING_CONTACT)));
|
|
||||||
}});
|
|
||||||
|
|
||||||
contactManager.eventOccurred(new RendezvousConnectionOpenedEvent(
|
|
||||||
pendingContact.getId()));
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
// The pending contact expires - the FAILED state is broadcast
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
|
||||||
PendingContactStateChangedEvent.class, e ->
|
|
||||||
e.getPendingContactState() == FAILED)));
|
|
||||||
}});
|
|
||||||
|
|
||||||
contactManager.eventOccurred(new RendezvousFailedEvent(
|
|
||||||
pendingContact.getId()));
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
// The rendezvous connection fails - no state is broadcast
|
|
||||||
contactManager.eventOccurred(new RendezvousConnectionClosedEvent(
|
|
||||||
pendingContact.getId(), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPendingContactExpiresDuringSuccessfulConnection()
|
|
||||||
throws Exception {
|
|
||||||
Transaction txn = new Transaction(null, false);
|
|
||||||
|
|
||||||
// A rendezvous connection is opened - the ADDING_CONTACT state is
|
|
||||||
// broadcast
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
|
||||||
PendingContactStateChangedEvent.class, e ->
|
|
||||||
e.getPendingContactState() == ADDING_CONTACT)));
|
|
||||||
}});
|
|
||||||
|
|
||||||
contactManager.eventOccurred(new RendezvousConnectionOpenedEvent(
|
|
||||||
pendingContact.getId()));
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
// The pending contact expires - the FAILED state is broadcast
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
|
||||||
PendingContactStateChangedEvent.class, e ->
|
|
||||||
e.getPendingContactState() == FAILED)));
|
|
||||||
}});
|
|
||||||
|
|
||||||
contactManager.eventOccurred(new RendezvousFailedEvent(
|
|
||||||
pendingContact.getId()));
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
// The pending contact is converted to a contact - no state is broadcast
|
|
||||||
context.checking(new DbExpectations() {{
|
|
||||||
oneOf(db).getPendingContact(txn, pendingContact.getId());
|
|
||||||
will(returnValue(pendingContact));
|
|
||||||
oneOf(db).removePendingContact(txn, pendingContact.getId());
|
|
||||||
oneOf(db).addContact(txn, remote, local,
|
|
||||||
pendingContact.getPublicKey(), verified);
|
|
||||||
will(returnValue(contactId));
|
|
||||||
oneOf(db).setContactAlias(txn, contactId,
|
|
||||||
pendingContact.getAlias());
|
|
||||||
oneOf(identityManager).getHandshakeKeys(txn);
|
|
||||||
will(returnValue(handshakeKeyPair));
|
|
||||||
oneOf(keyManager).addContact(txn, contactId,
|
|
||||||
pendingContact.getPublicKey(), handshakeKeyPair);
|
|
||||||
oneOf(keyManager).addRotationKeys(txn, contactId, rootKey,
|
|
||||||
timestamp, alice, active);
|
|
||||||
oneOf(db).getContact(txn, contactId);
|
|
||||||
will(returnValue(contact));
|
|
||||||
}});
|
|
||||||
|
|
||||||
contactManager.addContact(txn, pendingContact.getId(), remote,
|
|
||||||
local, rootKey, timestamp, alice, verified, active);
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
// The rendezvous connection succeeds - no state is broadcast
|
|
||||||
contactManager.eventOccurred(new RendezvousConnectionClosedEvent(
|
|
||||||
pendingContact.getId(), true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPendingContactRemovedDuringFailedConnection()
|
|
||||||
throws Exception {
|
|
||||||
Transaction txn = new Transaction(null, false);
|
|
||||||
|
|
||||||
// A rendezvous connection is opened - the ADDING_CONTACT state is
|
|
||||||
// broadcast
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
|
||||||
PendingContactStateChangedEvent.class, e ->
|
|
||||||
e.getPendingContactState() == ADDING_CONTACT)));
|
|
||||||
}});
|
|
||||||
|
|
||||||
contactManager.eventOccurred(new RendezvousConnectionOpenedEvent(
|
|
||||||
pendingContact.getId()));
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
// The pending contact is removed - no state is broadcast
|
|
||||||
context.checking(new DbExpectations() {{
|
|
||||||
oneOf(db).transaction(with(false), withDbRunnable(txn));
|
|
||||||
oneOf(db).removePendingContact(txn, pendingContact.getId());
|
|
||||||
}});
|
|
||||||
|
|
||||||
contactManager.removePendingContact(pendingContact.getId());
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
|
|
||||||
// The rendezvous connection fails - no state is broadcast
|
|
||||||
contactManager.eventOccurred(new RendezvousConnectionClosedEvent(
|
|
||||||
pendingContact.getId(), false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package org.briarproject.bramble.rendezvous;
|
package org.briarproject.bramble.rendezvous;
|
||||||
|
|
||||||
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.event.PendingContactAddedEvent;
|
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
|
||||||
|
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
@@ -20,12 +22,15 @@ import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
|
|||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
||||||
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousFailedEvent;
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousPollEvent;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.CaptureArgumentAction;
|
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||||
|
import org.briarproject.bramble.test.PredicateMatcher;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -38,6 +43,9 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static org.briarproject.bramble.api.contact.PendingContactState.ADDING_CONTACT;
|
||||||
|
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
|
||||||
|
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||||
import static org.briarproject.bramble.rendezvous.RendezvousConstants.POLLING_INTERVAL_MS;
|
import static org.briarproject.bramble.rendezvous.RendezvousConstants.POLLING_INTERVAL_MS;
|
||||||
import static org.briarproject.bramble.rendezvous.RendezvousConstants.RENDEZVOUS_TIMEOUT_MS;
|
import static org.briarproject.bramble.rendezvous.RendezvousConstants.RENDEZVOUS_TIMEOUT_MS;
|
||||||
import static org.briarproject.bramble.test.CollectionMatcher.collectionOf;
|
import static org.briarproject.bramble.test.CollectionMatcher.collectionOf;
|
||||||
@@ -110,7 +118,10 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
// The pending contact has not expired
|
// The pending contact has not expired
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(beforeExpiry));
|
will(returnValue(beforeExpiry));
|
||||||
// Capture the poll task, we'll run it later
|
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
||||||
|
PendingContactStateChangedEvent.class, e ->
|
||||||
|
e.getPendingContactState() == WAITING_FOR_CONNECTION)));
|
||||||
|
// Capture the poll task
|
||||||
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
|
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
|
||||||
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
|
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
|
||||||
with(MILLISECONDS));
|
with(MILLISECONDS));
|
||||||
@@ -124,11 +135,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
// Run the poll task - pending contact expires
|
// Run the poll task - pending contact expires
|
||||||
context.checking(new Expectations() {{
|
expectPendingContactExpires(afterExpiry);
|
||||||
oneOf(clock).currentTimeMillis();
|
|
||||||
will(returnValue(afterExpiry));
|
|
||||||
oneOf(eventBus).broadcast(with(any(RendezvousFailedEvent.class)));
|
|
||||||
}});
|
|
||||||
|
|
||||||
capturePollTask.get().run();
|
capturePollTask.get().run();
|
||||||
}
|
}
|
||||||
@@ -147,7 +154,9 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
// The pending contact has already expired
|
// The pending contact has already expired
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(atExpiry));
|
will(returnValue(atExpiry));
|
||||||
oneOf(eventBus).broadcast(with(any(RendezvousFailedEvent.class)));
|
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
||||||
|
PendingContactStateChangedEvent.class, e ->
|
||||||
|
e.getPendingContactState() == FAILED)));
|
||||||
// Schedule the poll task
|
// Schedule the poll task
|
||||||
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
|
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
|
||||||
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
|
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
|
||||||
@@ -175,11 +184,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
// Add the pending contact - endpoint should be created and polled
|
// Add the pending contact - endpoint should be created and polled
|
||||||
context.checking(new Expectations() {{
|
expectAddUnexpiredPendingContact(beforeExpiry);
|
||||||
oneOf(clock).currentTimeMillis();
|
|
||||||
will(returnValue(beforeExpiry));
|
|
||||||
}});
|
|
||||||
|
|
||||||
expectDeriveRendezvousKey();
|
expectDeriveRendezvousKey();
|
||||||
expectCreateEndpoint();
|
expectCreateEndpoint();
|
||||||
|
|
||||||
@@ -187,6 +192,9 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
// Poll newly added pending contact
|
// Poll newly added pending contact
|
||||||
oneOf(rendezvousEndpoint).getRemoteTransportProperties();
|
oneOf(rendezvousEndpoint).getRemoteTransportProperties();
|
||||||
will(returnValue(transportProperties));
|
will(returnValue(transportProperties));
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(beforeExpiry));
|
||||||
|
oneOf(eventBus).broadcast(with(any(RendezvousPollEvent.class)));
|
||||||
oneOf(plugin).poll(with(collectionOf(pairOf(
|
oneOf(plugin).poll(with(collectionOf(pairOf(
|
||||||
equal(transportProperties),
|
equal(transportProperties),
|
||||||
any(ConnectionHandler.class)))));
|
any(ConnectionHandler.class)))));
|
||||||
@@ -230,11 +238,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
// Add the pending contact - endpoint should be created and polled
|
// Add the pending contact - endpoint should be created and polled
|
||||||
context.checking(new Expectations() {{
|
expectAddUnexpiredPendingContact(beforeExpiry);
|
||||||
oneOf(clock).currentTimeMillis();
|
|
||||||
will(returnValue(beforeExpiry));
|
|
||||||
}});
|
|
||||||
|
|
||||||
expectDeriveRendezvousKey();
|
expectDeriveRendezvousKey();
|
||||||
expectCreateEndpoint();
|
expectCreateEndpoint();
|
||||||
|
|
||||||
@@ -242,6 +246,9 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
// Poll newly added pending contact
|
// Poll newly added pending contact
|
||||||
oneOf(rendezvousEndpoint).getRemoteTransportProperties();
|
oneOf(rendezvousEndpoint).getRemoteTransportProperties();
|
||||||
will(returnValue(transportProperties));
|
will(returnValue(transportProperties));
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(beforeExpiry));
|
||||||
|
oneOf(eventBus).broadcast(with(any(RendezvousPollEvent.class)));
|
||||||
oneOf(plugin).poll(with(collectionOf(pairOf(
|
oneOf(plugin).poll(with(collectionOf(pairOf(
|
||||||
equal(transportProperties),
|
equal(transportProperties),
|
||||||
any(ConnectionHandler.class)))));
|
any(ConnectionHandler.class)))));
|
||||||
@@ -252,11 +259,10 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
// Run the poll task - pending contact expires, endpoint is closed
|
// Run the poll task - pending contact expires, endpoint is closed
|
||||||
|
expectPendingContactExpires(afterExpiry);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(clock).currentTimeMillis();
|
|
||||||
will(returnValue(afterExpiry));
|
|
||||||
oneOf(rendezvousEndpoint).close();
|
oneOf(rendezvousEndpoint).close();
|
||||||
oneOf(eventBus).broadcast(with(any(RendezvousFailedEvent.class)));
|
|
||||||
}});
|
}});
|
||||||
|
|
||||||
capturePollTask.get().run();
|
capturePollTask.get().run();
|
||||||
@@ -283,11 +289,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
// Add the pending contact - no endpoints should be created yet
|
// Add the pending contact - no endpoints should be created yet
|
||||||
context.checking(new DbExpectations() {{
|
expectAddUnexpiredPendingContact(beforeExpiry);
|
||||||
oneOf(clock).currentTimeMillis();
|
|
||||||
will(returnValue(beforeExpiry));
|
|
||||||
}});
|
|
||||||
|
|
||||||
expectDeriveRendezvousKey();
|
expectDeriveRendezvousKey();
|
||||||
|
|
||||||
rendezvousPoller.eventOccurred(
|
rendezvousPoller.eventOccurred(
|
||||||
@@ -314,15 +316,162 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
new PendingContactRemovedEvent(pendingContact.getId()));
|
new PendingContactRemovedEvent(pendingContact.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRendezvousConnectionEvents() throws Exception {
|
||||||
|
long beforeExpiry = pendingContact.getTimestamp();
|
||||||
|
|
||||||
|
// Start the service
|
||||||
|
expectStartupWithPendingContact(beforeExpiry);
|
||||||
|
|
||||||
|
rendezvousPoller.startService();
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Connection is opened - event should be broadcast
|
||||||
|
expectStateChangedEvent(ADDING_CONTACT);
|
||||||
|
|
||||||
|
rendezvousPoller.eventOccurred(
|
||||||
|
new RendezvousConnectionOpenedEvent(pendingContact.getId()));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Connection fails - event should be broadcast
|
||||||
|
expectStateChangedEvent(WAITING_FOR_CONNECTION);
|
||||||
|
|
||||||
|
rendezvousPoller.eventOccurred(new RendezvousConnectionClosedEvent(
|
||||||
|
pendingContact.getId(), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPendingContactExpiresBeforeConnection() throws Exception {
|
||||||
|
long beforeExpiry = pendingContact.getTimestamp()
|
||||||
|
+ RENDEZVOUS_TIMEOUT_MS - 1000;
|
||||||
|
long afterExpiry = beforeExpiry + POLLING_INTERVAL_MS;
|
||||||
|
|
||||||
|
// Start the service, capturing the poll task
|
||||||
|
AtomicReference<Runnable> capturePollTask =
|
||||||
|
expectStartupWithPendingContact(beforeExpiry);
|
||||||
|
|
||||||
|
rendezvousPoller.startService();
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Run the poll task - pending contact expires
|
||||||
|
expectPendingContactExpires(afterExpiry);
|
||||||
|
|
||||||
|
capturePollTask.get().run();
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Connection is opened - no event should be broadcast
|
||||||
|
rendezvousPoller.eventOccurred(
|
||||||
|
new RendezvousConnectionOpenedEvent(pendingContact.getId()));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Connection fails - no event should be broadcast
|
||||||
|
rendezvousPoller.eventOccurred(new RendezvousConnectionClosedEvent(
|
||||||
|
pendingContact.getId(), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPendingContactExpiresDuringFailedConnection()
|
||||||
|
throws Exception {
|
||||||
|
long beforeExpiry = pendingContact.getTimestamp()
|
||||||
|
+ RENDEZVOUS_TIMEOUT_MS - 1000;
|
||||||
|
long afterExpiry = beforeExpiry + POLLING_INTERVAL_MS;
|
||||||
|
|
||||||
|
// Start the service, capturing the poll task
|
||||||
|
AtomicReference<Runnable> capturePollTask =
|
||||||
|
expectStartupWithPendingContact(beforeExpiry);
|
||||||
|
|
||||||
|
rendezvousPoller.startService();
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Connection is opened - event should be broadcast
|
||||||
|
expectStateChangedEvent(ADDING_CONTACT);
|
||||||
|
|
||||||
|
rendezvousPoller.eventOccurred(
|
||||||
|
new RendezvousConnectionOpenedEvent(pendingContact.getId()));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Run the poll task - pending contact expires
|
||||||
|
expectPendingContactExpires(afterExpiry);
|
||||||
|
|
||||||
|
capturePollTask.get().run();
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Connection fails - no event should be broadcast
|
||||||
|
rendezvousPoller.eventOccurred(new RendezvousConnectionClosedEvent(
|
||||||
|
pendingContact.getId(), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPendingContactExpiresDuringSuccessfulConnection()
|
||||||
|
throws Exception {
|
||||||
|
long beforeExpiry = pendingContact.getTimestamp()
|
||||||
|
+ RENDEZVOUS_TIMEOUT_MS - 1000;
|
||||||
|
long afterExpiry = beforeExpiry + POLLING_INTERVAL_MS;
|
||||||
|
|
||||||
|
// Start the service, capturing the poll task
|
||||||
|
AtomicReference<Runnable> capturePollTask =
|
||||||
|
expectStartupWithPendingContact(beforeExpiry);
|
||||||
|
|
||||||
|
rendezvousPoller.startService();
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Connection is opened - event should be broadcast
|
||||||
|
expectStateChangedEvent(ADDING_CONTACT);
|
||||||
|
|
||||||
|
rendezvousPoller.eventOccurred(
|
||||||
|
new RendezvousConnectionOpenedEvent(pendingContact.getId()));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Run the poll task - pending contact expires
|
||||||
|
expectPendingContactExpires(afterExpiry);
|
||||||
|
|
||||||
|
capturePollTask.get().run();
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Pending contact is removed - no event should be broadcast
|
||||||
|
rendezvousPoller.eventOccurred(
|
||||||
|
new PendingContactRemovedEvent(pendingContact.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPendingContactRemovedDuringFailedConnection()
|
||||||
|
throws Exception {
|
||||||
|
long beforeExpiry = pendingContact.getTimestamp();
|
||||||
|
|
||||||
|
// Start the service
|
||||||
|
expectStartupWithPendingContact(beforeExpiry);
|
||||||
|
|
||||||
|
rendezvousPoller.startService();
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Connection is opened - event should be broadcast
|
||||||
|
expectStateChangedEvent(ADDING_CONTACT);
|
||||||
|
|
||||||
|
rendezvousPoller.eventOccurred(
|
||||||
|
new RendezvousConnectionOpenedEvent(pendingContact.getId()));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Pending contact is removed - no event should be broadcast
|
||||||
|
rendezvousPoller.eventOccurred(
|
||||||
|
new PendingContactRemovedEvent(pendingContact.getId()));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Connection fails - no event should be broadcast
|
||||||
|
rendezvousPoller.eventOccurred(new RendezvousConnectionClosedEvent(
|
||||||
|
pendingContact.getId(), false));
|
||||||
|
}
|
||||||
|
|
||||||
private AtomicReference<Runnable> expectStartupWithNoPendingContacts()
|
private AtomicReference<Runnable> expectStartupWithNoPendingContacts()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
AtomicReference<Runnable> capturePollTask = new AtomicReference<>();
|
AtomicReference<Runnable> capturePollTask = new AtomicReference<>();
|
||||||
|
|
||||||
context.checking(new DbExpectations() {{
|
context.checking(new DbExpectations() {{
|
||||||
|
// Load the pending contacts
|
||||||
oneOf(db).transaction(with(true), withDbRunnable(txn));
|
oneOf(db).transaction(with(true), withDbRunnable(txn));
|
||||||
oneOf(db).getPendingContacts(txn);
|
oneOf(db).getPendingContacts(txn);
|
||||||
will(returnValue(emptyList()));
|
will(returnValue(emptyList()));
|
||||||
|
// Capture the poll task
|
||||||
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
|
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
|
||||||
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
|
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
|
||||||
with(MILLISECONDS));
|
with(MILLISECONDS));
|
||||||
@@ -333,6 +482,16 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
return capturePollTask;
|
return capturePollTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void expectAddUnexpiredPendingContact(long now) {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(now));
|
||||||
|
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
||||||
|
PendingContactStateChangedEvent.class, e ->
|
||||||
|
e.getPendingContactState() == WAITING_FOR_CONNECTION)));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
private void expectDeriveRendezvousKey() throws Exception {
|
private void expectDeriveRendezvousKey() throws Exception {
|
||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
|
|
||||||
@@ -371,4 +530,50 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(transportId));
|
will(returnValue(transportId));
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AtomicReference<Runnable> expectStartupWithPendingContact(long now)
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, true);
|
||||||
|
AtomicReference<Runnable> capturePollTask = new AtomicReference<>();
|
||||||
|
|
||||||
|
context.checking(new DbExpectations() {{
|
||||||
|
// Load the pending contacts
|
||||||
|
oneOf(db).transaction(with(true), withDbRunnable(txn));
|
||||||
|
oneOf(db).getPendingContacts(txn);
|
||||||
|
will(returnValue(singletonList(pendingContact)));
|
||||||
|
// The pending contact has not expired
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(now));
|
||||||
|
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
||||||
|
PendingContactStateChangedEvent.class, e ->
|
||||||
|
e.getPendingContactState() == WAITING_FOR_CONNECTION)));
|
||||||
|
// Capture the poll task
|
||||||
|
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
|
||||||
|
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
|
||||||
|
with(MILLISECONDS));
|
||||||
|
will(new CaptureArgumentAction<>(capturePollTask, Runnable.class,
|
||||||
|
0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
expectDeriveRendezvousKey();
|
||||||
|
|
||||||
|
return capturePollTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectPendingContactExpires(long now) {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(now));
|
||||||
|
}});
|
||||||
|
|
||||||
|
expectStateChangedEvent(FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectStateChangedEvent(PendingContactState state) {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
|
||||||
|
PendingContactStateChangedEvent.class, e ->
|
||||||
|
e.getPendingContactState() == state)));
|
||||||
|
}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,24 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.contact.PendingContactState.CONNECTING;
|
||||||
|
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class PendingContactItem {
|
class PendingContactItem {
|
||||||
|
|
||||||
|
static final int POLL_DURATION_MS = 15_000;
|
||||||
|
|
||||||
private final PendingContact pendingContact;
|
private final PendingContact pendingContact;
|
||||||
private final PendingContactState state;
|
private final PendingContactState state;
|
||||||
|
private final long lastPoll;
|
||||||
|
|
||||||
PendingContactItem(PendingContact pendingContact,
|
PendingContactItem(PendingContact pendingContact,
|
||||||
PendingContactState state) {
|
PendingContactState state, long lastPoll) {
|
||||||
this.pendingContact = pendingContact;
|
this.pendingContact = pendingContact;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
this.lastPoll = lastPoll;
|
||||||
}
|
}
|
||||||
|
|
||||||
PendingContact getPendingContact() {
|
PendingContact getPendingContact() {
|
||||||
@@ -24,6 +31,10 @@ class PendingContactItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PendingContactState getState() {
|
PendingContactState getState() {
|
||||||
|
if (state == WAITING_FOR_CONNECTION &&
|
||||||
|
System.currentTimeMillis() - lastPoll < POLL_DURATION_MS) {
|
||||||
|
return CONNECTING;
|
||||||
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import javax.annotation.Nullable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
|
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
|
||||||
|
import static org.briarproject.briar.android.contact.add.remote.PendingContactItem.POLL_DURATION_MS;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -69,7 +70,7 @@ public class PendingContactListActivity extends BriarActivity
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
list.startPeriodicUpdate();
|
list.startPeriodicUpdate(POLL_DURATION_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ 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.rendezvous.RendezvousPoller;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousPollEvent;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -41,6 +43,7 @@ public class PendingContactListViewModel extends AndroidViewModel
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final ContactManager contactManager;
|
private final ContactManager contactManager;
|
||||||
|
private final RendezvousPoller rendezvousPoller;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
|
|
||||||
private final MutableLiveData<Collection<PendingContactItem>>
|
private final MutableLiveData<Collection<PendingContactItem>>
|
||||||
@@ -49,10 +52,13 @@ public class PendingContactListViewModel extends AndroidViewModel
|
|||||||
@Inject
|
@Inject
|
||||||
PendingContactListViewModel(Application application,
|
PendingContactListViewModel(Application application,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
ContactManager contactManager, EventBus eventBus) {
|
ContactManager contactManager,
|
||||||
|
RendezvousPoller rendezvousPoller,
|
||||||
|
EventBus eventBus) {
|
||||||
super(application);
|
super(application);
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.contactManager = contactManager;
|
this.contactManager = contactManager;
|
||||||
|
this.rendezvousPoller = rendezvousPoller;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.eventBus.addListener(this);
|
this.eventBus.addListener(this);
|
||||||
loadPendingContacts();
|
loadPendingContacts();
|
||||||
@@ -67,7 +73,8 @@ public class PendingContactListViewModel extends AndroidViewModel
|
|||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof PendingContactStateChangedEvent ||
|
if (e instanceof PendingContactStateChangedEvent ||
|
||||||
e instanceof PendingContactRemovedEvent) {
|
e instanceof PendingContactRemovedEvent ||
|
||||||
|
e instanceof RendezvousPollEvent) {
|
||||||
loadPendingContacts();
|
loadPendingContacts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,9 +85,11 @@ public class PendingContactListViewModel extends AndroidViewModel
|
|||||||
Collection<Pair<PendingContact, PendingContactState>> pairs =
|
Collection<Pair<PendingContact, PendingContactState>> pairs =
|
||||||
contactManager.getPendingContacts();
|
contactManager.getPendingContacts();
|
||||||
List<PendingContactItem> items = new ArrayList<>(pairs.size());
|
List<PendingContactItem> items = new ArrayList<>(pairs.size());
|
||||||
for (Pair<PendingContact, PendingContactState> p : pairs) {
|
for (Pair<PendingContact, PendingContactState> pair : pairs) {
|
||||||
items.add(new PendingContactItem(p.getFirst(),
|
PendingContact p = pair.getFirst();
|
||||||
p.getSecond()));
|
long lastPoll = rendezvousPoller.getLastPollTime(p.getId());
|
||||||
|
items.add(new PendingContactItem(p, pair.getSecond(),
|
||||||
|
lastPoll));
|
||||||
}
|
}
|
||||||
pendingContacts.postValue(items);
|
pendingContacts.postValue(items);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class PendingContactViewHolder extends ViewHolder {
|
|||||||
.getColor(status.getContext(), R.color.briar_yellow);
|
.getColor(status.getContext(), R.color.briar_yellow);
|
||||||
status.setText(R.string.waiting_for_contact_to_come_online);
|
status.setText(R.string.waiting_for_contact_to_come_online);
|
||||||
break;
|
break;
|
||||||
case CONNECTED:
|
case CONNECTING:
|
||||||
status.setText(R.string.connecting);
|
status.setText(R.string.connecting);
|
||||||
break;
|
break;
|
||||||
case ADDING_CONTACT:
|
case ADDING_CONTACT:
|
||||||
|
|||||||
@@ -211,15 +211,19 @@ public class BriarRecyclerView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startPeriodicUpdate() {
|
public void startPeriodicUpdate() {
|
||||||
|
startPeriodicUpdate(MIN_DATE_RESOLUTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startPeriodicUpdate(long interval) {
|
||||||
if (recyclerView == null || recyclerView.getAdapter() == null) {
|
if (recyclerView == null || recyclerView.getAdapter() == null) {
|
||||||
throw new IllegalStateException("Need to call setAdapter() first!");
|
throw new IllegalStateException("Need to call setAdapter() first!");
|
||||||
}
|
}
|
||||||
refresher = () -> {
|
refresher = () -> {
|
||||||
Adapter adapter = recyclerView.getAdapter();
|
Adapter adapter = recyclerView.getAdapter();
|
||||||
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
|
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
|
||||||
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
|
handler.postDelayed(refresher, interval);
|
||||||
};
|
};
|
||||||
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
|
handler.postDelayed(refresher, interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopPeriodicUpdate() {
|
public void stopPeriodicUpdate() {
|
||||||
|
|||||||
@@ -193,7 +193,7 @@
|
|||||||
<string name="pending_contact_requests">Pending Contact Requests</string>
|
<string name="pending_contact_requests">Pending Contact Requests</string>
|
||||||
<string name="no_pending_contacts">No pending contacts</string>
|
<string name="no_pending_contacts">No pending contacts</string>
|
||||||
<string name="add_contact_remote_connecting">Connecting…</string>
|
<string name="add_contact_remote_connecting">Connecting…</string>
|
||||||
<string name="waiting_for_contact_to_come_online">Waiting for contact to come online…\n\nDid they enter your link already?</string>
|
<string name="waiting_for_contact_to_come_online">Waiting for contact to come online…</string>
|
||||||
<string name="connecting">Connecting…</string>
|
<string name="connecting">Connecting…</string>
|
||||||
<string name="adding_contact">Adding contact…</string>
|
<string name="adding_contact">Adding contact…</string>
|
||||||
<string name="adding_contact_failed">Adding contact has failed</string>
|
<string name="adding_contact_failed">Adding contact has failed</string>
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ This will return a JSON array of pending contacts and their states:
|
|||||||
The state can be one of these values:
|
The state can be one of these values:
|
||||||
|
|
||||||
* `waiting_for_connection`
|
* `waiting_for_connection`
|
||||||
* `connected`
|
* `connecting`
|
||||||
* `adding_contact`
|
* `adding_contact`
|
||||||
* `failed`
|
* `failed`
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ internal fun PendingContact.output() = JsonDict(
|
|||||||
|
|
||||||
internal fun PendingContactState.output() = when(this) {
|
internal fun PendingContactState.output() = when(this) {
|
||||||
WAITING_FOR_CONNECTION -> "waiting_for_connection"
|
WAITING_FOR_CONNECTION -> "waiting_for_connection"
|
||||||
CONNECTED -> "connected"
|
CONNECTING -> "connecting"
|
||||||
ADDING_CONTACT -> "adding_contact"
|
ADDING_CONTACT -> "adding_contact"
|
||||||
FAILED -> "failed"
|
FAILED -> "failed"
|
||||||
else -> throw AssertionError()
|
else -> throw AssertionError()
|
||||||
|
|||||||
Reference in New Issue
Block a user