diff --git a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java index 4ebf3886e5d60bf67cbdb8a2480029902fa6d638..ca899bc8aed4e8a67cc0c13e3619426cbedd4c2a 100644 --- a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java +++ b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java @@ -38,7 +38,7 @@ public class AndroidPluginsModule { DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor, androidExecutor, appContext, random, backoffFactory); DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext, - locationUtils, reporter, eventBus); + locationUtils, reporter, eventBus, backoffFactory); DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, backoffFactory, appContext); final Collection<DuplexPluginFactory> duplex = diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java index ee7acf26ba47c4fb39128f3eede571189c39e4f7..7bc16525ac0907e75839c8205c0329cfe6bfea9a 100644 --- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java @@ -16,8 +16,8 @@ import java.util.concurrent.Executor; public class DroidtoothPluginFactory implements DuplexPluginFactory { private static final int MAX_LATENCY = 30 * 1000; // 30 seconds - private static final int MIN_POLLING_INTERVAL = 2 * 60 * 1000; // 2 minutes - private static final int MAX_POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour + 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 double BACKOFF_BASE = 1.2; private final Executor ioExecutor; @@ -36,14 +36,17 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory { this.backoffFactory = backoffFactory; } + @Override public TransportId getId() { return DroidtoothPlugin.ID; } + @Override public int getMaxLatency() { return MAX_LATENCY; } + @Override public DuplexPlugin createPlugin(DuplexPluginCallback callback) { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); diff --git a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java index 16d456ad3b22e47a2e128e45afd55705dccba3dc..50ee377b84516ed0bf3eb3a8e29663878b8439d2 100644 --- a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java @@ -15,8 +15,8 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory { 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 = 2 * 60 * 1000; // 2 minutes - private static final int MAX_POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour + 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 double BACKOFF_BASE = 1.2; private final Executor ioExecutor; @@ -30,14 +30,17 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory { this.appContext = appContext; } + @Override public TransportId getId() { return LanTcpPlugin.ID; } + @Override public int getMaxLatency() { return MAX_LATENCY; } + @Override public DuplexPlugin createPlugin(DuplexPluginCallback callback) { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index e7852bff66fe9340f3c16b4d6f3744f0913ae88e..19499171b2279cfb18510382618d787586c459c5 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -23,13 +23,13 @@ import org.briarproject.api.event.EventListener; import org.briarproject.api.event.SettingsUpdatedEvent; import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.TransportDescriptor; +import org.briarproject.api.plugins.Backoff; import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.properties.TransportProperties; import org.briarproject.api.reporting.DevReporter; import org.briarproject.api.settings.Settings; -import org.briarproject.api.system.Clock; import org.briarproject.api.system.LocationUtils; import org.briarproject.util.StringUtils; @@ -79,7 +79,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private static final int SOCKS_PORT = 59050, CONTROL_PORT = 59051; private static final int COOKIE_TIMEOUT = 3000; // Milliseconds private static final Pattern ONION = Pattern.compile("[a-z2-7]{16}"); - private static final int MIN_DESCRIPTORS_PUBLISHED = 3; private static final Logger LOG = Logger.getLogger(TorPlugin.class.getName()); @@ -87,10 +86,10 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private final Context appContext; private final LocationUtils locationUtils; private final DevReporter reporter; - private final Clock clock; + private final Backoff backoff; private final DuplexPluginCallback callback; private final String architecture; - private final int maxLatency, maxIdleTime, pollingInterval, socketTimeout; + private final int maxLatency, maxIdleTime, socketTimeout; private final ConnectionStatus connectionStatus; private final File torDirectory, torFile, geoIpFile, configFile; private final File doneFile, cookieFile; @@ -103,23 +102,22 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private volatile BroadcastReceiver networkStateReceiver = null; TorPlugin(Executor ioExecutor, Context appContext, - LocationUtils locationUtils, DevReporter reporter, Clock clock, + LocationUtils locationUtils, DevReporter reporter, Backoff backoff, DuplexPluginCallback callback, String architecture, int maxLatency, - int maxIdleTime, int pollingInterval) { + int maxIdleTime) { this.ioExecutor = ioExecutor; this.appContext = appContext; this.locationUtils = locationUtils; this.reporter = reporter; - this.clock = clock; + this.backoff = backoff; this.callback = callback; this.architecture = architecture; this.maxLatency = maxLatency; this.maxIdleTime = maxIdleTime; - this.pollingInterval = pollingInterval; if (maxIdleTime > Integer.MAX_VALUE / 2) socketTimeout = Integer.MAX_VALUE; else socketTimeout = maxIdleTime * 2; - connectionStatus = new ConnectionStatus(pollingInterval); + connectionStatus = new ConnectionStatus(); torDirectory = appContext.getDir("tor", MODE_PRIVATE); torFile = new File(torDirectory, "tor"); geoIpFile = new File(torDirectory, "geoip"); @@ -228,7 +226,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (phase != null && phase.contains("PROGRESS=100")) { LOG.info("Tor has already bootstrapped"); connectionStatus.setBootstrapped(); - sendCrashReports(); + sendDevReports(); } } // Register to receive network status events @@ -359,12 +357,12 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } - private void sendCrashReports() { + private void sendDevReports() { ioExecutor.execute(new Runnable() { @Override public void run() { - reporter.sendReports( - AndroidUtils.getReportDir(appContext), SOCKS_PORT); + File reportDir = AndroidUtils.getReportDir(appContext); + reporter.sendReports(reportDir, SOCKS_PORT); } }); } @@ -404,6 +402,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { publishHiddenService(localPort); } }); + backoff.reset(); // Accept incoming hidden service connections from Tor acceptContactConnections(ss); } @@ -470,6 +469,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { return; } LOG.info("Connection received"); + backoff.reset(); TorTransportConnection conn = new TorTransportConnection(this, s); callback.incomingConnectionCreated(conn); } @@ -517,25 +517,25 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } public int getPollingInterval() { - return pollingInterval; + return backoff.getPollingInterval(); } public void poll(Collection<ContactId> connected) { if (!isRunning()) return; - if (connectionStatus.shouldPoll(clock.currentTimeMillis())) { - // TODO: Pass properties to connectAndCallBack() - for (ContactId c : callback.getRemoteProperties().keySet()) - if (!connected.contains(c)) connectAndCallBack(c); - } else { - LOG.info("Hidden service descriptor published, not polling"); - } + backoff.increment(); + // TODO: Pass properties to connectAndCallBack() + for (ContactId c : callback.getRemoteProperties().keySet()) + if (!connected.contains(c)) connectAndCallBack(c); } private void connectAndCallBack(final ContactId c) { ioExecutor.execute(new Runnable() { public void run() { DuplexTransportConnection d = createConnection(c); - if (d != null) callback.outgoingConnectionCreated(c, d); + if (d != null) { + backoff.reset(); + callback.outgoingConnectionCreated(c, d); + } } }); } @@ -561,7 +561,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { return new TorTransportConnection(this, s); } catch (IOException e) { if (LOG.isLoggable(INFO)) - LOG.log(INFO, "Could not connect to " + onion + ": ", e); + LOG.info("Could not connect to " + onion + ": " + e.toString()); return null; } } @@ -593,6 +593,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (status.equals("BUILT") && connectionStatus.getAndSetCircuitBuilt()) { LOG.info("First circuit built"); + backoff.reset(); if (isRunning()) callback.transportEnabled(); } } @@ -614,14 +615,15 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { connectionStatus.setBootstrapped(); - sendCrashReports(); + sendDevReports(); + backoff.reset(); if (isRunning()) callback.transportEnabled(); } } public void unrecognized(String type, String msg) { if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) - connectionStatus.descriptorPublished(clock.currentTimeMillis()); + LOG.info("Descriptor uploaded"); } private static class WriteObserver extends FileObserver { @@ -707,17 +709,9 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private static class ConnectionStatus { - private final int pollingInterval; - // All of the following are locking: this private boolean networkEnabled = false; private boolean bootstrapped = false, circuitBuilt = false; - private int descriptorsPublished = 0; - private long descriptorsPublishedTime = Long.MAX_VALUE; - - private ConnectionStatus(int pollingInterval) { - this.pollingInterval = pollingInterval; - } private synchronized void setBootstrapped() { bootstrapped = true; @@ -729,26 +723,13 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { return firstCircuit; } - private synchronized void descriptorPublished(long now) { - descriptorsPublished++; - if (descriptorsPublished == MIN_DESCRIPTORS_PUBLISHED) - descriptorsPublishedTime = now; - } - private synchronized void enableNetwork(boolean enable) { networkEnabled = enable; circuitBuilt = false; - descriptorsPublished = 0; - descriptorsPublishedTime = Long.MAX_VALUE; } private synchronized boolean isConnected() { return networkEnabled && bootstrapped && circuitBuilt; } - - private synchronized boolean shouldPoll(long now) { - return descriptorsPublished < MIN_DESCRIPTORS_PUBLISHED - || now - descriptorsPublishedTime < 2 * pollingInterval; - } } } diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java index edf0103da9fed1cb4ad300377d1044aad3097eee..9cfabc3cb5a42e1833948920b752ce18107cccf0 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java @@ -6,13 +6,13 @@ import android.os.Build; import org.briarproject.android.util.AndroidUtils; import org.briarproject.api.TransportId; import org.briarproject.api.event.EventBus; +import org.briarproject.api.plugins.Backoff; +import org.briarproject.api.plugins.BackoffFactory; import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginFactory; import org.briarproject.api.reporting.DevReporter; -import org.briarproject.api.system.Clock; import org.briarproject.api.system.LocationUtils; -import org.briarproject.system.SystemClock; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -24,34 +24,39 @@ public class TorPluginFactory implements DuplexPluginFactory { 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 POLLING_INTERVAL = 2 * 60 * 1000; // 2 minutes + 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 double BACKOFF_BASE = 1.2; private final Executor ioExecutor; private final Context appContext; private final LocationUtils locationUtils; private final DevReporter reporter; private final EventBus eventBus; - private final Clock clock; + private final BackoffFactory backoffFactory; public TorPluginFactory(Executor ioExecutor, Context appContext, LocationUtils locationUtils, DevReporter reporter, - EventBus eventBus) { + EventBus eventBus, BackoffFactory backoffFactory) { this.ioExecutor = ioExecutor; this.appContext = appContext; this.locationUtils = locationUtils; this.reporter = reporter; this.eventBus = eventBus; - clock = new SystemClock(); + this.backoffFactory = backoffFactory; } + @Override public TransportId getId() { return TorPlugin.ID; } + @Override public int getMaxLatency() { return MAX_LATENCY; } + @Override public DuplexPlugin createPlugin(DuplexPluginCallback callback) { // Check that we have a Tor binary for this architecture @@ -72,10 +77,11 @@ public class TorPluginFactory implements DuplexPluginFactory { // Use position-independent executable for SDK >= 16 if (Build.VERSION.SDK_INT >= 16) architecture += "-pie"; - TorPlugin plugin = - new TorPlugin(ioExecutor, appContext, locationUtils, reporter, - clock, callback, architecture, MAX_LATENCY, - MAX_IDLE_TIME, POLLING_INTERVAL); + Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, + MAX_POLLING_INTERVAL, BACKOFF_BASE); + TorPlugin plugin = new TorPlugin(ioExecutor, appContext, locationUtils, + reporter, backoff, callback, architecture, MAX_LATENCY, + MAX_IDLE_TIME); eventBus.addListener(plugin); return plugin; } diff --git a/briar-api/src/org/briarproject/api/event/ConnectionClosedEvent.java b/briar-api/src/org/briarproject/api/event/ConnectionClosedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..29030edcc4e382b0aa3392823fc51fe7a1ec2862 --- /dev/null +++ b/briar-api/src/org/briarproject/api/event/ConnectionClosedEvent.java @@ -0,0 +1,30 @@ +package org.briarproject.api.event; + +import org.briarproject.api.TransportId; +import org.briarproject.api.contact.ContactId; + +public class ConnectionClosedEvent extends Event { + + private final ContactId contactId; + private final TransportId transportId; + private final boolean incoming; + + public ConnectionClosedEvent(ContactId contactId, TransportId transportId, + boolean incoming) { + this.contactId = contactId; + this.transportId = transportId; + this.incoming = incoming; + } + + public ContactId getContactId() { + return contactId; + } + + public TransportId getTransportId() { + return transportId; + } + + public boolean isIncoming() { + return incoming; + } +} diff --git a/briar-api/src/org/briarproject/api/event/ConnectionOpenedEvent.java b/briar-api/src/org/briarproject/api/event/ConnectionOpenedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..2143eb37a035b4e79c7887354cc38115b891cdfb --- /dev/null +++ b/briar-api/src/org/briarproject/api/event/ConnectionOpenedEvent.java @@ -0,0 +1,30 @@ +package org.briarproject.api.event; + +import org.briarproject.api.TransportId; +import org.briarproject.api.contact.ContactId; + +public class ConnectionOpenedEvent extends Event { + + private final ContactId contactId; + private final TransportId transportId; + private final boolean incoming; + + public ConnectionOpenedEvent(ContactId contactId, TransportId transportId, + boolean incoming) { + this.contactId = contactId; + this.transportId = transportId; + this.incoming = incoming; + } + + public ContactId getContactId() { + return contactId; + } + + public TransportId getTransportId() { + return transportId; + } + + public boolean isIncoming() { + return incoming; + } +} diff --git a/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java b/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java index dc9376195387a5da851a9aab37a67707bc470e1c..6afc50acecd28015f61e0c92502faffdaae28ad9 100644 --- a/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java +++ b/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java @@ -10,9 +10,9 @@ import java.util.Collection; */ public interface ConnectionRegistry { - void registerConnection(ContactId c, TransportId t); + void registerConnection(ContactId c, TransportId t, boolean incoming); - void unregisterConnection(ContactId c, TransportId t); + void unregisterConnection(ContactId c, TransportId t, boolean incoming); Collection<ContactId> getConnectedContacts(TransportId t); diff --git a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java index 3c9f5fb48ed3d792a8180ae93bd5e8cc1e04e0eb..c85c5f016d64c3af085454ca856d240943f0b93f 100644 --- a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java @@ -144,7 +144,7 @@ class ConnectionManagerImpl implements ConnectionManager { return; } ContactId contactId = ctx.getContactId(); - connectionRegistry.registerConnection(contactId, transportId); + connectionRegistry.registerConnection(contactId, transportId, true); try { // Create and run the incoming session createIncomingSession(ctx, reader).run(); @@ -153,7 +153,8 @@ class ConnectionManagerImpl implements ConnectionManager { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); disposeReader(true, true); } finally { - connectionRegistry.unregisterConnection(contactId, transportId); + connectionRegistry.unregisterConnection(contactId, transportId, + true); } } @@ -194,7 +195,8 @@ class ConnectionManagerImpl implements ConnectionManager { disposeWriter(true); return; } - connectionRegistry.registerConnection(contactId, transportId); + connectionRegistry.registerConnection(contactId, transportId, + false); try { // Create and run the outgoing session createSimplexOutgoingSession(ctx, writer).run(); @@ -203,7 +205,8 @@ class ConnectionManagerImpl implements ConnectionManager { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); disposeWriter(true); } finally { - connectionRegistry.unregisterConnection(contactId, transportId); + connectionRegistry.unregisterConnection(contactId, transportId, + false); } } @@ -254,7 +257,7 @@ class ConnectionManagerImpl implements ConnectionManager { return; } contactId = ctx.getContactId(); - connectionRegistry.registerConnection(contactId, transportId); + connectionRegistry.registerConnection(contactId, transportId, true); // Start the outgoing session on another thread ioExecutor.execute(new Runnable() { public void run() { @@ -270,7 +273,8 @@ class ConnectionManagerImpl implements ConnectionManager { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); disposeReader(true, true); } finally { - connectionRegistry.unregisterConnection(contactId, transportId); + connectionRegistry.unregisterConnection(contactId, transportId, + true); } } @@ -398,7 +402,8 @@ class ConnectionManagerImpl implements ConnectionManager { disposeReader(true, true); return; } - connectionRegistry.registerConnection(contactId, transportId); + connectionRegistry.registerConnection(contactId, transportId, + false); try { // Create and run the incoming session incomingSession = createIncomingSession(ctx, reader); @@ -408,7 +413,8 @@ class ConnectionManagerImpl implements ConnectionManager { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); disposeReader(true, true); } finally { - connectionRegistry.unregisterConnection(contactId, transportId); + connectionRegistry.unregisterConnection(contactId, transportId, + false); } } diff --git a/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java b/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java index 0fe9c3b4b4cae11e0f407d61498acd62a89b9ff9..c7f726a985c9086ebbf11d90d1efd15e8b99df93 100644 --- a/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java +++ b/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java @@ -2,6 +2,8 @@ package org.briarproject.plugins; import org.briarproject.api.TransportId; import org.briarproject.api.contact.ContactId; +import org.briarproject.api.event.ConnectionClosedEvent; +import org.briarproject.api.event.ConnectionOpenedEvent; import org.briarproject.api.event.ContactConnectedEvent; import org.briarproject.api.event.ContactDisconnectedEvent; import org.briarproject.api.event.EventBus; @@ -40,8 +42,12 @@ class ConnectionRegistryImpl implements ConnectionRegistry { contactCounts = new HashMap<ContactId, Integer>(); } - public void registerConnection(ContactId c, TransportId t) { - if (LOG.isLoggable(INFO)) LOG.info("Connection registered: " + t); + 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; lock.lock(); try { @@ -63,14 +69,19 @@ class ConnectionRegistryImpl implements ConnectionRegistry { } finally { lock.unlock(); } + eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming)); if (firstConnection) { LOG.info("Contact connected"); eventBus.broadcast(new ContactConnectedEvent(c)); } } - public void unregisterConnection(ContactId c, TransportId t) { - if (LOG.isLoggable(INFO)) LOG.info("Connection unregistered: " + t); + 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; lock.lock(); try { @@ -94,6 +105,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry { } finally { lock.unlock(); } + eventBus.broadcast(new ConnectionClosedEvent(c, t, incoming)); if (lastConnection) { LOG.info("Contact disconnected"); eventBus.broadcast(new ContactDisconnectedEvent(c)); diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java index 4eb8da75b93907cb3e5ef1a03fbea07f9e74d21c..78c3cd707006a1b9f2afb1d5eba4f689892e09f6 100644 --- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java @@ -3,6 +3,7 @@ package org.briarproject.plugins; import org.briarproject.api.TransportId; import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; +import org.briarproject.api.event.ConnectionClosedEvent; import org.briarproject.api.event.ContactStatusChangedEvent; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; @@ -141,10 +142,12 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { } } + @Override public Plugin getPlugin(TransportId t) { return plugins.get(t); } + @Override public Collection<DuplexPlugin> getInvitationPlugins() { List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>(); for (DuplexPlugin d : duplexPlugins) @@ -152,6 +155,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { return Collections.unmodifiableList(supported); } + @Override public Collection<DuplexPlugin> getKeyAgreementPlugins() { List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>(); for (DuplexPlugin d : duplexPlugins) @@ -163,7 +167,16 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { public void eventOccurred(Event e) { if (e instanceof ContactStatusChangedEvent) { ContactStatusChangedEvent c = (ContactStatusChangedEvent) e; - if (c.isActive()) connectToContact(c.getContactId()); + if (c.isActive()) { + // Connect to the newly activated contact + connectToContact(c.getContactId()); + } + } else if (e instanceof ConnectionClosedEvent) { + ConnectionClosedEvent c = (ConnectionClosedEvent) e; + if (!c.isIncoming()) { + // Connect to the disconnected contact + connectToContact(c.getContactId(), c.getTransportId()); + } } } @@ -174,8 +187,17 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { if (d.shouldPoll()) connectToContact(c, d); } + private void connectToContact(ContactId c, TransportId t) { + Plugin p = plugins.get(t); + if (p instanceof SimplexPlugin && p.shouldPoll()) + connectToContact(c, (SimplexPlugin) p); + else if (p instanceof DuplexPlugin && p.shouldPoll()) + connectToContact(c, (DuplexPlugin) p); + } + private void connectToContact(final ContactId c, final SimplexPlugin p) { ioExecutor.execute(new Runnable() { + @Override public void run() { TransportId t = p.getId(); if (!connectionRegistry.isConnected(c, t)) { @@ -189,6 +211,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { private void connectToContact(final ContactId c, final DuplexPlugin p) { ioExecutor.execute(new Runnable() { + @Override public void run() { TransportId t = p.getId(); if (!connectionRegistry.isConnected(c, t)) { @@ -211,6 +234,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { this.latch = latch; } + @Override public void run() { try { TransportId id = factory.getId(); @@ -230,7 +254,6 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { if (started) { plugins.put(id, plugin); simplexPlugins.add(plugin); - if (plugin.shouldPoll()) poller.addPlugin(plugin); if (LOG.isLoggable(INFO)) { String name = plugin.getClass().getSimpleName(); LOG.info("Starting " + name + " took " + @@ -263,6 +286,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { this.latch = latch; } + @Override public void run() { try { TransportId id = factory.getId(); @@ -282,7 +306,6 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { if (started) { plugins.put(id, plugin); duplexPlugins.add(plugin); - if (plugin.shouldPoll()) poller.addPlugin(plugin); if (LOG.isLoggable(INFO)) { String name = plugin.getClass().getSimpleName(); LOG.info("Starting " + name + " took " + @@ -314,6 +337,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { this.latch = latch; } + @Override public void run() { try { long start = System.currentTimeMillis(); @@ -339,6 +363,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { this.id = id; } + @Override public Settings getSettings() { try { return settingsManager.getSettings(id.getString()); @@ -348,6 +373,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { } } + @Override public TransportProperties getLocalProperties() { try { TransportProperties p = @@ -359,6 +385,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { } } + @Override public Map<ContactId, TransportProperties> getRemoteProperties() { try { return transportPropertyManager.getRemoteProperties(id); @@ -368,6 +395,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { } } + @Override public void mergeSettings(Settings s) { try { settingsManager.mergeSettings(s, id.getString()); @@ -376,6 +404,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { } } + @Override public void mergeLocalProperties(TransportProperties p) { try { transportPropertyManager.mergeLocalProperties(id, p); @@ -384,24 +413,29 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { } } + @Override public int showChoice(String[] options, String... message) { return uiCallback.showChoice(options, message); } + @Override public boolean showConfirmationMessage(String... message) { return uiCallback.showConfirmationMessage(message); } + @Override public void showMessage(String... message) { uiCallback.showMessage(message); } + @Override public void transportEnabled() { eventBus.broadcast(new TransportEnabledEvent(id)); Plugin p = plugins.get(id); if (p != null) poller.pollNow(p); } + @Override public void transportDisabled() { eventBus.broadcast(new TransportDisabledEvent(id)); } @@ -414,10 +448,12 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { super(id); } + @Override public void readerCreated(TransportConnectionReader r) { connectionManager.manageIncomingConnection(id, r); } + @Override public void writerCreated(ContactId c, TransportConnectionWriter w) { connectionManager.manageOutgoingConnection(c, id, w); } @@ -430,10 +466,12 @@ class PluginManagerImpl implements PluginManager, Service, EventListener { super(id); } + @Override public void incomingConnectionCreated(DuplexTransportConnection d) { connectionManager.manageIncomingConnection(id, d); } + @Override public void outgoingConnectionCreated(ContactId c, DuplexTransportConnection d) { connectionManager.manageOutgoingConnection(c, id, d); diff --git a/briar-core/src/org/briarproject/plugins/Poller.java b/briar-core/src/org/briarproject/plugins/Poller.java index 22de0148949ee7003dfec2ef87546579f603af48..2cacf1561b0bb8a513fd3f91140eeeb9c1367170 100644 --- a/briar-core/src/org/briarproject/plugins/Poller.java +++ b/briar-core/src/org/briarproject/plugins/Poller.java @@ -4,9 +4,6 @@ import org.briarproject.api.plugins.Plugin; interface Poller { - /** Adds the given plugin to the collection of plugins to be polled. */ - void addPlugin(Plugin p); - /** Tells the poller to poll the given plugin immediately. */ void pollNow(Plugin p); diff --git a/briar-core/src/org/briarproject/plugins/PollerImpl.java b/briar-core/src/org/briarproject/plugins/PollerImpl.java index 938997a7646d573188bd8716871c5196e10347b1..db8e1b0b8d8c85587e0696692e2890372e6c147a 100644 --- a/briar-core/src/org/briarproject/plugins/PollerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PollerImpl.java @@ -39,25 +39,17 @@ class PollerImpl implements Poller { tasks = new ConcurrentHashMap<TransportId, PollTask>(); } + @Override public void stop() { timer.cancel(); } - public void addPlugin(Plugin p) { - // Randomise first polling interval - if (p.shouldPoll()) - schedule(p, randomise(p.getPollingInterval()), false); - } - + @Override public void pollNow(Plugin p) { // Randomise next polling interval if (p.shouldPoll()) schedule(p, 0, true); } - private int randomise(int interval) { - return (int) (interval * random.nextDouble()); - } - private void schedule(Plugin p, int interval, boolean randomiseNext) { // Replace any previously scheduled task for this plugin PollTask task = new PollTask(p, randomiseNext); @@ -68,6 +60,7 @@ class PollerImpl implements Poller { private void poll(final Plugin p) { ioExecutor.execute(new Runnable() { + @Override public void run() { if (LOG.isLoggable(INFO)) LOG.info("Polling " + p.getClass().getSimpleName()); @@ -90,7 +83,8 @@ class PollerImpl implements Poller { public void run() { tasks.remove(plugin.getId()); int interval = plugin.getPollingInterval(); - if (randomiseNext) interval = randomise(interval); + if (randomiseNext) + interval = (int) (interval * random.nextDouble()); schedule(plugin, interval, false); poll(plugin); } diff --git a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java index 9b354427a240b81f05bb54aadade681ccdf3727b..541b8ad5ff4b9cc772f0d262cae36d23f6b8cd4e 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java +++ b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java @@ -13,8 +13,8 @@ public class LanTcpPluginFactory implements DuplexPluginFactory { 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 = 2 * 60 * 1000; // 2 minutes - private static final int MAX_POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour + 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 double BACKOFF_BASE = 1.2; private final Executor ioExecutor; @@ -26,14 +26,17 @@ public class LanTcpPluginFactory implements DuplexPluginFactory { this.backoffFactory = backoffFactory; } + @Override public TransportId getId() { return LanTcpPlugin.ID; } + @Override public int getMaxLatency() { return MAX_LATENCY; } + @Override public DuplexPlugin createPlugin(DuplexPluginCallback callback) { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); diff --git a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java index 44fdf5edfdf1ffe9b7b4c60c04678f6002dd6d4f..b5e2823ca7ab1e3959ff6e4952d847c08ddeaeb2 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java +++ b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java @@ -14,8 +14,8 @@ public class WanTcpPluginFactory implements DuplexPluginFactory { 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 = 2 * 60 * 1000; // 2 minutes - private static final int MAX_POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour + 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 double BACKOFF_BASE = 1.2; private final Executor ioExecutor; @@ -29,14 +29,17 @@ public class WanTcpPluginFactory implements DuplexPluginFactory { this.shutdownManager = shutdownManager; } + @Override public TransportId getId() { return WanTcpPlugin.ID; } + @Override public int getMaxLatency() { return MAX_LATENCY; } + @Override public DuplexPlugin createPlugin(DuplexPluginCallback callback) { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java index d4165bc410fbbe3ba5f6a1ff71e55a388bf8945d..1fd6648b3ba134ee6579a9476783c4fc12a357b0 100644 --- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java +++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java @@ -13,8 +13,8 @@ import java.util.concurrent.Executor; public class BluetoothPluginFactory implements DuplexPluginFactory { private static final int MAX_LATENCY = 30 * 1000; // 30 seconds - private static final int MIN_POLLING_INTERVAL = 2 * 60 * 1000; // 2 minutes - private static final int MAX_POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour + 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 double BACKOFF_BASE = 1.2; private final Executor ioExecutor; @@ -28,14 +28,17 @@ public class BluetoothPluginFactory implements DuplexPluginFactory { this.backoffFactory = backoffFactory; } + @Override public TransportId getId() { return BluetoothPlugin.ID; } + @Override public int getMaxLatency() { return MAX_LATENCY; } + @Override public DuplexPlugin createPlugin(DuplexPluginCallback callback) { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); diff --git a/briar-tests/src/org/briarproject/plugins/ConnectionRegistryImplTest.java b/briar-tests/src/org/briarproject/plugins/ConnectionRegistryImplTest.java index 8ea235249bca696de2b2edc2323c5d9ecad2a3cc..b18634e0aa7db117d6ce634cf4d0f411bb5370bd 100644 --- a/briar-tests/src/org/briarproject/plugins/ConnectionRegistryImplTest.java +++ b/briar-tests/src/org/briarproject/plugins/ConnectionRegistryImplTest.java @@ -3,6 +3,8 @@ package org.briarproject.plugins; import org.briarproject.BriarTestCase; import org.briarproject.api.TransportId; import org.briarproject.api.contact.ContactId; +import org.briarproject.api.event.ConnectionClosedEvent; +import org.briarproject.api.event.ConnectionOpenedEvent; import org.briarproject.api.event.ContactConnectedEvent; import org.briarproject.api.event.ContactDisconnectedEvent; import org.briarproject.api.event.EventBus; @@ -35,6 +37,10 @@ public class ConnectionRegistryImplTest extends BriarTestCase { Mockery context = new Mockery(); final EventBus eventBus = context.mock(EventBus.class); context.checking(new Expectations() {{ + exactly(5).of(eventBus).broadcast(with(any( + ConnectionOpenedEvent.class))); + exactly(2).of(eventBus).broadcast(with(any( + ConnectionClosedEvent.class))); exactly(3).of(eventBus).broadcast(with(any( ContactConnectedEvent.class))); oneOf(eventBus).broadcast(with(any( @@ -49,43 +55,46 @@ public class ConnectionRegistryImplTest extends BriarTestCase { assertEquals(Collections.emptyList(), c.getConnectedContacts(transportId1)); // Check that a registered connection shows up - this should - // broadcast a ContactConnectedEvent - c.registerConnection(contactId, transportId); + // broadcast a ConnectionOpenedEvent and a ContactConnectedEvent + c.registerConnection(contactId, transportId, true); assertEquals(Collections.singletonList(contactId), c.getConnectedContacts(transportId)); assertEquals(Collections.emptyList(), c.getConnectedContacts(transportId1)); - // Register an identical connection - lookup should be unaffected - c.registerConnection(contactId, transportId); + // Register an identical connection - this should broadcast a + // ConnectionOpenedEvent and lookup should be unaffected + c.registerConnection(contactId, transportId, true); assertEquals(Collections.singletonList(contactId), c.getConnectedContacts(transportId)); assertEquals(Collections.emptyList(), c.getConnectedContacts(transportId1)); - // Unregister one of the connections - lookup should be unaffected - c.unregisterConnection(contactId, transportId); + // Unregister one of the connections - this should broadcast a + // ConnectionClosedEvent and lookup should be unaffected + c.unregisterConnection(contactId, transportId, true); assertEquals(Collections.singletonList(contactId), c.getConnectedContacts(transportId)); assertEquals(Collections.emptyList(), c.getConnectedContacts(transportId1)); - // Unregister the other connection - lookup should be affected - - // this should broadcast a ContactDisconnectedEvent - c.unregisterConnection(contactId, transportId); + // Unregister the other connection - this should broadcast a + // ConnectionClosedEvent and a ContactDisconnectedEvent + c.unregisterConnection(contactId, transportId, true); assertEquals(Collections.emptyList(), c.getConnectedContacts(transportId)); assertEquals(Collections.emptyList(), c.getConnectedContacts(transportId1)); // Try to unregister the connection again - exception should be thrown try { - c.unregisterConnection(contactId, transportId); + c.unregisterConnection(contactId, transportId, true); fail(); } catch (IllegalArgumentException expected) { // Expected } // Register both contacts with one transport, one contact with both - - // this should broadcast two ContactConnectedEvents - c.registerConnection(contactId, transportId); - c.registerConnection(contactId1, transportId); - c.registerConnection(contactId1, transportId1); + // this should broadcast three ConnectionOpenedEvents and two + // ContactConnectedEvents + 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)); diff --git a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java index 6627225c3ddf3417e92b3a2316d4d52baab1e6f9..cd2400c7780c73d92a0fc36f7bf7ce45d9973c0f 100644 --- a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java +++ b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java @@ -84,9 +84,6 @@ public class PluginManagerImplTest extends BriarTestCase { will(returnValue(simplexPlugin)); // Created oneOf(simplexPlugin).start(); will(returnValue(true)); // Started - oneOf(simplexPlugin).shouldPoll(); - will(returnValue(true)); - oneOf(poller).addPlugin(simplexPlugin); // Second simplex plugin oneOf(simplexFailFactory).getId(); will(returnValue(simplexFailId)); @@ -105,8 +102,6 @@ public class PluginManagerImplTest extends BriarTestCase { will(returnValue(duplexPlugin)); // Created oneOf(duplexPlugin).start(); will(returnValue(true)); // Started - oneOf(duplexPlugin).shouldPoll(); - will(returnValue(false)); // Second duplex plugin oneOf(duplexFailFactory).getId(); will(returnValue(duplexFailId)); @@ -193,9 +188,6 @@ public class PluginManagerImplTest extends BriarTestCase { will(returnValue(simplexPlugin)); // Created oneOf(simplexPlugin).start(); will(returnValue(true)); // Started - oneOf(simplexPlugin).shouldPoll(); - will(returnValue(true)); // Should poll - oneOf(poller).addPlugin(simplexPlugin); // Second simplex plugin oneOf(simplexFactory1).getId(); will(returnValue(simplexId1)); @@ -204,8 +196,6 @@ public class PluginManagerImplTest extends BriarTestCase { will(returnValue(simplexPlugin1)); // Created oneOf(simplexPlugin1).start(); will(returnValue(true)); // Started - oneOf(simplexPlugin1).shouldPoll(); - will(returnValue(false)); // Should not poll // First duplex plugin oneOf(pluginConfig).getDuplexFactories(); will(returnValue(Arrays.asList(duplexFactory, duplexFactory1))); @@ -216,9 +206,6 @@ public class PluginManagerImplTest extends BriarTestCase { will(returnValue(duplexPlugin)); // Created oneOf(duplexPlugin).start(); will(returnValue(true)); // Started - oneOf(duplexPlugin).shouldPoll(); - will(returnValue(true)); // Should poll - oneOf(poller).addPlugin(duplexPlugin); // Second duplex plugin oneOf(duplexFactory1).getId(); will(returnValue(duplexId1)); @@ -227,8 +214,6 @@ public class PluginManagerImplTest extends BriarTestCase { will(returnValue(duplexPlugin1)); // Created oneOf(duplexPlugin1).start(); will(returnValue(true)); // Started - oneOf(duplexPlugin1).shouldPoll(); - will(returnValue(false)); // Should not poll // Start listening for events oneOf(eventBus).addListener(with(any(EventListener.class))); // eventOccurred()