Remove RemovableDrivePlugin, refactor plugin interface.

This commit is contained in:
akwizgran
2018-05-24 13:15:38 +01:00
parent b2ac210586
commit 3181b695df
52 changed files with 250 additions and 1710 deletions

View File

@@ -50,7 +50,6 @@ import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -334,7 +333,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return zin; return zin;
} }
private InputStream getConfigInputStream() throws IOException { private InputStream getConfigInputStream() {
int resId = getResourceId("torrc"); int resId = getResourceId("torrc");
return appContext.getResources().openRawResource(resId); return appContext.getResources().openRawResource(resId);
} }
@@ -499,7 +498,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public void stop() throws PluginException { public void stop() {
running = false; running = false;
tryToClose(socket); tryToClose(socket);
if (networkStateReceiver != null) if (networkStateReceiver != null)
@@ -533,20 +532,16 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public void poll(Collection<ContactId> connected) { public void poll(Map<ContactId, TransportProperties> contacts) {
if (!isRunning()) return; if (!isRunning()) return;
backoff.increment(); backoff.increment();
Map<ContactId, TransportProperties> remote = for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
callback.getRemoteProperties(); connectAndCallBack(e.getKey(), e.getValue());
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
} }
} }
private void connectAndCallBack(ContactId c, TransportProperties p) { private void connectAndCallBack(ContactId c, TransportProperties p) {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
@@ -556,13 +551,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning()) return null; if (!isRunning()) return null;
return createConnection(callback.getRemoteProperties(c));
}
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
String onion = p.get(PROP_ONION); String onion = p.get(PROP_ONION);
if (StringUtils.isNullOrEmpty(onion)) return null; if (StringUtils.isNullOrEmpty(onion)) return null;
if (!ONION.matcher(onion).matches()) { if (!ONION.matcher(onion).matches()) {

View File

@@ -0,0 +1,6 @@
package org.briarproject.bramble.api.plugin;
public interface FileConstants {
String PROP_PATH = "path";
}

View File

@@ -2,8 +2,9 @@ package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.util.Collection; import java.util.Map;
@NotNullByDefault @NotNullByDefault
public interface Plugin { public interface Plugin {
@@ -39,21 +40,19 @@ public interface Plugin {
boolean isRunning(); boolean isRunning();
/** /**
* Returns true if the plugin's {@link #poll(Collection)} method should be * Returns true if the plugin should be polled periodically to attempt to
* called periodically to attempt to establish connections. * establish connections.
*/ */
boolean shouldPoll(); boolean shouldPoll();
/** /**
* Returns the desired interval in milliseconds between calls to the * Returns the desired interval in milliseconds between polling attempts.
* plugin's {@link #poll(Collection)} method.
*/ */
int getPollingInterval(); int getPollingInterval();
/** /**
* Attempts to establish connections to contacts, passing any created * Attempts to establish connections to the given contacts, passing any
* connections to the callback. To avoid creating redundant connections, * created connections to the callback.
* the plugin may exclude the given contacts from polling.
*/ */
void poll(Collection<ContactId> connected); void poll(Map<ContactId, TransportProperties> contacts);
} }

View File

@@ -1,12 +1,9 @@
package org.briarproject.bramble.api.plugin; package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import java.util.Map;
/** /**
* An interface through which a transport plugin interacts with the rest of * An interface through which a transport plugin interacts with the rest of
* the application. * the application.
@@ -25,17 +22,7 @@ public interface PluginCallback {
TransportProperties getLocalProperties(); TransportProperties getLocalProperties();
/** /**
* Returns the plugin's remote transport properties. * Merges the given settings with the plugin's settings
*/
Map<ContactId, TransportProperties> getRemoteProperties();
/**
* Returns the plugin's remote transport properties for the given contact.
*/
TransportProperties getRemoteProperties(ContactId c);
/**
* Merges the given settings with the namespaced settings
*/ */
void mergeSettings(Settings s); void mergeSettings(Settings s);
@@ -45,34 +32,12 @@ public interface PluginCallback {
void mergeLocalProperties(TransportProperties p); void mergeLocalProperties(TransportProperties p);
/** /**
* Presents the user with a choice among two or more named options and * Signals that the transport is enabled.
* returns the user's response. The message may consist of a translatable
* format string and arguments.
*
* @return an index into the array of options indicating the user's choice,
* or -1 if the user cancelled the choice.
*/
int showChoice(String[] options, String... message);
/**
* Asks the user to confirm an action and returns the user's response. The
* message may consist of a translatable format string and arguments.
*/
boolean showConfirmationMessage(String... message);
/**
* Shows a message to the user. The message may consist of a translatable
* format string and arguments.
*/
void showMessage(String... message);
/**
* Signal that the transport got enabled.
*/ */
void transportEnabled(); void transportEnabled();
/** /**
* Signal that the transport got disabled. * Signals that the transport is disabled.
*/ */
void transportDisabled(); void transportDisabled();
} }

View File

@@ -22,11 +22,6 @@ public interface TransportConnectionWriter {
*/ */
int getMaxIdleTime(); int getMaxIdleTime();
/**
* Returns the capacity of the transport connection in bytes.
*/
long getCapacity();
/** /**
* Returns an output stream for writing to the transport connection. * Returns an output stream for writing to the transport connection.
*/ */

View File

@@ -71,11 +71,6 @@ public abstract class AbstractDuplexTransportConnection
return plugin.getMaxIdleTime(); return plugin.getMaxIdleTime();
} }
@Override
public long getCapacity() {
return Long.MAX_VALUE;
}
@Override @Override
public OutputStream getOutputStream() throws IOException { public OutputStream getOutputStream() throws IOException {
return AbstractDuplexTransportConnection.this.getOutputStream(); return AbstractDuplexTransportConnection.this.getOutputStream();

View File

@@ -1,10 +1,10 @@
package org.briarproject.bramble.api.plugin.duplex; package org.briarproject.bramble.api.plugin.duplex;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -15,12 +15,11 @@ import javax.annotation.Nullable;
public interface DuplexPlugin extends Plugin { public interface DuplexPlugin extends Plugin {
/** /**
* Attempts to create and return a connection to the given contact using * Attempts to create and return a connection using the given transport
* the current transport and configuration properties. Returns null if a * properties. Returns null if a connection cannot be created.
* connection cannot be created.
*/ */
@Nullable @Nullable
DuplexTransportConnection createConnection(ContactId c); DuplexTransportConnection createConnection(TransportProperties p);
/** /**
* Returns true if the plugin supports short-range key agreement. * Returns true if the plugin supports short-range key agreement.

View File

@@ -5,7 +5,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
/** /**
* An interface for handling connections created by a duplex transport plugin. * An interface through which a duplex plugin interacts with the rest of the
* application.
*/ */
@NotNullByDefault @NotNullByDefault
public interface DuplexPluginCallback extends PluginCallback { public interface DuplexPluginCallback extends PluginCallback {

View File

@@ -1,10 +1,10 @@
package org.briarproject.bramble.api.plugin.simplex; package org.briarproject.bramble.api.plugin.simplex;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.properties.TransportProperties;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -15,18 +15,16 @@ import javax.annotation.Nullable;
public interface SimplexPlugin extends Plugin { public interface SimplexPlugin extends Plugin {
/** /**
* Attempts to create and return a reader for the given contact using the * Attempts to create and return a reader for the given transport
* current transport and configuration properties. Returns null if a reader * properties. Returns null if a reader cannot be created.
* cannot be created.
*/ */
@Nullable @Nullable
TransportConnectionReader createReader(ContactId c); TransportConnectionReader createReader(TransportProperties p);
/** /**
* Attempts to create and return a writer for the given contact using the * Attempts to create and return a writer for the given transport
* current transport and configuration properties. Returns null if a writer * properties. Returns null if a writer cannot be created.
* cannot be created.
*/ */
@Nullable @Nullable
TransportConnectionWriter createWriter(ContactId c); TransportConnectionWriter createWriter(TransportProperties p);
} }

View File

@@ -7,8 +7,8 @@ import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
/** /**
* An interface for handling readers and writers created by a simplex transport * An interface through which a simplex plugin interacts with the rest of the
* plugin. * application.
*/ */
@NotNullByDefault @NotNullByDefault
public interface SimplexPluginCallback extends PluginCallback { public interface SimplexPluginCallback extends PluginCallback {

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.api.ui;
public interface UiCallback {
/**
* Presents the user with a choice among two or more named options and
* returns the user's response. The message may consist of a translatable
* format string and arguments.
*
* @return an index into the array of options indicating the user's choice,
* or -1 if the user cancelled the choice.
*/
int showChoice(String[] options, String... message);
/**
* Asks the user to confirm an action and returns the user's response. The
* message may consist of a translatable format string and arguments.
*/
boolean showConfirmationMessage(String... message);
/**
* Shows a message to the user. The message may consist of a translatable
* format string and arguments.
*/
void showMessage(String... message);
}

View File

@@ -22,19 +22,6 @@ public class OsUtils {
return os != null && os.contains("Mac OS"); return os != null && os.contains("Mac OS");
} }
public static boolean isMacLeopardOrNewer() {
if (!isMac() || version == null) return false;
try {
String[] v = version.split("\\.");
if (v.length != 3) return false;
int major = Integer.parseInt(v[0]);
int minor = Integer.parseInt(v[1]);
return major >= 10 && minor >= 5;
} catch (NumberFormatException e) {
return false;
}
}
public static boolean isLinux() { public static boolean isLinux() {
return os != null && os.contains("Linux") && !isAndroid(); return os != null && os.contains("Linux") && !isAndroid();
} }

View File

@@ -32,12 +32,10 @@ import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
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;
import org.briarproject.bramble.api.ui.UiCallback;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -71,7 +69,6 @@ class PluginManagerImpl implements PluginManager, Service {
private final TransportPropertyManager transportPropertyManager; private final TransportPropertyManager transportPropertyManager;
private final SecureRandom random; private final SecureRandom random;
private final Clock clock; private final Clock clock;
private final UiCallback uiCallback;
private final Map<TransportId, Plugin> plugins; private final Map<TransportId, Plugin> plugins;
private final List<SimplexPlugin> simplexPlugins; private final List<SimplexPlugin> simplexPlugins;
private final List<DuplexPlugin> duplexPlugins; private final List<DuplexPlugin> duplexPlugins;
@@ -85,8 +82,7 @@ class PluginManagerImpl implements PluginManager, Service {
ConnectionRegistry connectionRegistry, ConnectionRegistry connectionRegistry,
SettingsManager settingsManager, SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock, SecureRandom random, Clock clock) {
UiCallback uiCallback) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler; this.scheduler = scheduler;
this.eventBus = eventBus; this.eventBus = eventBus;
@@ -97,7 +93,6 @@ class PluginManagerImpl implements PluginManager, Service {
this.transportPropertyManager = transportPropertyManager; this.transportPropertyManager = transportPropertyManager;
this.random = random; this.random = random;
this.clock = clock; this.clock = clock;
this.uiCallback = uiCallback;
plugins = new ConcurrentHashMap<>(); plugins = new ConcurrentHashMap<>();
simplexPlugins = new CopyOnWriteArrayList<>(); simplexPlugins = new CopyOnWriteArrayList<>();
duplexPlugins = new CopyOnWriteArrayList<>(); duplexPlugins = new CopyOnWriteArrayList<>();
@@ -106,13 +101,14 @@ class PluginManagerImpl implements PluginManager, Service {
} }
@Override @Override
public void startService() throws ServiceException { public void startService() {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
// Instantiate the poller // Instantiate the poller
if (pluginConfig.shouldPoll()) { if (pluginConfig.shouldPoll()) {
LOG.info("Starting poller"); LOG.info("Starting poller");
Poller poller = new Poller(ioExecutor, scheduler, connectionManager, Poller poller = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, this, random, clock); connectionRegistry, this, transportPropertyManager, random,
clock);
eventBus.addListener(poller); eventBus.addListener(poller);
} }
// Instantiate the simplex plugins and start them asynchronously // Instantiate the simplex plugins and start them asynchronously
@@ -297,26 +293,6 @@ class PluginManagerImpl implements PluginManager, Service {
} }
} }
@Override
public Map<ContactId, TransportProperties> getRemoteProperties() {
try {
return transportPropertyManager.getRemoteProperties(id);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return Collections.emptyMap();
}
}
@Override
public TransportProperties getRemoteProperties(ContactId c) {
try {
return transportPropertyManager.getRemoteProperties(c, id);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return new TransportProperties();
}
}
@Override @Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
try { try {
@@ -335,21 +311,6 @@ class PluginManagerImpl implements PluginManager, Service {
} }
} }
@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 @Override
public void transportEnabled() { public void transportEnabled() {
eventBus.broadcast(new TransportEnabledEvent(id)); eventBus.broadcast(new TransportEnabledEvent(id));

View File

@@ -1,18 +1,10 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent; import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
@@ -19,10 +20,13 @@ import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
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;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -33,10 +37,10 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.concurrent.TimeUnit.MILLISECONDS; 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;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
@@ -49,6 +53,7 @@ class Poller implements EventListener {
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry; private final ConnectionRegistry connectionRegistry;
private final PluginManager pluginManager; private final PluginManager pluginManager;
private final TransportPropertyManager transportPropertyManager;
private final SecureRandom random; private final SecureRandom random;
private final Clock clock; private final Clock clock;
private final Lock lock; private final Lock lock;
@@ -58,12 +63,14 @@ class Poller implements EventListener {
@Scheduler ScheduledExecutorService scheduler, @Scheduler ScheduledExecutorService scheduler,
ConnectionManager connectionManager, ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager, ConnectionRegistry connectionRegistry, PluginManager pluginManager,
TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock) { SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler; this.scheduler = scheduler;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry; this.connectionRegistry = connectionRegistry;
this.pluginManager = pluginManager; this.pluginManager = pluginManager;
this.transportPropertyManager = transportPropertyManager;
this.random = random; this.random = random;
this.clock = clock; this.clock = clock;
lock = new ReentrantLock(); lock = new ReentrantLock();
@@ -119,10 +126,15 @@ class Poller implements EventListener {
private void connectToContact(ContactId c, SimplexPlugin p) { private void connectToContact(ContactId c, SimplexPlugin p) {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
TransportId t = p.getId(); TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) { if (connectionRegistry.isConnected(c, t)) return;
TransportConnectionWriter w = p.createWriter(c); try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
TransportConnectionWriter w = p.createWriter(props);
if (w != null) if (w != null)
connectionManager.manageOutgoingConnection(c, t, w); connectionManager.manageOutgoingConnection(c, t, w);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
}); });
} }
@@ -130,10 +142,15 @@ class Poller implements EventListener {
private void connectToContact(ContactId c, DuplexPlugin p) { private void connectToContact(ContactId c, DuplexPlugin p) {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
TransportId t = p.getId(); TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) { if (connectionRegistry.isConnected(c, t)) return;
DuplexTransportConnection d = p.createConnection(c); try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
DuplexTransportConnection d = p.createConnection(props);
if (d != null) if (d != null)
connectionManager.manageOutgoingConnection(c, t, d); connectionManager.manageOutgoingConnection(c, t, d);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
}); });
} }
@@ -185,7 +202,17 @@ class Poller implements EventListener {
private void poll(Plugin p) { private void poll(Plugin p) {
TransportId t = p.getId(); TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t); if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t);
p.poll(connectionRegistry.getConnectedContacts(t)); try {
Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(t);
Collection<ContactId> connected =
connectionRegistry.getConnectedContacts(t);
remote = new HashMap<>(remote);
remote.keySet().removeAll(connected);
if (!remote.isEmpty()) p.poll(remote);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
} }
private class ScheduledPollTask { private class ScheduledPollTask {

View File

@@ -26,7 +26,6 @@ import org.briarproject.bramble.util.StringUtils;
import java.io.IOException; import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.UUID; import java.util.UUID;
@@ -250,19 +249,16 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
} }
@Override @Override
public void poll(Collection<ContactId> connected) { public void poll(Map<ContactId, TransportProperties> contacts) {
if (!isRunning() || !shouldAllowContactConnections()) return; if (!isRunning() || !shouldAllowContactConnections()) return;
backoff.increment(); backoff.increment();
// Try to connect to known devices in parallel // Try to connect to known devices in parallel
Map<ContactId, TransportProperties> remote = for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (connected.contains(c)) continue;
String address = e.getValue().get(PROP_ADDRESS); String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue; if (StringUtils.isNullOrEmpty(address)) continue;
String uuid = e.getValue().get(PROP_UUID); String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue; if (StringUtils.isNullOrEmpty(uuid)) continue;
ContactId c = e.getKey();
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
if (!isRunning() || !shouldAllowContactConnections()) return; if (!isRunning() || !shouldAllowContactConnections()) return;
if (!connectionLimiter.canOpenContactConnection()) return; if (!connectionLimiter.canOpenContactConnection()) return;
@@ -308,10 +304,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
} }
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning() || !shouldAllowContactConnections()) return null; if (!isRunning() || !shouldAllowContactConnections()) return null;
if (!connectionLimiter.canOpenContactConnection()) return null; if (!connectionLimiter.canOpenContactConnection()) return null;
TransportProperties p = callback.getRemoteProperties(c);
String address = p.get(PROP_ADDRESS); String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null; if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);

View File

@@ -1,27 +1,21 @@
package org.briarproject.bramble.plugin.file; package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.transport.TransportConstants.MIN_STREAM_LENGTH; import static org.briarproject.bramble.api.plugin.FileConstants.PROP_PATH;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@NotNullByDefault @NotNullByDefault
abstract class FilePlugin implements SimplexPlugin { abstract class FilePlugin implements SimplexPlugin {
@@ -29,25 +23,15 @@ abstract class FilePlugin implements SimplexPlugin {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(FilePlugin.class.getName()); Logger.getLogger(FilePlugin.class.getName());
protected final Executor ioExecutor;
protected final SimplexPluginCallback callback; protected final SimplexPluginCallback callback;
protected final int maxLatency; protected final int maxLatency;
protected final AtomicBoolean used = new AtomicBoolean(false);
protected volatile boolean running = false; protected abstract void writerFinished(File f, boolean exception);
@Nullable protected abstract void readerFinished(File f, boolean exception,
protected abstract File chooseOutputDirectory(); boolean recognised);
protected abstract Collection<File> findFilesByName(String filename); FilePlugin(SimplexPluginCallback callback, int maxLatency) {
protected abstract void writerFinished(File f);
protected abstract void readerFinished(File f);
protected FilePlugin(Executor ioExecutor, SimplexPluginCallback callback,
int maxLatency) {
this.ioExecutor = ioExecutor;
this.callback = callback; this.callback = callback;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
} }
@@ -58,81 +42,36 @@ abstract class FilePlugin implements SimplexPlugin {
} }
@Override @Override
public int getMaxIdleTime() { public TransportConnectionReader createReader(TransportProperties p) {
return Integer.MAX_VALUE; // We don't need keepalives if (!isRunning()) return null;
} String path = p.get(PROP_PATH);
if (isNullOrEmpty(path)) return null;
@Override
public boolean isRunning() {
return running;
}
@Override
public TransportConnectionReader createReader(ContactId c) {
return null;
}
@Override
public TransportConnectionWriter createWriter(ContactId c) {
if (!running) return null;
return createWriter(createConnectionFilename());
}
private String createConnectionFilename() {
StringBuilder s = new StringBuilder(12);
for (int i = 0; i < 8; i++) s.append((char) ('a' + Math.random() * 26));
s.append(".dat");
return s.toString();
}
// Package access for testing
boolean isPossibleConnectionFilename(String filename) {
return filename.toLowerCase(Locale.US).matches("[a-z]{8}\\.dat");
}
@Nullable
private TransportConnectionWriter createWriter(String filename) {
if (!running) return null;
File dir = chooseOutputDirectory();
if (dir == null || !dir.exists() || !dir.isDirectory()) return null;
File f = new File(dir, filename);
try { try {
long capacity = dir.getFreeSpace(); File file = new File(path);
if (capacity < MIN_STREAM_LENGTH) return null; FileInputStream in = new FileInputStream(file);
OutputStream out = new FileOutputStream(f); return new FileTransportReader(file, in, this);
return new FileTransportWriter(f, out, capacity, this);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
f.delete();
return null; return null;
} }
} }
protected void createReaderFromFile(File f) { @Override
if (!running) return; public TransportConnectionWriter createWriter(TransportProperties p) {
ioExecutor.execute(new ReaderCreator(f)); if (!isRunning()) return null;
} String path = p.get(PROP_PATH);
if (isNullOrEmpty(path)) return null;
private class ReaderCreator implements Runnable { try {
File file = new File(path);
private final File file; if (!file.exists() && !file.createNewFile()) {
LOG.info("Failed to create file");
private ReaderCreator(File file) { return null;
this.file = file;
}
@Override
public void run() {
if (isPossibleConnectionFilename(file.getName())) {
try {
FileInputStream in = new FileInputStream(file);
callback.readerCreated(new FileTransportReader(file, in,
FilePlugin.this));
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
} }
FileOutputStream out = new FileOutputStream(file);
return new FileTransportWriter(file, out, this);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} }
} }
} }

View File

@@ -38,9 +38,6 @@ class FileTransportReader implements TransportConnectionReader {
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
if (recognised) { plugin.readerFinished(file, exception, recognised);
file.delete();
plugin.readerFinished(file);
}
} }
} }

