From 08b91d2483e9e3549d3d0810ee54e90051f93055 Mon Sep 17 00:00:00 2001 From: akwizgran <akwizgran@users.sourceforge.net> Date: Fri, 4 Apr 2014 22:04:05 +0100 Subject: [PATCH] Poll plugins when connectivity changes. Bug #66. This should enable us to connect to contacts faster at startup and whenever a new means of connecting becomes available. --- .../plugins/droidtooth/DroidtoothPlugin.java | 1 + .../briarproject/plugins/tor/TorPlugin.java | 75 +++++++------- .../api/plugins/PluginCallback.java | 3 + .../plugins/PluginManagerImpl.java | 5 + .../src/org/briarproject/plugins/Poller.java | 7 +- .../org/briarproject/plugins/PollerImpl.java | 99 +++++-------------- .../briarproject/plugins/tcp/TcpPlugin.java | 1 + .../plugins/bluetooth/BluetoothPlugin.java | 52 +++++----- .../plugins/DuplexClientTest.java | 2 + .../plugins/DuplexServerTest.java | 2 + .../plugins/tcp/LanTcpPluginTest.java | 2 + 11 files changed, 113 insertions(+), 136 deletions(-) diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java index 97762a53ff..70b6a134c1 100644 --- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java +++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java @@ -172,6 +172,7 @@ class DroidtoothPlugin implements DuplexPlugin { } LOG.info("Socket bound"); socket = ss; + callback.pollNow(); acceptContactConnections(); } }); diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index 116fad3a09..9093a9a5cf 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Scanner; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.zip.ZipInputStream; @@ -80,6 +81,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { private final long maxLatency, pollingInterval; private final File torDirectory, torFile, geoIpFile, configFile, doneFile; private final File cookieFile, pidFile, hostnameFile; + private final AtomicBoolean firstCircuit; private volatile boolean running = false, networkEnabled = false; private volatile Process tor = null; @@ -109,6 +111,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { cookieFile = new File(torDirectory, ".tor/control_auth_cookie"); pidFile = new File(torDirectory, ".tor/pid"); hostnameFile = new File(torDirectory, "hostname"); + firstCircuit = new AtomicBoolean(true); } public TransportId getId() { @@ -216,11 +219,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION); appContext.registerReceiver(networkStateReceiver, filter); // Bind a server socket to receive incoming hidden service connections - pluginExecutor.execute(new Runnable() { - public void run() { - bind(); - } - }); + bind(); return true; } @@ -428,38 +427,43 @@ class TorPlugin implements DuplexPlugin, EventHandler { } private void bind() { - // If there's already a port number stored in config, reuse it - String portString = callback.getConfig().get("port"); - int port; - if(StringUtils.isNullOrEmpty(portString)) port = 0; - else port = Integer.parseInt(portString); - // Bind a server socket to receive connections from the Tor process - ServerSocket ss = null; - try { - ss = new ServerSocket(); - ss.bind(new InetSocketAddress("127.0.0.1", port)); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - tryToClose(ss); - } - if(!running) { - tryToClose(ss); - return; - } - socket = ss; - // Store the port number - final String localPort = String.valueOf(ss.getLocalPort()); - TransportConfig c = new TransportConfig(); - c.put("port", localPort); - callback.mergeConfig(c); - // Create a hidden service if necessary pluginExecutor.execute(new Runnable() { public void run() { - publishHiddenService(localPort); + // If there's already a port number stored in config, reuse it + String portString = callback.getConfig().get("port"); + int port; + if(StringUtils.isNullOrEmpty(portString)) port = 0; + else port = Integer.parseInt(portString); + // Bind a server socket to receive connections from Tor + ServerSocket ss = null; + try { + ss = new ServerSocket(); + ss.bind(new InetSocketAddress("127.0.0.1", port)); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + tryToClose(ss); + } + if(!running) { + tryToClose(ss); + return; + } + socket = ss; + // Store the port number + final String localPort = String.valueOf(ss.getLocalPort()); + TransportConfig c = new TransportConfig(); + c.put("port", localPort); + callback.mergeConfig(c); + // Create a hidden service if necessary + pluginExecutor.execute(new Runnable() { + public void run() { + publishHiddenService(localPort); + } + }); + // Accept incoming hidden service connections from Tor + acceptContactConnections(ss); } }); - // Accept incoming hidden service connections from the Tor process - acceptContactConnections(ss); } private void tryToClose(ServerSocket ss) { @@ -534,6 +538,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { private void enableNetwork(boolean enable) throws IOException { if(!running) return; if(LOG.isLoggable(INFO)) LOG.info("Enabling network: " + enable); + if(!enable) firstCircuit.set(true); controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); networkEnabled = enable; } @@ -629,6 +634,10 @@ class TorPlugin implements DuplexPlugin, EventHandler { if(!"EXTENDED".equals(status)) LOG.info("Circuit " + id + " " + status); } + if("BUILT".equals(status) && firstCircuit.getAndSet(false)) { + LOG.info("First circuit built"); + callback.pollNow(); + } } public void streamStatus(String status, String id, String target) { diff --git a/briar-api/src/org/briarproject/api/plugins/PluginCallback.java b/briar-api/src/org/briarproject/api/plugins/PluginCallback.java index e65408c172..3a5890b2d2 100644 --- a/briar-api/src/org/briarproject/api/plugins/PluginCallback.java +++ b/briar-api/src/org/briarproject/api/plugins/PluginCallback.java @@ -49,4 +49,7 @@ public interface PluginCallback { * format string and arguments. */ void showMessage(String... message); + + /** Schedules the plugin to be polled immediately. */ + void pollNow(); } diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java index 214d310a72..3cdfbe9a9c 100644 --- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java @@ -363,6 +363,11 @@ class PluginManagerImpl implements PluginManager { public void showMessage(String... message) { uiCallback.showMessage(message); } + + public void pollNow() { + Plugin p = plugins.get(id); + if(p != null) poller.pollNow(p); + } } private class SimplexCallback extends PluginCallbackImpl diff --git a/briar-core/src/org/briarproject/plugins/Poller.java b/briar-core/src/org/briarproject/plugins/Poller.java index 6a0d1bb3b6..ae57caebae 100644 --- a/briar-core/src/org/briarproject/plugins/Poller.java +++ b/briar-core/src/org/briarproject/plugins/Poller.java @@ -6,9 +6,12 @@ import org.briarproject.api.plugins.Plugin; interface Poller { - /** Starts a new thread to poll the given collection of plugins. */ + /** Starts a poller for the given collection of plugins. */ void start(Collection<Plugin> plugins); - /** Tells the poller thread to exit. */ + /** Stops the poller. */ void stop(); + + /** 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 170689a52e..6d331877b4 100644 --- a/briar-core/src/org/briarproject/plugins/PollerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PollerImpl.java @@ -3,125 +3,72 @@ package org.briarproject.plugins; import static java.util.logging.Level.INFO; import java.util.Collection; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.TimerTask; import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.inject.Inject; -import org.briarproject.api.ContactId; import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.PluginExecutor; -import org.briarproject.api.system.Clock; +import org.briarproject.api.system.Timer; import org.briarproject.api.transport.ConnectionRegistry; -class PollerImpl implements Poller, Runnable { +class PollerImpl implements Poller { private static final Logger LOG = Logger.getLogger(PollerImpl.class.getName()); private final Executor pluginExecutor; private final ConnectionRegistry connRegistry; - private final Clock clock; - private final SortedSet<PollTime> pollTimes; + private final Timer timer; @Inject PollerImpl(@PluginExecutor Executor pluginExecutor, - ConnectionRegistry connRegistry, Clock clock) { + ConnectionRegistry connRegistry, Timer timer) { this.pluginExecutor = pluginExecutor; this.connRegistry = connRegistry; - this.clock = clock; - pollTimes = new TreeSet<PollTime>(); + this.timer = timer; } - public synchronized void start(Collection<Plugin> plugins) { + public void start(Collection<Plugin> plugins) { for(Plugin plugin : plugins) schedule(plugin, true); - new Thread(this, "Poller").start(); } - private synchronized void schedule(Plugin plugin, boolean randomise) { + private void schedule(Plugin plugin, boolean randomise) { if(plugin.shouldPoll()) { - long now = clock.currentTimeMillis(); long interval = plugin.getPollingInterval(); // Randomise intervals at startup to spread out connection attempts if(randomise) interval = (long) (interval * Math.random()); - pollTimes.add(new PollTime(now + interval, plugin)); + timer.schedule(new PollTask(plugin), interval); } } - public synchronized void stop() { - pollTimes.clear(); - notifyAll(); + public void stop() { + timer.cancel(); } - public void run() { - while(true) { - synchronized(this) { - if(pollTimes.isEmpty()) { - LOG.info("Finished polling"); - return; - } - long now = clock.currentTimeMillis(); - final PollTime p = pollTimes.first(); - if(now >= p.time) { - boolean removed = pollTimes.remove(p); - assert removed; - final Collection<ContactId> connected = - connRegistry.getConnectedContacts(p.plugin.getId()); - if(LOG.isLoggable(INFO)) { - String name = p.plugin.getClass().getSimpleName(); - LOG.info("Polling " + name); - } - pluginExecutor.execute(new Runnable() { - public void run() { - p.plugin.poll(connected); - } - }); - schedule(p.plugin, false); - } else { - try { - wait(p.time - now); - } catch(InterruptedException e) { - LOG.warning("Interrupted while waiting to poll"); - Thread.currentThread().interrupt(); - return; - } - } + public void pollNow(final Plugin p) { + pluginExecutor.execute(new Runnable() { + public void run() { + if(LOG.isLoggable(INFO)) + LOG.info("Polling " + p.getClass().getSimpleName()); + p.poll(connRegistry.getConnectedContacts(p.getId())); } - } + }); } - private static class PollTime implements Comparable<PollTime> { + private class PollTask extends TimerTask { - private final long time; private final Plugin plugin; - private PollTime(long time, Plugin plugin) { - this.time = time; + private PollTask(Plugin plugin) { this.plugin = plugin; } - // Must be consistent with equals() - public int compareTo(PollTime p) { - if(time < p.time) return -1; - if(time > p.time) return 1; - return 0; - } - - // Must be consistent with equals() - @Override - public int hashCode() { - return (int) (time ^ (time >>> 32)) ^ plugin.hashCode(); - } - - @Override - public boolean equals(Object o) { - if(o instanceof PollTime) { - PollTime p = (PollTime) o; - return time == p.time && plugin == p.plugin; - } - return false; + public void run() { + pollNow(plugin); + schedule(plugin, false); } } } diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java index 13d913ce84..ce47e0c706 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java @@ -98,6 +98,7 @@ abstract class TcpPlugin implements DuplexPlugin { SocketAddress local = ss.getLocalSocketAddress(); setLocalSocketAddress((InetSocketAddress) local); if(LOG.isLoggable(INFO)) LOG.info("Listening on " + local); + callback.pollNow(); acceptContactConnections(); } }); diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java index 38c96f893e..ec2258c4ca 100644 --- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java @@ -91,35 +91,37 @@ class BluetoothPlugin implements DuplexPlugin { if(LOG.isLoggable(INFO)) LOG.info("Local address " + localDevice.getBluetoothAddress()); running = true; - pluginExecutor.execute(new Runnable() { - public void run() { - bind(); - } - }); + bind(); return true; } private void bind() { - if(!running) return; - // Advertise the Bluetooth address to contacts - TransportProperties p = new TransportProperties(); - p.put("address", localDevice.getBluetoothAddress()); - callback.mergeLocalProperties(p); - // Bind a server socket to accept connections from contacts - String url = makeUrl("localhost", getUuid()); - StreamConnectionNotifier ss; - try { - ss = (StreamConnectionNotifier) Connector.open(url); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - return; - } - if(!running) { - tryToClose(ss); - return; - } - socket = ss; - acceptContactConnections(ss); + pluginExecutor.execute(new Runnable() { + public void run() { + if(!running) return; + // Advertise the Bluetooth address to contacts + TransportProperties p = new TransportProperties(); + p.put("address", localDevice.getBluetoothAddress()); + callback.mergeLocalProperties(p); + // Bind a server socket to accept connections from contacts + String url = makeUrl("localhost", getUuid()); + StreamConnectionNotifier ss; + try { + ss = (StreamConnectionNotifier) Connector.open(url); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + return; + } + if(!running) { + tryToClose(ss); + return; + } + socket = ss; + callback.pollNow(); + acceptContactConnections(ss); + } + }); } private String makeUrl(String address, String uuid) { diff --git a/briar-tests/src/org/briarproject/plugins/DuplexClientTest.java b/briar-tests/src/org/briarproject/plugins/DuplexClientTest.java index 2b22110bb6..7b47ecef74 100644 --- a/briar-tests/src/org/briarproject/plugins/DuplexClientTest.java +++ b/briar-tests/src/org/briarproject/plugins/DuplexClientTest.java @@ -100,6 +100,8 @@ public abstract class DuplexClientTest extends DuplexTest { public void showMessage(String... message) {} + public void pollNow() {} + public void incomingConnectionCreated(DuplexTransportConnection d) {} public void outgoingConnectionCreated(ContactId contactId, diff --git a/briar-tests/src/org/briarproject/plugins/DuplexServerTest.java b/briar-tests/src/org/briarproject/plugins/DuplexServerTest.java index 805d3a755d..49fdad0885 100644 --- a/briar-tests/src/org/briarproject/plugins/DuplexServerTest.java +++ b/briar-tests/src/org/briarproject/plugins/DuplexServerTest.java @@ -99,6 +99,8 @@ public abstract class DuplexServerTest extends DuplexTest { public void showMessage(String... message) {} + public void pollNow() {} + public void incomingConnectionCreated(DuplexTransportConnection d) { System.out.println("Connection received"); sendChallengeReceiveResponse(d); diff --git a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java index afa1dde1a2..268de98441 100644 --- a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java +++ b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java @@ -133,6 +133,8 @@ public class LanTcpPluginTest extends BriarTestCase { public void showMessage(String... message) {} + public void pollNow() {} + public void incomingConnectionCreated(DuplexTransportConnection d) { connectionsLatch.countDown(); } -- GitLab