From 6cc8463209de195e72d2aecfe9f3a580b4a419fb Mon Sep 17 00:00:00 2001 From: akwizgran <michael@briarproject.org> Date: Sat, 24 Nov 2012 13:35:23 +0000 Subject: [PATCH] First pass at a modem plugin (unfinished). --- src/net/sf/briar/plugins/modem/Modem.java | 2 +- .../sf/briar/plugins/modem/ModemFactory.java | 6 + .../briar/plugins/modem/ModemFactoryImpl.java | 16 ++ .../sf/briar/plugins/modem/ModemPlugin.java | 150 ++++++++++++++++-- .../plugins/modem/ModemPluginFactory.java | 4 +- 5 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 src/net/sf/briar/plugins/modem/ModemFactory.java create mode 100644 src/net/sf/briar/plugins/modem/ModemFactoryImpl.java diff --git a/src/net/sf/briar/plugins/modem/Modem.java b/src/net/sf/briar/plugins/modem/Modem.java index e758ef8b82..42c3693bf3 100644 --- a/src/net/sf/briar/plugins/modem/Modem.java +++ b/src/net/sf/briar/plugins/modem/Modem.java @@ -6,7 +6,7 @@ import java.io.OutputStream; /** * A modem that can be used for multiple sequential incoming and outgoing - * calls. If the modem or its input or output streams throw an exception it + * calls. If the modem or its input or output streams throw any exceptions they * cannot continue to be used. */ interface Modem { diff --git a/src/net/sf/briar/plugins/modem/ModemFactory.java b/src/net/sf/briar/plugins/modem/ModemFactory.java new file mode 100644 index 0000000000..36328c49c2 --- /dev/null +++ b/src/net/sf/briar/plugins/modem/ModemFactory.java @@ -0,0 +1,6 @@ +package net.sf.briar.plugins.modem; + +interface ModemFactory { + + Modem createModem(Modem.Callback callback, String portName); +} diff --git a/src/net/sf/briar/plugins/modem/ModemFactoryImpl.java b/src/net/sf/briar/plugins/modem/ModemFactoryImpl.java new file mode 100644 index 0000000000..1abf52b11c --- /dev/null +++ b/src/net/sf/briar/plugins/modem/ModemFactoryImpl.java @@ -0,0 +1,16 @@ +package net.sf.briar.plugins.modem; + +import java.util.concurrent.Executor; + +class ModemFactoryImpl implements ModemFactory { + + private final Executor executor; + + ModemFactoryImpl(Executor executor) { + this.executor = executor; + } + + public Modem createModem(Modem.Callback callback, String portName) { + return new ModemImpl(executor, callback, portName); + } +} diff --git a/src/net/sf/briar/plugins/modem/ModemPlugin.java b/src/net/sf/briar/plugins/modem/ModemPlugin.java index bb4065dbf2..e51dd7b035 100644 --- a/src/net/sf/briar/plugins/modem/ModemPlugin.java +++ b/src/net/sf/briar/plugins/modem/ModemPlugin.java @@ -1,20 +1,34 @@ package net.sf.briar.plugins.modem; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; + import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.Semaphore; import java.util.logging.Logger; +import jssc.SerialPortList; import net.sf.briar.api.ContactId; +import net.sf.briar.api.TransportProperties; import net.sf.briar.api.crypto.PseudoRandom; -import net.sf.briar.api.plugins.PluginCallback; import net.sf.briar.api.plugins.PluginExecutor; import net.sf.briar.api.plugins.duplex.DuplexPlugin; +import net.sf.briar.api.plugins.duplex.DuplexPluginCallback; import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.util.StringUtils; -class ModemPlugin implements DuplexPlugin { +class ModemPlugin implements DuplexPlugin, Modem.Callback { static final byte[] TRANSPORT_ID = StringUtils.fromHexString("8f573867bedf54884b5868ee5d902832" + @@ -26,14 +40,22 @@ class ModemPlugin implements DuplexPlugin { Logger.getLogger(ModemPlugin.class.getName()); private final Executor pluginExecutor; - private final PluginCallback callback; + private final ModemFactory modemFactory; + private final DuplexPluginCallback callback; private final long pollingInterval; + private final Semaphore polling; + + private volatile boolean running = false; + private volatile Modem modem = null; ModemPlugin(@PluginExecutor Executor pluginExecutor, - PluginCallback callback, long pollingInterval) { + ModemFactory modemFactory, DuplexPluginCallback callback, + long pollingInterval) { this.pluginExecutor = pluginExecutor; + this.modemFactory = modemFactory; this.callback = callback; this.pollingInterval = pollingInterval; + polling = new Semaphore(1); } public TransportId getId() { @@ -44,13 +66,22 @@ class ModemPlugin implements DuplexPlugin { return "MODEM_PLUGIN_NAME"; } - public boolean start() throws IOException { - // FIXME + public boolean start() { + for(String portName : SerialPortList.getPortNames()) { + modem = modemFactory.createModem(this, portName); + try { + modem.init(); + running = true; + return true; + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); + } + } return false; } - public void stop() throws IOException { - // FIXME + public void stop() { + running = false; } public boolean shouldPoll() { @@ -62,12 +93,68 @@ class ModemPlugin implements DuplexPlugin { } public void poll(Collection<ContactId> connected) { - // FIXME + if(!connected.isEmpty()) return; // One at a time please + pluginExecutor.execute(new Runnable() { + public void run() { + poll(); + } + }); + } + + private void poll() { + if(!running) return; + if(!polling.tryAcquire()) { + if(LOG.isLoggable(INFO)) + LOG.info("Previous poll still in progress"); + return; + } + // Call contacts one at a time in a random order + Map<ContactId, TransportProperties> remote = + callback.getRemoteProperties(); + List<ContactId> contacts = new ArrayList<ContactId>(remote.keySet()); + Collections.shuffle(contacts); + Iterator<ContactId> it = contacts.iterator(); + while(it.hasNext() && running) { + ContactId c = it.next(); + String number = remote.get(c).get("number"); + if(number == null) continue; + try { + if(!modem.dial(number)) continue; + } catch(IOException e) { + // FIXME: Race condition with stop() + running = false; + if(start()) continue; + else break; + } + ModemTransportConnection conn = new ModemTransportConnection(); + callback.outgoingConnectionCreated(c, conn); + try { + conn.waitForDisposal(); + } catch(InterruptedException e) { + if(LOG.isLoggable(WARNING)) + LOG.warning("Interrupted while polling"); + Thread.currentThread().interrupt(); + break; + } + } + polling.release(); } public DuplexTransportConnection createConnection(ContactId c) { - // FIXME - return null; + if(!running) return null; + final Map<ContactId, TransportProperties> remote = + callback.getRemoteProperties(); + String number = remote.get(c).get("number"); + if(number == null) return null; + try { + if(!modem.dial(number)) return null; + } catch(IOException e) { + // FIXME: Race condition with stop() + running = false; + start(); + return null; + } + return new ModemTransportConnection(); } public boolean supportsInvitations() { @@ -83,4 +170,45 @@ class ModemPlugin implements DuplexPlugin { long timeout) { throw new UnsupportedOperationException(); } + + public void incomingCallConnected() { + callback.incomingConnectionCreated(new ModemTransportConnection()); + } + + private class ModemTransportConnection + implements DuplexTransportConnection { + + private final CountDownLatch finished = new CountDownLatch(1); + + public InputStream getInputStream() { + return modem.getInputStream(); + } + + public OutputStream getOutputStream() { + return modem.getOutputStream(); + } + + public boolean shouldFlush() { + return true; + } + + public void dispose(boolean exception, boolean recognised) { + try { + modem.hangUp(); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); + exception = true; + } + if(exception) { + // FIXME: Race condition with stop() + running = false; + start(); + } + finished.countDown(); + } + + private void waitForDisposal() throws InterruptedException { + finished.await(); + } + } } diff --git a/src/net/sf/briar/plugins/modem/ModemPluginFactory.java b/src/net/sf/briar/plugins/modem/ModemPluginFactory.java index 3b7e81588b..558edcc393 100644 --- a/src/net/sf/briar/plugins/modem/ModemPluginFactory.java +++ b/src/net/sf/briar/plugins/modem/ModemPluginFactory.java @@ -28,6 +28,8 @@ public class ModemPluginFactory implements DuplexPluginFactory { // This plugin is not enabled by default String enabled = callback.getConfig().get("enabled"); if(StringUtils.isNullOrEmpty(enabled)) return null; - return new ModemPlugin(pluginExecutor, callback, POLLING_INTERVAL); + ModemFactory modemFactory = new ModemFactoryImpl(pluginExecutor); + return new ModemPlugin(pluginExecutor, modemFactory, callback, + POLLING_INTERVAL); } } -- GitLab