View File

@@ -18,14 +18,11 @@ class FileTransportWriter implements TransportConnectionWriter {
private final File file; private final File file;
private final OutputStream out; private final OutputStream out;
private final long capacity;
private final FilePlugin plugin; private final FilePlugin plugin;
FileTransportWriter(File file, OutputStream out, long capacity, FileTransportWriter(File file, OutputStream out, FilePlugin plugin) {
FilePlugin plugin) {
this.file = file; this.file = file;
this.out = out; this.out = out;
this.capacity = capacity;
this.plugin = plugin; this.plugin = plugin;
} }
@@ -39,11 +36,6 @@ class FileTransportWriter implements TransportConnectionWriter {
return plugin.getMaxIdleTime(); return plugin.getMaxIdleTime();
} }
@Override
public long getCapacity() {
return capacity;
}
@Override @Override
public OutputStream getOutputStream() { public OutputStream getOutputStream() {
return out; return out;
@@ -56,7 +48,6 @@ class FileTransportWriter implements TransportConnectionWriter {
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
if (exception) file.delete(); plugin.writerFinished(file, exception);
else plugin.writerFinished(file);
} }
} }

View File

@@ -207,20 +207,16 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
@Override @Override
public void poll(Collection<ContactId> connected) { public void poll(Map<ContactId, TransportProperties> contacts) {
if (!isRunning()) return; if (!isRunning()) return;
backoff.increment(); backoff.increment();
Map<ContactId, TransportProperties> remote = for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
callback.getRemoteProperties(); connectAndCallBack(e.getKey(), e.getValue());
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
} }
} }
private void connectAndCallBack(ContactId c, TransportProperties p) { private void connectAndCallBack(ContactId c, TransportProperties p) {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
@@ -230,13 +226,8 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning()) return null; if (!isRunning()) return null;
return createConnection(callback.getRemoteProperties(c));
}
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
for (InetSocketAddress remote : getRemoteSocketAddresses(p)) { for (InetSocketAddress remote : getRemoteSocketAddresses(p)) {
if (!isConnectable(remote)) { if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {

View File

@@ -15,7 +15,6 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.ui.UiCallback;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
@@ -26,9 +25,7 @@ import java.security.SecureRandom;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -40,19 +37,20 @@ public class PluginManagerImplTest extends BrambleTestCase {
setThreadingPolicy(new Synchroniser()); setThreadingPolicy(new Synchroniser());
}}; }};
Executor ioExecutor = Executors.newSingleThreadExecutor(); Executor ioExecutor = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduler = context.mock(ScheduledExecutorService.class); ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
SecureRandom random = new SecureRandom(); SecureRandom random = new SecureRandom();
Clock clock = context.mock(Clock.class); Clock clock = context.mock(Clock.class);
EventBus eventBus = context.mock(EventBus.class); EventBus eventBus = context.mock(EventBus.class);
PluginConfig pluginConfig = context.mock(PluginConfig.class); PluginConfig pluginConfig = context.mock(PluginConfig.class);
ConnectionManager connectionManager = ConnectionManager connectionManager =
context.mock(ConnectionManager.class); context.mock(ConnectionManager.class);
ConnectionRegistry connectionRegistry = context.mock(ConnectionRegistry.class); ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
SettingsManager settingsManager = SettingsManager settingsManager =
context.mock(SettingsManager.class); context.mock(SettingsManager.class);
TransportPropertyManager transportPropertyManager = TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class); context.mock(TransportPropertyManager.class);
UiCallback uiCallback = context.mock(UiCallback.class);
// Two simplex plugin factories: both create plugins, one fails to start // Two simplex plugin factories: both create plugins, one fails to start
SimplexPluginFactory simplexFactory = SimplexPluginFactory simplexFactory =
@@ -124,9 +122,9 @@ public class PluginManagerImplTest extends BrambleTestCase {
oneOf(duplexPlugin).stop(); oneOf(duplexPlugin).stop();
}}); }});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, scheduler, eventBus, PluginManagerImpl p = new PluginManagerImpl(ioExecutor, scheduler,
pluginConfig, connectionManager, connectionRegistry, settingsManager, eventBus, pluginConfig, connectionManager, connectionRegistry,
transportPropertyManager, random, clock, uiCallback); settingsManager, transportPropertyManager, random, clock);
// Two plugins should be started and stopped // Two plugins should be started and stopped
p.startService(); p.startService();

View File

@@ -15,6 +15,8 @@ import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
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.ImmediateExecutor; import org.briarproject.bramble.test.ImmediateExecutor;
@@ -24,13 +26,15 @@ import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test; import org.junit.Test;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -44,6 +48,8 @@ public class PollerTest extends BrambleMockTestCase {
context.mock(ConnectionRegistry.class); context.mock(ConnectionRegistry.class);
private final PluginManager pluginManager = private final PluginManager pluginManager =
context.mock(PluginManager.class); context.mock(PluginManager.class);
private final TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class);
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final ScheduledFuture future = context.mock(ScheduledFuture.class); private final ScheduledFuture future = context.mock(ScheduledFuture.class);
private final SecureRandom random; private final SecureRandom random;
@@ -51,6 +57,7 @@ public class PollerTest extends BrambleMockTestCase {
private final Executor ioExecutor = new ImmediateExecutor(); private final Executor ioExecutor = new ImmediateExecutor();
private final TransportId transportId = getTransportId(); private final TransportId transportId = getTransportId();
private final ContactId contactId = new ContactId(234); private final ContactId contactId = new ContactId(234);
private final TransportProperties properties = new TransportProperties();
private final int pollingInterval = 60 * 1000; private final int pollingInterval = 60 * 1000;
private final long now = System.currentTimeMillis(); private final long now = System.currentTimeMillis();
@@ -66,8 +73,8 @@ public class PollerTest extends BrambleMockTestCase {
SimplexPlugin simplexPlugin1 = SimplexPlugin simplexPlugin1 =
context.mock(SimplexPlugin.class, "simplexPlugin1"); context.mock(SimplexPlugin.class, "simplexPlugin1");
TransportId simplexId1 = getTransportId(); TransportId simplexId1 = getTransportId();
List<SimplexPlugin> simplexPlugins = Arrays.asList(simplexPlugin, List<SimplexPlugin> simplexPlugins =
simplexPlugin1); asList(simplexPlugin, simplexPlugin1);
TransportConnectionWriter simplexWriter = TransportConnectionWriter simplexWriter =
context.mock(TransportConnectionWriter.class); context.mock(TransportConnectionWriter.class);
@@ -76,8 +83,8 @@ public class PollerTest extends BrambleMockTestCase {
TransportId duplexId = getTransportId(); TransportId duplexId = getTransportId();
DuplexPlugin duplexPlugin1 = DuplexPlugin duplexPlugin1 =
context.mock(DuplexPlugin.class, "duplexPlugin1"); context.mock(DuplexPlugin.class, "duplexPlugin1");
List<DuplexPlugin> duplexPlugins = Arrays.asList(duplexPlugin, List<DuplexPlugin> duplexPlugins =
duplexPlugin1); asList(duplexPlugin, duplexPlugin1);
DuplexTransportConnection duplexConnection = DuplexTransportConnection duplexConnection =
context.mock(DuplexTransportConnection.class); context.mock(DuplexTransportConnection.class);
@@ -96,8 +103,12 @@ public class PollerTest extends BrambleMockTestCase {
will(returnValue(simplexId1)); will(returnValue(simplexId1));
oneOf(connectionRegistry).isConnected(contactId, simplexId1); oneOf(connectionRegistry).isConnected(contactId, simplexId1);
will(returnValue(false)); will(returnValue(false));
// Get the transport properties
oneOf(transportPropertyManager).getRemoteProperties(contactId,
simplexId1);
will(returnValue(properties));
// Connect to the contact // Connect to the contact
oneOf(simplexPlugin1).createWriter(contactId); oneOf(simplexPlugin1).createWriter(properties);
will(returnValue(simplexWriter)); will(returnValue(simplexWriter));
// Pass the connection to the connection manager // Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId, oneOf(connectionManager).manageOutgoingConnection(contactId,
@@ -105,7 +116,7 @@ public class PollerTest extends BrambleMockTestCase {
// Get the duplex plugins // Get the duplex plugins
oneOf(pluginManager).getDuplexPlugins(); oneOf(pluginManager).getDuplexPlugins();
will(returnValue(duplexPlugins)); will(returnValue(duplexPlugins));
// The first plugin supports polling // The duplex plugin supports polling
oneOf(duplexPlugin).shouldPoll(); oneOf(duplexPlugin).shouldPoll();
will(returnValue(true)); will(returnValue(true));
// Check whether the contact is already connected // Check whether the contact is already connected
@@ -113,8 +124,12 @@ public class PollerTest extends BrambleMockTestCase {
will(returnValue(duplexId)); will(returnValue(duplexId));
oneOf(connectionRegistry).isConnected(contactId, duplexId); oneOf(connectionRegistry).isConnected(contactId, duplexId);
will(returnValue(false)); will(returnValue(false));
// Get the transport properties
oneOf(transportPropertyManager).getRemoteProperties(contactId,
duplexId);
will(returnValue(properties));
// Connect to the contact // Connect to the contact
oneOf(duplexPlugin).createConnection(contactId); oneOf(duplexPlugin).createConnection(properties);
will(returnValue(duplexConnection)); will(returnValue(duplexConnection));
// Pass the connection to the connection manager // Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId, oneOf(connectionManager).manageOutgoingConnection(contactId,
@@ -125,7 +140,8 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ContactStatusChangedEvent(contactId, true)); p.eventOccurred(new ContactStatusChangedEvent(contactId, true));
} }
@@ -165,8 +181,12 @@ public class PollerTest extends BrambleMockTestCase {
// Check whether the contact is already connected // Check whether the contact is already connected
oneOf(connectionRegistry).isConnected(contactId, transportId); oneOf(connectionRegistry).isConnected(contactId, transportId);
will(returnValue(false)); will(returnValue(false));
// Get the transport properties
oneOf(transportPropertyManager).getRemoteProperties(contactId,
transportId);
will(returnValue(properties));
// Connect to the contact // Connect to the contact
oneOf(plugin).createConnection(contactId); oneOf(plugin).createConnection(properties);
will(returnValue(duplexConnection)); will(returnValue(duplexConnection));
// Pass the connection to the connection manager // Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId, oneOf(connectionManager).manageOutgoingConnection(contactId,
@@ -174,15 +194,15 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionClosedEvent(contactId, transportId, p.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
false)); false));
} }
@Test @Test
public void testRescheduleOnConnectionOpened() throws Exception { public void testRescheduleOnConnectionOpened() {
Plugin plugin = context.mock(Plugin.class); Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -205,14 +225,15 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
} }
@Test @Test
public void testRescheduleDoesNotReplaceEarlierTask() throws Exception { public void testRescheduleDoesNotReplaceEarlierTask() {
Plugin plugin = context.mock(Plugin.class); Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -248,7 +269,8 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
@@ -257,7 +279,7 @@ public class PollerTest extends BrambleMockTestCase {
} }
@Test @Test
public void testRescheduleReplacesLaterTask() throws Exception { public void testRescheduleReplacesLaterTask() {
Plugin plugin = context.mock(Plugin.class); Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -296,7 +318,8 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
@@ -306,8 +329,7 @@ public class PollerTest extends BrambleMockTestCase {
@Test @Test
public void testPollsOnTransportEnabled() throws Exception { public void testPollsOnTransportEnabled() throws Exception {
Plugin plugin = context.mock(Plugin.class); DuplexPlugin plugin = context.mock(DuplexPlugin.class);
List<ContactId> connected = Collections.singletonList(contactId);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(plugin).getId(); allowing(plugin).getId();
@@ -335,20 +357,69 @@ public class PollerTest extends BrambleMockTestCase {
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) (pollingInterval * 0.5)), with(MILLISECONDS)); with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
will(returnValue(future)); will(returnValue(future));
// Poll the plugin // Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties)));
oneOf(connectionRegistry).getConnectedContacts(transportId); oneOf(connectionRegistry).getConnectedContacts(transportId);
will(returnValue(connected)); will(returnValue(emptyList()));
oneOf(plugin).poll(connected); // Poll the plugin
oneOf(plugin).poll(singletonMap(contactId, properties));
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId)); p.eventOccurred(new TransportEnabledEvent(transportId));
} }
@Test @Test
public void testCancelsPollingOnTransportDisabled() throws Exception { public void testDoesNotPollIfAllContactsAreConnected() throws Exception {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule a polling task immediately
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L),
with(MILLISECONDS));
will(returnValue(future));
will(new RunAction());
// Running the polling task schedules the next polling task
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(random).nextDouble();
will(returnValue(0.5));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
will(returnValue(future));
// Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties)));
oneOf(connectionRegistry).getConnectedContacts(transportId);
will(returnValue(singletonList(contactId)));
// All contacts are connected, so don't poll the plugin
}});
Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId));
}
@Test
public void testCancelsPollingOnTransportDisabled() {
Plugin plugin = context.mock(Plugin.class); Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -371,7 +442,8 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId)); p.eventOccurred(new TransportEnabledEvent(transportId));
p.eventOccurred(new TransportDisabledEvent(transportId)); p.eventOccurred(new TransportDisabledEvent(transportId));

View File

@@ -23,8 +23,6 @@ import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@@ -40,7 +38,6 @@ import static org.junit.Assert.assertTrue;
public class LanTcpPluginTest extends BrambleTestCase { public class LanTcpPluginTest extends BrambleTestCase {
private final ContactId contactId = new ContactId(234);
private final Backoff backoff = new TestBackoff(); private final Backoff backoff = new TestBackoff();
@Test @Test
@@ -160,12 +157,10 @@ public class LanTcpPluginTest extends BrambleTestCase {
error.set(true); error.set(true);
} }
}).start(); }).start();
// Tell the plugin about the port // Connect to the port
TransportProperties p = new TransportProperties(); TransportProperties p = new TransportProperties();
p.put("ipPorts", addrString + ":" + port); p.put("ipPorts", addrString + ":" + port);
callback.remote.put(contactId, p); DuplexTransportConnection d = plugin.createConnection(p);
// Connect to the port
DuplexTransportConnection d = plugin.createConnection(contactId);
assertNotNull(d); assertNotNull(d);
// Check that the connection was accepted // Check that the connection was accepted
assertTrue(latch.await(5, SECONDS)); assertTrue(latch.await(5, SECONDS));
@@ -281,7 +276,7 @@ public class LanTcpPluginTest extends BrambleTestCase {
} }
@Test @Test
public void testComparatorPrefersNonZeroPorts() throws Exception { public void testComparatorPrefersNonZeroPorts() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator(); Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress nonZero = new InetSocketAddress("1.2.3.4", 1234); InetSocketAddress nonZero = new InetSocketAddress("1.2.3.4", 1234);
InetSocketAddress zero = new InetSocketAddress("1.2.3.4", 0); InetSocketAddress zero = new InetSocketAddress("1.2.3.4", 0);
@@ -294,7 +289,7 @@ public class LanTcpPluginTest extends BrambleTestCase {
} }
@Test @Test
public void testComparatorPrefersLongerPrefixes() throws Exception { public void testComparatorPrefersLongerPrefixes() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator(); Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0); InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0);
InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0); InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0);
@@ -314,7 +309,7 @@ public class LanTcpPluginTest extends BrambleTestCase {
} }
@Test @Test
public void testComparatorPrefersSiteLocalToLinkLocal() throws Exception { public void testComparatorPrefersSiteLocalToLinkLocal() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator(); Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0); InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0);
InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0); InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0);
@@ -345,8 +340,6 @@ public class LanTcpPluginTest extends BrambleTestCase {
@NotNullByDefault @NotNullByDefault
private static class Callback implements DuplexPluginCallback { private static class Callback implements DuplexPluginCallback {
private final Map<ContactId, TransportProperties> remote =
new Hashtable<>();
private final CountDownLatch propertiesLatch = new CountDownLatch(1); private final CountDownLatch propertiesLatch = new CountDownLatch(1);
private final CountDownLatch connectionsLatch = new CountDownLatch(1); private final CountDownLatch connectionsLatch = new CountDownLatch(1);
private final TransportProperties local = new TransportProperties(); private final TransportProperties local = new TransportProperties();
@@ -361,16 +354,6 @@ public class LanTcpPluginTest extends BrambleTestCase {
return local; return local;
} }
@Override
public Map<ContactId, TransportProperties> getRemoteProperties() {
return remote;
}
@Override
public TransportProperties getRemoteProperties(ContactId c) {
return remote.get(c);
}
@Override @Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
} }
@@ -381,20 +364,6 @@ public class LanTcpPluginTest extends BrambleTestCase {
propertiesLatch.countDown(); propertiesLatch.countDown();
} }
@Override
public int showChoice(String[] options, String... message) {
return -1;
}
@Override
public boolean showConfirmationMessage(String... message) {
return false;
}
@Override
public void showMessage(String... message) {
}
@Override @Override
public void incomingConnectionCreated(DuplexTransportConnection d) { public void incomingConnectionCreated(DuplexTransportConnection d) {
connectionsLatch.countDown(); connectionsLatch.countDown();

View File

@@ -20,7 +20,7 @@ public class CaptureArgumentAction<T> implements Action {
} }
@Override @Override
public Object invoke(Invocation invocation) throws Throwable { public Object invoke(Invocation invocation) {
captured.set(capturedClass.cast(invocation.getParameter(index))); captured.set(capturedClass.cast(invocation.getParameter(index)));
return null; return null;
} }

View File

@@ -9,13 +9,14 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@Module @Module
@@ -51,12 +52,12 @@ public class TestPluginConfigModule {
@Override @Override
public Collection<DuplexPluginFactory> getDuplexFactories() { public Collection<DuplexPluginFactory> getDuplexFactories() {
return Collections.emptyList(); return emptyList();
} }
@Override @Override
public Collection<SimplexPluginFactory> getSimplexFactories() { public Collection<SimplexPluginFactory> getSimplexFactories() {
return Collections.singletonList(simplex); return singletonList(simplex);
} }
@Override @Override

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -10,20 +10,20 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory; import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory;
import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory; import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory;
import org.briarproject.bramble.plugin.file.RemovableDrivePluginFactory;
import org.briarproject.bramble.plugin.modem.ModemPluginFactory; import org.briarproject.bramble.plugin.modem.ModemPluginFactory;
import org.briarproject.bramble.plugin.tcp.LanTcpPluginFactory; import org.briarproject.bramble.plugin.tcp.LanTcpPluginFactory;
import org.briarproject.bramble.plugin.tcp.WanTcpPluginFactory; import org.briarproject.bramble.plugin.tcp.WanTcpPluginFactory;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
@Module @Module
public class DesktopPluginModule extends PluginModule { public class DesktopPluginModule extends PluginModule {
@@ -41,12 +41,8 @@ public class DesktopPluginModule extends PluginModule {
backoffFactory); backoffFactory);
DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor, DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor,
backoffFactory, shutdownManager); backoffFactory, shutdownManager);
SimplexPluginFactory removable =
new RemovableDrivePluginFactory(ioExecutor);
Collection<SimplexPluginFactory> simplex =
Collections.singletonList(removable);
Collection<DuplexPluginFactory> duplex = Collection<DuplexPluginFactory> duplex =
Arrays.asList(bluetooth, modem, lan, wan); asList(bluetooth, modem, lan, wan);
@NotNullByDefault @NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() { PluginConfig pluginConfig = new PluginConfig() {
@@ -57,7 +53,7 @@ public class DesktopPluginModule extends PluginModule {
@Override @Override
public Collection<SimplexPluginFactory> getSimplexFactories() { public Collection<SimplexPluginFactory> getSimplexFactories() {
return simplex; return emptyList();
} }
@Override @Override

View File

@@ -1,28 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
@NotNullByDefault
class LinuxRemovableDriveFinder extends UnixRemovableDriveFinder {
@Override
protected String getMountCommand() {
return "/bin/mount";
}
@Override
@Nullable
protected String parseMountPoint(String line) {
// The format is "/dev/foo on /bar/baz type bam (opt1,opt2)"
String pattern = "^/dev/[^ ]+ on (.*) type [^ ]+ \\([^)]+\\)$";
String path = line.replaceFirst(pattern, "$1");
return path.equals(line) ? null : path;
}
@Override
protected boolean isRemovableDriveMountPoint(String path) {
return path.startsWith("/mnt/") || path.startsWith("/media/");
}
}

View File

@@ -1,12 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class LinuxRemovableDriveMonitor extends UnixRemovableDriveMonitor {
@Override
protected String[] getPathsToWatch() {
return new String[] {"/mnt", "/media"};
}
}

View File

@@ -1,28 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
@NotNullByDefault
class MacRemovableDriveFinder extends UnixRemovableDriveFinder {
@Override
protected String getMountCommand() {
return "/sbin/mount";
}
@Override
@Nullable
protected String parseMountPoint(String line) {
// The format is "/dev/foo on /bar/baz (opt1, opt2)"
String pattern = "^/dev/[^ ]+ on (.*) \\([^)]+\\)$";
String path = line.replaceFirst(pattern, "$1");
return path.equals(line) ? null : path;
}
@Override
protected boolean isRemovableDriveMountPoint(String path) {
return path.startsWith("/Volumes/");
}
}

View File

@@ -1,12 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class MacRemovableDriveMonitor extends UnixRemovableDriveMonitor {
@Override
protected String[] getPathsToWatch() {
return new String[] {"/Volumes"};
}
}

View File

@@ -1,84 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
private static final Logger LOG =
Logger.getLogger(PollingRemovableDriveMonitor.class.getName());
private final Executor ioExecutor;
private final RemovableDriveFinder finder;
private final int pollingInterval;
private final Lock pollingLock = new ReentrantLock();
private final Condition stopPolling = pollingLock.newCondition();
private volatile boolean running = false;
private volatile Callback callback = null;
PollingRemovableDriveMonitor(Executor ioExecutor,
RemovableDriveFinder finder, int pollingInterval) {
this.ioExecutor = ioExecutor;
this.finder = finder;
this.pollingInterval = pollingInterval;
}
@Override
public void start(Callback callback) throws IOException {
this.callback = callback;
running = true;
ioExecutor.execute(this);
}
@Override
public void stop() throws IOException {
running = false;
pollingLock.lock();
try {
stopPolling.signalAll();
} finally {
pollingLock.unlock();
}
}
@Override
public void run() {
try {
Collection<File> drives = finder.findRemovableDrives();
while (running) {
pollingLock.lock();
try {
stopPolling.await(pollingInterval, MILLISECONDS);
} finally {
pollingLock.unlock();
}
if (!running) return;
Collection<File> newDrives = finder.findRemovableDrives();
for (File f : newDrives) {
if (!drives.contains(f)) callback.driveInserted(f);
}
drives = newDrives;
}
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting to poll");
Thread.currentThread().interrupt();
} catch (IOException e) {
callback.exceptionThrown(e);
}
}
}

View File

@@ -1,13 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
@NotNullByDefault
interface RemovableDriveFinder {
Collection<File> findRemovableDrives() throws IOException;
}

View File

@@ -1,21 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
import java.io.IOException;
@NotNullByDefault
interface RemovableDriveMonitor {
void start(Callback c) throws IOException;
void stop() throws IOException;
interface Callback {
void driveInserted(File root);
void exceptionThrown(IOException e);
}
}

View File

@@ -1,140 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
@NotNullByDefault
class RemovableDrivePlugin extends FilePlugin
implements RemovableDriveMonitor.Callback {
static final TransportId ID =
new TransportId("org.briarproject.bramble.file");
private static final Logger LOG =
Logger.getLogger(RemovableDrivePlugin.class.getName());
private final RemovableDriveFinder finder;
private final RemovableDriveMonitor monitor;
RemovableDrivePlugin(Executor ioExecutor, SimplexPluginCallback callback,
RemovableDriveFinder finder, RemovableDriveMonitor monitor,
int maxLatency) {
super(ioExecutor, callback, maxLatency);
this.finder = finder;
this.monitor = monitor;
}
@Override
public TransportId getId() {
return ID;
}
@Override
public void start() throws PluginException {
if (used.getAndSet(true)) throw new IllegalStateException();
running = true;
try {
monitor.start(this);
} catch (IOException e) {
throw new PluginException(e);
}
}
@Override
public void stop() throws PluginException {
running = false;
try {
monitor.stop();
} catch (IOException e) {
throw new PluginException(e);
}
}
@Override
public boolean shouldPoll() {
return false;
}
@Override
public int getPollingInterval() {
throw new UnsupportedOperationException();
}
@Override
public void poll(Collection<ContactId> connected) {
throw new UnsupportedOperationException();
}
@Override
protected File chooseOutputDirectory() {
try {
List<File> drives = new ArrayList<>(finder.findRemovableDrives());
if (drives.isEmpty()) return null;
String[] paths = new String[drives.size()];
for (int i = 0; i < paths.length; i++) {
paths[i] = drives.get(i).getPath();
}
int i = callback.showChoice(paths, "REMOVABLE_DRIVE_CHOOSE_DRIVE");
if (i == -1) return null;
return drives.get(i);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
}
@Override
protected void readerFinished(File f) {
callback.showMessage("REMOVABLE_DRIVE_READ_FINISHED");
}
@Override
protected void writerFinished(File f) {
callback.showMessage("REMOVABLE_DRIVE_WRITE_FINISHED");
}
@Override
protected Collection<File> findFilesByName(String filename) {
List<File> matches = new ArrayList<>();
try {
for (File drive : finder.findRemovableDrives()) {
File[] files = drive.listFiles();
if (files != null) {
for (File f : files) {
if (f.isFile() && filename.equals(f.getName()))
matches.add(f);
}
}
}
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
return matches;
}
@Override
public void driveInserted(File root) {
File[] files = root.listFiles();
if (files != null) {
for (File f : files) if (f.isFile()) createReaderFromFile(f);
}
}
@Override
public void exceptionThrown(IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}

View File

@@ -1,63 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.util.OsUtils;
import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class RemovableDrivePluginFactory implements SimplexPluginFactory {
// Maximum latency 14 days (Royal Mail or lackadaisical carrier pigeon)
private static final int MAX_LATENCY = 14 * 24 * 60 * 60 * 1000;
private static final int POLLING_INTERVAL = 10 * 1000; // 10 seconds
private final Executor ioExecutor;
public RemovableDrivePluginFactory(Executor ioExecutor) {
this.ioExecutor = ioExecutor;
}
@Override
public TransportId getId() {
return RemovableDrivePlugin.ID;
}
@Override
public int getMaxLatency() {
return MAX_LATENCY;
}
@Override
public SimplexPlugin createPlugin(SimplexPluginCallback callback) {
RemovableDriveFinder finder;
RemovableDriveMonitor monitor;
if (OsUtils.isLinux()) {
finder = new LinuxRemovableDriveFinder();
monitor = new LinuxRemovableDriveMonitor();
} else if (OsUtils.isMacLeopardOrNewer()) {
finder = new MacRemovableDriveFinder();
monitor = new MacRemovableDriveMonitor();
} else if (OsUtils.isMac()) {
// JNotify requires OS X 10.5 or newer, so we have to poll
finder = new MacRemovableDriveFinder();
monitor = new PollingRemovableDriveMonitor(ioExecutor, finder,
POLLING_INTERVAL);
} else if (OsUtils.isWindows()) {
finder = new WindowsRemovableDriveFinder();
monitor = new PollingRemovableDriveMonitor(ioExecutor, finder,
POLLING_INTERVAL);
} else {
return null;
}
return new RemovableDrivePlugin(ioExecutor, callback, finder, monitor,
MAX_LATENCY);
}
}

View File

@@ -1,48 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import javax.annotation.Nullable;
@NotNullByDefault
abstract class UnixRemovableDriveFinder implements RemovableDriveFinder {
protected abstract String getMountCommand();
@Nullable
protected abstract String parseMountPoint(String line);
protected abstract boolean isRemovableDriveMountPoint(String path);
@Override
public List<File> findRemovableDrives() throws IOException {
List<File> drives = new ArrayList<>();
Process p = new ProcessBuilder(getMountCommand()).start();
Scanner s = new Scanner(p.getInputStream(), "UTF-8");
try {
while (s.hasNextLine()) {
String line = s.nextLine();
String[] tokens = line.split(" ");
if (tokens.length < 3) continue;
// The general format is "/dev/foo on /bar/baz ..."
if (tokens[0].startsWith("/dev/") && tokens[1].equals("on")) {
// The path may contain spaces so we can't use tokens[2]
String path = parseMountPoint(line);
if (path != null && isRemovableDriveMountPoint(path)) {
File f = new File(path);
if (f.exists() && f.isDirectory()) drives.add(f);
}
}
}
} finally {
s.close();
}
return drives;
}
}

View File

@@ -1,129 +0,0 @@
package org.briarproject.bramble.plugin.file;
import net.contentobjects.jnotify.JNotify;
import net.contentobjects.jnotify.JNotifyListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nullable;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
abstract class UnixRemovableDriveMonitor implements RemovableDriveMonitor,
JNotifyListener {
//TODO: rationalise this in a further refactor
private static final Lock staticLock = new ReentrantLock();
// The following are locking: staticLock
private static boolean triedLoad = false;
private static Throwable loadError = null;
private final Lock lock = new ReentrantLock();
// The following are locking: lock
private final List<Integer> watches = new ArrayList<>();
private boolean started = false;
private Callback callback = null;
protected abstract String[] getPathsToWatch();
@Nullable
private static Throwable tryLoad() {
try {
Class.forName("net.contentobjects.jnotify.JNotify");
return null;
} catch (UnsatisfiedLinkError | ClassNotFoundException e) {
return e;
}
}
private static void checkEnabled() throws IOException {
staticLock.lock();
try {
if (!triedLoad) {
loadError = tryLoad();
triedLoad = true;
}
if (loadError != null) throw new IOException(loadError.toString());
} finally {
staticLock.unlock();
}
}
@Override
public void start(Callback callback) throws IOException {
checkEnabled();
List<Integer> watches = new ArrayList<>();
int mask = JNotify.FILE_CREATED;
for (String path : getPathsToWatch()) {
if (new File(path).exists())
watches.add(JNotify.addWatch(path, mask, false, this));
}
lock.lock();
try {
if (started) throw new AssertionError();
if (this.callback != null) throw new AssertionError();
started = true;
this.callback = callback;
this.watches.addAll(watches);
} finally {
lock.unlock();
}
}
@Override
public void stop() throws IOException {
checkEnabled();
List<Integer> watches;
lock.lock();
try {
if (!started) throw new AssertionError();
if (callback == null) throw new AssertionError();
started = false;
callback = null;
watches = new ArrayList<>(this.watches);
this.watches.clear();
} finally {
lock.unlock();
}
for (Integer w : watches) JNotify.removeWatch(w);
}
@Override
public void fileCreated(int wd, String rootPath, String name) {
Callback callback;
lock.lock();
try {
callback = this.callback;
} finally {
lock.unlock();
}
if (callback != null)
callback.driveInserted(new File(rootPath + "/" + name));
}
@Override
public void fileDeleted(int wd, String rootPath, String name) {
throw new UnsupportedOperationException();
}
@Override
public void fileModified(int wd, String rootPath, String name) {
throw new UnsupportedOperationException();
}
@Override
public void fileRenamed(int wd, String rootPath, String oldName,
String newName) {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,34 +0,0 @@
package org.briarproject.bramble.plugin.file;
import com.sun.jna.platform.win32.Kernel32;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@NotNullByDefault
class WindowsRemovableDriveFinder implements RemovableDriveFinder {
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa364939.aspx
private static final int DRIVE_REMOVABLE = 2;
@Override
public Collection<File> findRemovableDrives() throws IOException {
File[] roots = File.listRoots();
if (roots == null) throw new IOException();
List<File> drives = new ArrayList<>();
for (File root : roots) {
try {
int type = Kernel32.INSTANCE.GetDriveType(root.getPath());
if (type == DRIVE_REMOVABLE) drives.add(root);
} catch (RuntimeException e) {
throw new IOException(e);
}
}
return drives;
}
}

View File

@@ -17,7 +17,7 @@ import org.briarproject.bramble.util.StringUtils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Collection; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -115,7 +115,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
} }
@Override @Override
public void poll(Collection<ContactId> connected) { public void poll(Map<ContactId, TransportProperties> contacts) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@@ -139,17 +139,16 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
} }
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (!running) return null; if (!running) return null;
// Get the ISO 3166 code for the caller's country // Get the ISO 3166 code for the caller's country
String fromIso = callback.getLocalProperties().get("iso3166"); String fromIso = callback.getLocalProperties().get("iso3166");
if (StringUtils.isNullOrEmpty(fromIso)) return null; if (StringUtils.isNullOrEmpty(fromIso)) return null;
// Get the ISO 3166 code for the callee's country // Get the ISO 3166 code for the callee's country
TransportProperties properties = callback.getRemoteProperties(c); String toIso = p.get("iso3166");
String toIso = properties.get("iso3166");
if (StringUtils.isNullOrEmpty(toIso)) return null; if (StringUtils.isNullOrEmpty(toIso)) return null;
// Get the callee's phone number // Get the callee's phone number
String number = properties.get("number"); String number = p.get("number");
if (StringUtils.isNullOrEmpty(number)) return null; if (StringUtils.isNullOrEmpty(number)) return null;
// Convert the number into direct dialling form // Convert the number into direct dialling form
number = CountryCodes.translate(number, fromIso, toIso); number = CountryCodes.translate(number, fromIso, toIso);

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class LinuxRemovableDriveFinderTest extends BrambleTestCase {
@Test
public void testParseMountPoint() {
LinuxRemovableDriveFinder f = new LinuxRemovableDriveFinder();
String line = "/dev/sda3 on / type ext3"
+ " (rw,errors=remount-ro,commit=0)";
assertEquals("/", f.parseMountPoint(line));
line = "gvfs-fuse-daemon on /home/alice/.gvfs"
+ " type fuse.gvfs-fuse-daemon (rw,nosuid,nodev,user=alice)";
assertEquals(null, f.parseMountPoint(line)); // Can't be parsed
line = "fusectl on /sys/fs/fuse/connections type fusectl (rw)";
assertEquals(null, f.parseMountPoint(line)); // Can't be parsed
line = "/dev/sdd1 on /media/HAZ SPACE(!) type vfat"
+ " (rw,nosuid,nodev,uhelper=udisks,uid=1000,gid=1000,"
+ "shortname=mixed,dmask=0077,utf8=1,showexec,flush)";
assertEquals("/media/HAZ SPACE(!)", f.parseMountPoint(line));
}
}

View File

@@ -1,24 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MacRemovableDriveFinderTest extends BrambleTestCase {
@Test
public void testParseMountPoint() {
MacRemovableDriveFinder f = new MacRemovableDriveFinder();
String line = "/dev/disk0s3 on / (local, journaled)";
assertEquals("/", f.parseMountPoint(line));
line = "devfs on /dev (local)";
assertEquals(null, f.parseMountPoint(line)); // Can't be parsed
line = "<volfs> on /.vol";
assertEquals(null, f.parseMountPoint(line)); // Can't be parsed
line = "automount -nsl [117] on /Network (automounted)";
assertEquals(null, f.parseMountPoint(line)); // Can't be parsed
line = "/dev/disk1s1 on /Volumes/HAZ SPACE(!) (local, nodev, nosuid)";
assertEquals("/Volumes/HAZ SPACE(!)", f.parseMountPoint(line));
}
}

View File

@@ -1,107 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.plugin.file.RemovableDriveMonitor.Callback;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class PollingRemovableDriveMonitorTest extends BrambleTestCase {
@Test
public void testOneCallbackPerFile() throws Exception {
// Create a finder that returns no files the first time, then two files
File file1 = new File("foo");
File file2 = new File("bar");
@NotNullByDefault
RemovableDriveFinder finder = new RemovableDriveFinder() {
private AtomicBoolean firstCall = new AtomicBoolean(true);
@Override
public Collection<File> findRemovableDrives() throws IOException {
if (firstCall.getAndSet(false)) return Collections.emptyList();
else return Arrays.asList(file1, file2);
}
};
// Create a callback that waits for two files
CountDownLatch latch = new CountDownLatch(2);
List<File> detected = new ArrayList<>();
@NotNullByDefault
Callback callback = new Callback() {
@Override
public void driveInserted(File f) {
detected.add(f);
latch.countDown();
}
@Override
public void exceptionThrown(IOException e) {
fail();
}
};
// Create the monitor and start it
RemovableDriveMonitor monitor = new PollingRemovableDriveMonitor(
Executors.newCachedThreadPool(), finder, 1);
monitor.start(callback);
// Wait for the monitor to detect the files
assertTrue(latch.await(10, SECONDS));
monitor.stop();
// Check that both files were detected
assertEquals(2, detected.size());
assertTrue(detected.contains(file1));
assertTrue(detected.contains(file2));
}
@Test
public void testExceptionCallback() throws Exception {
// Create a finder that throws an exception the second time it's polled
RemovableDriveFinder finder = new RemovableDriveFinder() {
private AtomicBoolean firstCall = new AtomicBoolean(true);
@Override
public Collection<File> findRemovableDrives() throws IOException {
if (firstCall.getAndSet(false)) return Collections.emptyList();
else throw new IOException();
}
};
// Create a callback that waits for an exception
CountDownLatch latch = new CountDownLatch(1);
@NotNullByDefault
Callback callback = new Callback() {
@Override
public void driveInserted(File root) {
fail();
}
@Override
public void exceptionThrown(IOException e) {
latch.countDown();
}
};
// Create the monitor and start it
RemovableDriveMonitor monitor = new PollingRemovableDriveMonitor(
Executors.newCachedThreadPool(), finder, 1);
monitor.start(callback);
assertTrue(latch.await(10, SECONDS));
monitor.stop();
}
}

View File

@@ -1,378 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.plugin.file.RemovableDriveMonitor.Callback;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.TestUtils;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.concurrent.Synchroniser;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import static org.briarproject.bramble.api.transport.TransportConstants.MIN_STREAM_LENGTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class RemovableDrivePluginTest extends BrambleTestCase {
private final File testDir = TestUtils.getTestDirectory();
private final ContactId contactId = new ContactId(234);
@Before
public void setUp() {
testDir.mkdirs();
}
@Test
public void testWriterIsNullIfNoDrivesAreFound() throws Exception {
List<File> drives = Collections.emptyList();
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
Executor executor = context.mock(Executor.class);
SimplexPluginCallback callback =
context.mock(SimplexPluginCallback.class);
RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
callback, finder, monitor, 0);
plugin.start();
assertNull(plugin.createWriter(contactId));
context.assertIsSatisfied();
}
@Test
public void testWriterIsNullIfNoDriveIsChosen() throws Exception {
File drive1 = new File(testDir, "1");
File drive2 = new File(testDir, "2");
List<File> drives = new ArrayList<>();
drives.add(drive1);
drives.add(drive2);
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
Executor executor = context.mock(Executor.class);
SimplexPluginCallback callback =
context.mock(SimplexPluginCallback.class);
RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
oneOf(callback).showChoice(with(any(String[].class)),
with(any(String[].class)));
will(returnValue(-1)); // The user cancelled the choice
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
callback, finder, monitor, 0);
plugin.start();
assertNull(plugin.createWriter(contactId));
File[] files = drive1.listFiles();
assertTrue(files == null || files.length == 0);
context.assertIsSatisfied();
}
@Test
public void testWriterIsNullIfOutputDirDoesNotExist() throws Exception {
File drive1 = new File(testDir, "1");
File drive2 = new File(testDir, "2");
List<File> drives = new ArrayList<>();
drives.add(drive1);
drives.add(drive2);
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
Executor executor = context.mock(Executor.class);
SimplexPluginCallback callback =
context.mock(SimplexPluginCallback.class);
RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
oneOf(callback).showChoice(with(any(String[].class)),
with(any(String[].class)));
will(returnValue(0)); // The user chose drive1 but it doesn't exist
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
callback, finder, monitor, 0);
plugin.start();
assertNull(plugin.createWriter(contactId));
File[] files = drive1.listFiles();
assertTrue(files == null || files.length == 0);
context.assertIsSatisfied();
}
@Test
public void testWriterIsNullIfOutputDirIsAFile() throws Exception {
File drive1 = new File(testDir, "1");
File drive2 = new File(testDir, "2");
List<File> drives = new ArrayList<>();
drives.add(drive1);
drives.add(drive2);
// Create drive1 as a file rather than a directory
assertTrue(drive1.createNewFile());
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
Executor executor = context.mock(Executor.class);
SimplexPluginCallback callback =
context.mock(SimplexPluginCallback.class);
RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
oneOf(callback).showChoice(with(any(String[].class)),
with(any(String[].class)));
will(returnValue(0)); // The user chose drive1 but it's not a dir
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
callback, finder, monitor, 0);
plugin.start();
assertNull(plugin.createWriter(contactId));
File[] files = drive1.listFiles();
assertTrue(files == null || files.length == 0);
context.assertIsSatisfied();
}
@Test
public void testWriterIsNotNullIfOutputDirIsADir() throws Exception {
File drive1 = new File(testDir, "1");
File drive2 = new File(testDir, "2");
List<File> drives = new ArrayList<>();
drives.add(drive1);
drives.add(drive2);
// Create drive1 as a directory
assertTrue(drive1.mkdir());
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
Executor executor = context.mock(Executor.class);
SimplexPluginCallback callback =
context.mock(SimplexPluginCallback.class);
RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
oneOf(callback).showChoice(with(any(String[].class)),
with(any(String[].class)));
will(returnValue(0)); // The user chose drive1
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
callback, finder, monitor, 0);
plugin.start();
assertNotNull(plugin.createWriter(contactId));
// The output file should exist and should be empty
File[] files = drive1.listFiles();
assertNotNull(files);
assertEquals(1, files.length);
assertEquals(0, files[0].length());
context.assertIsSatisfied();
}
@Test
public void testWritingToWriter() throws Exception {
File drive1 = new File(testDir, "1");
File drive2 = new File(testDir, "2");
List<File> drives = new ArrayList<>();
drives.add(drive1);
drives.add(drive2);
// Create drive1 as a directory
assertTrue(drive1.mkdir());
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
Executor executor = context.mock(Executor.class);
SimplexPluginCallback callback =
context.mock(SimplexPluginCallback.class);
RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
oneOf(callback).showChoice(with(any(String[].class)),
with(any(String[].class)));
will(returnValue(0)); // The user chose drive1
oneOf(callback).showMessage(with(any(String[].class)));
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
callback, finder, monitor, 0);
plugin.start();
TransportConnectionWriter writer = plugin.createWriter(contactId);
assertNotNull(writer);
// The output file should exist and should be empty
File[] files = drive1.listFiles();
assertNotNull(files);
assertEquals(1, files.length);
assertEquals(0, files[0].length());
// Writing to the output stream should increase the size of the file
OutputStream out = writer.getOutputStream();
out.write(new byte[1234]);
out.flush();
out.close();
// Disposing of the writer should not delete the file
writer.dispose(false);
assertTrue(files[0].exists());
assertEquals(1234, files[0].length());
context.assertIsSatisfied();
}
@Test
public void testEmptyDriveIsIgnored() throws Exception {
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
Executor executor = context.mock(Executor.class);
SimplexPluginCallback callback =
context.mock(SimplexPluginCallback.class);
RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
callback, finder, monitor, 0);
plugin.start();
plugin.driveInserted(testDir);
context.assertIsSatisfied();
}
@Test
public void testFilenames() {
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
Executor executor = context.mock(Executor.class);
SimplexPluginCallback callback =
context.mock(SimplexPluginCallback.class);
RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
callback, finder, monitor, 0);
assertFalse(plugin.isPossibleConnectionFilename("abcdefg.dat"));
assertFalse(plugin.isPossibleConnectionFilename("abcdefghi.dat"));
assertFalse(plugin.isPossibleConnectionFilename("abcdefgh_dat"));
assertFalse(plugin.isPossibleConnectionFilename("abcdefgh.rat"));
assertTrue(plugin.isPossibleConnectionFilename("abcdefgh.dat"));
assertTrue(plugin.isPossibleConnectionFilename("ABCDEFGH.DAT"));
context.assertIsSatisfied();
}
@Test
public void testReaderIsCreated() throws Exception {
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
SimplexPluginCallback callback =
context.mock(SimplexPluginCallback.class);
RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(callback).readerCreated(with(any(FileTransportReader.class)));
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(
new ImmediateExecutor(), callback, finder, monitor, 0);
plugin.start();
File f = new File(testDir, "abcdefgh.dat");
OutputStream out = new FileOutputStream(f);
out.write(new byte[MIN_STREAM_LENGTH]);
out.flush();
out.close();
assertEquals(MIN_STREAM_LENGTH, f.length());
plugin.driveInserted(testDir);
context.assertIsSatisfied();
}
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
}
}

View File

@@ -1,112 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.plugin.file.RemovableDriveMonitor.Callback;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.OsUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class UnixRemovableDriveMonitorTest extends BrambleTestCase {
private final File testDir = TestUtils.getTestDirectory();
@Before
public void setUp() {
testDir.mkdirs();
}
@Test
public void testNonexistentDir() throws Exception {
if (!(OsUtils.isLinux() || OsUtils.isMacLeopardOrNewer())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
File doesNotExist = new File(testDir, "doesNotExist");
RemovableDriveMonitor monitor = createMonitor(doesNotExist);
@NotNullByDefault
Callback callback = new Callback() {
@Override
public void driveInserted(File root) {
fail();
}
@Override
public void exceptionThrown(IOException e) {
fail();
}
};
monitor.start(callback);
monitor.stop();
}
@Test
public void testOneCallbackPerFile() throws Exception {
if (!(OsUtils.isLinux() || OsUtils.isMacLeopardOrNewer())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Create a callback that will wait for two files before stopping
List<File> detected = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(2);
@NotNullByDefault
Callback callback = new Callback() {
@Override
public void driveInserted(File f) {
detected.add(f);
latch.countDown();
}
@Override
public void exceptionThrown(IOException e) {
fail();
}
};
// Create the monitor and start it
RemovableDriveMonitor monitor = createMonitor(testDir);
monitor.start(callback);
// Create two files in the test directory
File file1 = new File(testDir, "1");
File file2 = new File(testDir, "2");
assertTrue(file1.createNewFile());
assertTrue(file2.createNewFile());
// Wait for the monitor to detect the files
assertTrue(latch.await(5, SECONDS));
monitor.stop();
// Check that both files were detected
assertEquals(2, detected.size());
assertTrue(detected.contains(file1));
assertTrue(detected.contains(file2));
}
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
}
private RemovableDriveMonitor createMonitor(File dir) {
@NotNullByDefault
RemovableDriveMonitor monitor = new UnixRemovableDriveMonitor() {
@Override
protected String[] getPathsToWatch() {
return new String[] {dir.getPath()};
}
};
return monitor;
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.modem; package org.briarproject.bramble.plugin.modem;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
@@ -66,7 +65,6 @@ public class ModemPluginTest extends BrambleTestCase {
TransportProperties remote = new TransportProperties(); TransportProperties remote = new TransportProperties();
remote.put("iso3166", ISO_1336); remote.put("iso3166", ISO_1336);
remote.put("number", NUMBER); remote.put("number", NUMBER);
ContactId contactId = new ContactId(234);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
oneOf(serialPortList).getPortNames(); oneOf(serialPortList).getPortNames();
@@ -78,14 +76,12 @@ public class ModemPluginTest extends BrambleTestCase {
// createConnection() // createConnection()
oneOf(callback).getLocalProperties(); oneOf(callback).getLocalProperties();
will(returnValue(local)); will(returnValue(local));
oneOf(callback).getRemoteProperties(contactId);
will(returnValue(remote));
oneOf(modem).dial(NUMBER); oneOf(modem).dial(NUMBER);
will(returnValue(true)); will(returnValue(true));
}}); }});
plugin.start(); plugin.start();
// A connection should be returned // A connection should be returned
assertNotNull(plugin.createConnection(contactId)); assertNotNull(plugin.createConnection(remote));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -105,7 +101,6 @@ public class ModemPluginTest extends BrambleTestCase {
TransportProperties remote = new TransportProperties(); TransportProperties remote = new TransportProperties();
remote.put("iso3166", ISO_1336); remote.put("iso3166", ISO_1336);
remote.put("number", NUMBER); remote.put("number", NUMBER);
ContactId contactId = new ContactId(234);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
oneOf(serialPortList).getPortNames(); oneOf(serialPortList).getPortNames();
@@ -117,14 +112,12 @@ public class ModemPluginTest extends BrambleTestCase {
// createConnection() // createConnection()
oneOf(callback).getLocalProperties(); oneOf(callback).getLocalProperties();
will(returnValue(local)); will(returnValue(local));
oneOf(callback).getRemoteProperties(contactId);
will(returnValue(remote));
oneOf(modem).dial(NUMBER); oneOf(modem).dial(NUMBER);
will(returnValue(false)); will(returnValue(false));
}}); }});
plugin.start(); plugin.start();
// No connection should be returned // No connection should be returned
assertNull(plugin.createConnection(contactId)); assertNull(plugin.createConnection(remote));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -144,7 +137,6 @@ public class ModemPluginTest extends BrambleTestCase {
TransportProperties remote = new TransportProperties(); TransportProperties remote = new TransportProperties();
remote.put("iso3166", ISO_1336); remote.put("iso3166", ISO_1336);
remote.put("number", NUMBER); remote.put("number", NUMBER);
ContactId contactId = new ContactId(234);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
oneOf(serialPortList).getPortNames(); oneOf(serialPortList).getPortNames();
@@ -156,8 +148,6 @@ public class ModemPluginTest extends BrambleTestCase {
// createConnection() // createConnection()
oneOf(callback).getLocalProperties(); oneOf(callback).getLocalProperties();
will(returnValue(local)); will(returnValue(local));
oneOf(callback).getRemoteProperties(contactId);
will(returnValue(remote));
oneOf(modem).dial(NUMBER); oneOf(modem).dial(NUMBER);
will(throwException(new IOException())); will(throwException(new IOException()));
// resetModem() // resetModem()
@@ -170,7 +160,7 @@ public class ModemPluginTest extends BrambleTestCase {
}}); }});
plugin.start(); plugin.start();
// No connection should be returned // No connection should be returned
assertNull(plugin.createConnection(contactId)); assertNull(plugin.createConnection(remote));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
} }

View File

@@ -19,15 +19,13 @@ import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevConfig; import org.briarproject.bramble.api.reporting.DevConfig;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.Scheduler; import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.ui.UiCallback;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory; import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory; import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
import org.briarproject.bramble.plugin.tor.TorPluginFactory; import org.briarproject.bramble.plugin.tor.TorPluginFactory;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.DozeWatchdog; import org.briarproject.briar.api.android.DozeWatchdog;
@@ -37,9 +35,7 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.io.File; import java.io.File;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@@ -51,6 +47,8 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_ONION_ADDRESS; import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_ONION_ADDRESS;
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_PUBLIC_KEY_HEX; import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_PUBLIC_KEY_HEX;
@@ -67,27 +65,9 @@ public class AppModule {
} }
private final Application application; private final Application application;
private final UiCallback uiCallback;
public AppModule(Application application) { public AppModule(Application application) {
this.application = application; this.application = application;
uiCallback = new UiCallback() {
@Override
public int showChoice(String[] options, String... message) {
throw new UnsupportedOperationException();
}
@Override
public boolean showConfirmationMessage(String... message) {
throw new UnsupportedOperationException();
}
@Override
public void showMessage(String... message) {
throw new UnsupportedOperationException();
}
};
} }
@Provides @Provides
@@ -96,11 +76,6 @@ public class AppModule {
return application; return application;
} }
@Provides
UiCallback provideUICallback() {
return uiCallback;
}
@Provides @Provides
@Singleton @Singleton
DatabaseConfig provideDatabaseConfig(Application app) { DatabaseConfig provideDatabaseConfig(Application app) {
@@ -122,8 +97,7 @@ public class AppModule {
@Scheduler ScheduledExecutorService scheduler, @Scheduler ScheduledExecutorService scheduler,
AndroidExecutor androidExecutor, SecureRandom random, AndroidExecutor androidExecutor, SecureRandom random,
SocketFactory torSocketFactory, BackoffFactory backoffFactory, SocketFactory torSocketFactory, BackoffFactory backoffFactory,
Application app, LocationUtils locationUtils, DevReporter reporter, Application app, LocationUtils locationUtils, EventBus eventBus) {
EventBus eventBus) {
Context appContext = app.getApplicationContext(); Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth = DuplexPluginFactory bluetooth =
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor, new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
@@ -133,8 +107,7 @@ public class AppModule {
torSocketFactory, backoffFactory); torSocketFactory, backoffFactory);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
scheduler, backoffFactory, appContext); scheduler, backoffFactory, appContext);
Collection<DuplexPluginFactory> duplex = Collection<DuplexPluginFactory> duplex = asList(bluetooth, tor, lan);
Arrays.asList(bluetooth, tor, lan);
@NotNullByDefault @NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() { PluginConfig pluginConfig = new PluginConfig() {
@@ -145,14 +118,13 @@ public class AppModule {
@Override @Override
public Collection<SimplexPluginFactory> getSimplexFactories() { public Collection<SimplexPluginFactory> getSimplexFactories() {
return Collections.emptyList(); return emptyList();
} }
@Override @Override
public boolean shouldPoll() { public boolean shouldPoll() {
return true; return true;
} }
}; };
return pluginConfig; return pluginConfig;
} }