mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
Compare commits
18 Commits
release-1.
...
bluetooth-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb98cb2f23 | ||
|
|
aa331981cc | ||
|
|
e9a6faad2d | ||
|
|
5025cf1e40 | ||
|
|
834342fd3a | ||
|
|
3028b236e1 | ||
|
|
254422bc02 | ||
|
|
c7949d6e00 | ||
|
|
0187264da7 | ||
|
|
85a18cf53f | ||
|
|
3181b695df | ||
|
|
b2ac210586 | ||
|
|
d20340416d | ||
|
|
9da871718c | ||
|
|
3793cb841b | ||
|
|
c6b88b51f0 | ||
|
|
2f00215a44 | ||
|
|
def62bce5a |
@@ -1,11 +1,5 @@
|
|||||||
import de.undercouch.gradle.tasks.download.Download
|
|
||||||
import de.undercouch.gradle.tasks.download.Verify
|
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException
|
|
||||||
|
|
||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
apply plugin: 'de.undercouch.download'
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 27
|
||||||
@@ -25,9 +19,14 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
tor
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
implementation project(path: ':bramble-core', configuration: 'default')
|
||||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
implementation 'org.briarproject:jtorctl:0.3'
|
||||||
|
tor 'org.briarproject:tor-android:0.2.9.15@zip'
|
||||||
|
|
||||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|
||||||
@@ -92,6 +91,8 @@ dependencyVerification {
|
|||||||
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
|
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
|
||||||
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||||
|
'org.briarproject:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864',
|
||||||
|
'org.briarproject:tor-android:0.2.9.15:tor-android-0.2.9.15.zip:34a6474ee219ffa52e0f3393e917dda6ed03d320b02247d4fa5075aa4094ee6d',
|
||||||
'org.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
|
'org.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
|
||||||
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
|
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
|
||||||
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
|
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
|
||||||
@@ -112,81 +113,9 @@ dependencyVerification {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.torBinaryDir = 'src/main/res/raw'
|
|
||||||
ext.torVersion = '0.2.9.14'
|
|
||||||
ext.geoipVersion = '2017-11-06'
|
|
||||||
ext.torDownloadUrl = 'https://briarproject.org/build/'
|
|
||||||
|
|
||||||
def torBinaries = [
|
|
||||||
"tor_arm" : '1710ea6c47b7f4c1a88bdf4858c7893837635db10e8866854eed8d61629f50e8',
|
|
||||||
"tor_arm_pie": '974e6949507db8fa2ea45231817c2c3677ed4ccf5488a2252317d744b0be1917',
|
|
||||||
"tor_x86" : '3a5e45b3f051fcda9353b098b7086e762ffe7ba9242f7d7c8bf6523faaa8b1e9',
|
|
||||||
"tor_x86_pie": 'd1d96d8ce1a4b68accf04850185780d10cd5563d3552f7e1f040f8ca32cb4e51',
|
|
||||||
"geoip" : '8239b98374493529a29096e45fc5877d4d6fdad0146ad8380b291f90d61484ea'
|
|
||||||
]
|
|
||||||
|
|
||||||
def verifyOrDeleteBinary(name, chksum, alreadyVerified) {
|
|
||||||
return tasks.create("verifyOrDeleteBinary${name}", VerifyOrDelete) {
|
|
||||||
src "${torBinaryDir}/${name}.zip"
|
|
||||||
algorithm 'SHA-256'
|
|
||||||
checksum chksum
|
|
||||||
result alreadyVerified
|
|
||||||
onlyIf {
|
|
||||||
src.exists()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def downloadBinary(name, chksum, alreadyVerified) {
|
|
||||||
return tasks.create([
|
|
||||||
name: "downloadBinary${name}",
|
|
||||||
type: Download,
|
|
||||||
dependsOn: verifyOrDeleteBinary(name, chksum, alreadyVerified)]) {
|
|
||||||
src "${torDownloadUrl}${name}.zip"
|
|
||||||
.replace('tor_', "tor-${torVersion}-")
|
|
||||||
.replace('geoip', "geoip-${geoipVersion}")
|
|
||||||
.replaceAll('_', '-')
|
|
||||||
dest "${torBinaryDir}/${name}.zip"
|
|
||||||
onlyIf {
|
|
||||||
!dest.exists()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def verifyBinary(name, chksum) {
|
|
||||||
boolean[] alreadyVerified = [false]
|
|
||||||
return tasks.create([
|
|
||||||
name : "verifyBinary${name}",
|
|
||||||
type : Verify,
|
|
||||||
dependsOn: downloadBinary(name, chksum, alreadyVerified)]) {
|
|
||||||
src "${torBinaryDir}/${name}.zip"
|
|
||||||
algorithm 'SHA-256'
|
|
||||||
checksum chksum
|
|
||||||
onlyIf {
|
|
||||||
!alreadyVerified[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
project.afterEvaluate {
|
project.afterEvaluate {
|
||||||
torBinaries.every { name, checksum ->
|
copy {
|
||||||
preBuild.dependsOn.add(verifyBinary(name, checksum))
|
from configurations.tor.collect { zipTree(it) }
|
||||||
}
|
into 'src/main/res/raw'
|
||||||
}
|
|
||||||
|
|
||||||
class VerifyOrDelete extends Verify {
|
|
||||||
|
|
||||||
boolean[] result
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
@Override
|
|
||||||
void verify() throws IOException, NoSuchAlgorithmException {
|
|
||||||
try {
|
|
||||||
super.verify()
|
|
||||||
result[0] = true
|
|
||||||
} catch (Exception e) {
|
|
||||||
println "${src} failed verification - deleting"
|
|
||||||
src.delete()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -1,12 +1,10 @@
|
|||||||
package org.briarproject.bramble;
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
import org.briarproject.bramble.plugin.AndroidPluginModule;
|
|
||||||
import org.briarproject.bramble.system.AndroidSystemModule;
|
import org.briarproject.bramble.system.AndroidSystemModule;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
|
|
||||||
@Module(includes = {
|
@Module(includes = {
|
||||||
AndroidPluginModule.class,
|
|
||||||
AndroidSystemModule.class
|
AndroidSystemModule.class
|
||||||
})
|
})
|
||||||
public class BrambleAndroidModule {
|
public class BrambleAndroidModule {
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.Scheduler;
|
|
||||||
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
|
|
||||||
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
|
|
||||||
import org.briarproject.bramble.plugin.tor.TorPluginFactory;
|
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class AndroidPluginModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
|
|
||||||
@Scheduler ScheduledExecutorService scheduler,
|
|
||||||
AndroidExecutor androidExecutor, SecureRandom random,
|
|
||||||
SocketFactory torSocketFactory, BackoffFactory backoffFactory,
|
|
||||||
Application app, LocationUtils locationUtils, EventBus eventBus) {
|
|
||||||
Context appContext = app.getApplicationContext();
|
|
||||||
DuplexPluginFactory bluetooth =
|
|
||||||
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
|
|
||||||
appContext, random, eventBus, backoffFactory);
|
|
||||||
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler,
|
|
||||||
appContext, locationUtils, eventBus, torSocketFactory,
|
|
||||||
backoffFactory);
|
|
||||||
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
|
|
||||||
scheduler, backoffFactory, appContext);
|
|
||||||
Collection<DuplexPluginFactory> duplex =
|
|
||||||
Arrays.asList(bluetooth, tor, lan);
|
|
||||||
@NotNullByDefault
|
|
||||||
PluginConfig pluginConfig = new PluginConfig() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<DuplexPluginFactory> getDuplexFactories() {
|
|
||||||
return duplex;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return pluginConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import java.util.List;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static android.content.Context.MODE_PRIVATE;
|
import static android.content.Context.MODE_PRIVATE;
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
|
|
||||||
public class AndroidUtils {
|
public class AndroidUtils {
|
||||||
|
|
||||||
@@ -59,57 +58,28 @@ public class AndroidUtils {
|
|||||||
&& !address.equals(FAKE_BLUETOOTH_ADDRESS);
|
&& !address.equals(FAKE_BLUETOOTH_ADDRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void logDataDirContents(Context ctx) {
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info("Contents of data directory:");
|
|
||||||
logFileOrDir(new File(ctx.getApplicationInfo().dataDir));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void logFileOrDir(File f) {
|
|
||||||
LOG.info(f.getAbsolutePath() + " " + f.length());
|
|
||||||
if (f.isDirectory()) {
|
|
||||||
File[] children = f.listFiles();
|
|
||||||
if (children == null) {
|
|
||||||
LOG.info("Could not list files in " + f.getAbsolutePath());
|
|
||||||
} else {
|
|
||||||
for (File child : children) logFileOrDir(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void deleteAppData(Context ctx, SharedPreferences... clear) {
|
public static void deleteAppData(Context ctx, SharedPreferences... clear) {
|
||||||
// Clear and commit shared preferences
|
// Clear and commit shared preferences
|
||||||
for (SharedPreferences prefs : clear) {
|
for (SharedPreferences prefs : clear) {
|
||||||
boolean cleared = prefs.edit().clear().commit();
|
if (!prefs.edit().clear().commit())
|
||||||
if (LOG.isLoggable(INFO)) {
|
LOG.warning("Could not clear shared preferences");
|
||||||
if (cleared) LOG.info("Cleared shared preferences");
|
|
||||||
else LOG.info("Could not clear shared preferences");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Delete files, except lib and shared_prefs directories
|
// Delete files, except lib and shared_prefs directories
|
||||||
File dataDir = new File(ctx.getApplicationInfo().dataDir);
|
File dataDir = new File(ctx.getApplicationInfo().dataDir);
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Deleting app data from " + dataDir.getAbsolutePath());
|
|
||||||
File[] children = dataDir.listFiles();
|
File[] children = dataDir.listFiles();
|
||||||
if (children != null) {
|
if (children == null) {
|
||||||
|
LOG.warning("Could not list files in app data dir");
|
||||||
|
} else {
|
||||||
for (File child : children) {
|
for (File child : children) {
|
||||||
String name = child.getName();
|
String name = child.getName();
|
||||||
if (!name.equals("lib") && !name.equals("shared_prefs")) {
|
if (!name.equals("lib") && !name.equals("shared_prefs")) {
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Deleting " + child.getAbsolutePath());
|
|
||||||
IoUtils.deleteFileOrDir(child);
|
IoUtils.deleteFileOrDir(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info("Could not list files in " + dataDir.getAbsolutePath());
|
|
||||||
}
|
}
|
||||||
// Recreate the cache dir as some OpenGL drivers expect it to exist
|
// Recreate the cache dir as some OpenGL drivers expect it to exist
|
||||||
boolean recreated = new File(dataDir, "cache").mkdir();
|
if (!new File(dataDir, "cache").mkdir())
|
||||||
if (LOG.isLoggable(INFO)) {
|
LOG.warning("Could not recreate cache dir");
|
||||||
if (recreated) LOG.info("Recreated cache dir");
|
|
||||||
else LOG.info("Could not recreate cache dir");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getReportDir(Context ctx) {
|
public static File getReportDir(Context ctx) {
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
|
public interface FileConstants {
|
||||||
|
|
||||||
|
String PROP_PATH = "path";
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ public interface PluginConfig {
|
|||||||
Collection<DuplexPluginFactory> getDuplexFactories();
|
Collection<DuplexPluginFactory> getDuplexFactories();
|
||||||
|
|
||||||
Collection<SimplexPluginFactory> getSimplexFactories();
|
Collection<SimplexPluginFactory> getSimplexFactories();
|
||||||
|
|
||||||
|
boolean shouldPoll();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class IoUtils {
|
public class IoUtils {
|
||||||
@@ -25,18 +25,21 @@ public class IoUtils {
|
|||||||
delete(f);
|
delete(f);
|
||||||
} else if (f.isDirectory()) {
|
} else if (f.isDirectory()) {
|
||||||
File[] children = f.listFiles();
|
File[] children = f.listFiles();
|
||||||
if (children != null)
|
if (children == null) {
|
||||||
|
if (LOG.isLoggable(WARNING)) {
|
||||||
|
LOG.warning("Could not list files in "
|
||||||
|
+ f.getAbsolutePath());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for (File child : children) deleteFileOrDir(child);
|
for (File child : children) deleteFileOrDir(child);
|
||||||
|
}
|
||||||
delete(f);
|
delete(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void delete(File f) {
|
private static void delete(File f) {
|
||||||
boolean deleted = f.delete();
|
if (!f.delete() && LOG.isLoggable(WARNING))
|
||||||
if (LOG.isLoggable(INFO)) {
|
LOG.warning("Could not delete " + f.getAbsolutePath());
|
||||||
if (deleted) LOG.info("Deleted " + f.getAbsolutePath());
|
|
||||||
else LOG.info("Could not delete " + f.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void copyAndClose(InputStream in, OutputStream out) {
|
public static void copyAndClose(InputStream in, OutputStream out) {
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.lifecycle.Service;
|
|||||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
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.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
@@ -29,17 +30,19 @@ import org.briarproject.bramble.api.properties.TransportProperties;
|
|||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
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.ui.UiCallback;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.system.Scheduler;
|
||||||
|
|
||||||
|
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;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -57,12 +60,15 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
Logger.getLogger(PluginManagerImpl.class.getName());
|
Logger.getLogger(PluginManagerImpl.class.getName());
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
|
private final ScheduledExecutorService scheduler;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final PluginConfig pluginConfig;
|
private final PluginConfig pluginConfig;
|
||||||
private final ConnectionManager connectionManager;
|
private final ConnectionManager connectionManager;
|
||||||
|
private final ConnectionRegistry connectionRegistry;
|
||||||
private final SettingsManager settingsManager;
|
private final SettingsManager settingsManager;
|
||||||
private final TransportPropertyManager transportPropertyManager;
|
private final TransportPropertyManager transportPropertyManager;
|
||||||
private final UiCallback uiCallback;
|
private final SecureRandom random;
|
||||||
|
private final Clock clock;
|
||||||
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;
|
||||||
@@ -70,27 +76,41 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus,
|
PluginManagerImpl(@IoExecutor Executor ioExecutor,
|
||||||
|
@Scheduler ScheduledExecutorService scheduler, EventBus eventBus,
|
||||||
PluginConfig pluginConfig, ConnectionManager connectionManager,
|
PluginConfig pluginConfig, ConnectionManager connectionManager,
|
||||||
|
ConnectionRegistry connectionRegistry,
|
||||||
SettingsManager settingsManager,
|
SettingsManager settingsManager,
|
||||||
TransportPropertyManager transportPropertyManager,
|
TransportPropertyManager transportPropertyManager,
|
||||||
UiCallback uiCallback) {
|
SecureRandom random, Clock clock) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
|
this.scheduler = scheduler;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.pluginConfig = pluginConfig;
|
this.pluginConfig = pluginConfig;
|
||||||
this.connectionManager = connectionManager;
|
this.connectionManager = connectionManager;
|
||||||
|
this.connectionRegistry = connectionRegistry;
|
||||||
this.settingsManager = settingsManager;
|
this.settingsManager = settingsManager;
|
||||||
this.transportPropertyManager = transportPropertyManager;
|
this.transportPropertyManager = transportPropertyManager;
|
||||||
this.uiCallback = uiCallback;
|
this.random = random;
|
||||||
|
this.clock = clock;
|
||||||
plugins = new ConcurrentHashMap<>();
|
plugins = new ConcurrentHashMap<>();
|
||||||
simplexPlugins = new CopyOnWriteArrayList<>();
|
simplexPlugins = new CopyOnWriteArrayList<>();
|
||||||
duplexPlugins = new CopyOnWriteArrayList<>();
|
duplexPlugins = new CopyOnWriteArrayList<>();
|
||||||
startLatches = new ConcurrentHashMap<>();
|
startLatches = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
||||||
|
if (pluginConfig.shouldPoll()) {
|
||||||
|
LOG.info("Starting poller");
|
||||||
|
Poller poller = new Poller(ioExecutor, scheduler, connectionManager,
|
||||||
|
connectionRegistry, this, transportPropertyManager, random,
|
||||||
|
clock);
|
||||||
|
eventBus.addListener(poller);
|
||||||
|
}
|
||||||
// Instantiate the simplex plugins and start them asynchronously
|
// Instantiate the simplex plugins and start them asynchronously
|
||||||
LOG.info("Starting simplex plugins");
|
LOG.info("Starting simplex plugins");
|
||||||
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) {
|
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) {
|
||||||
@@ -273,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 {
|
||||||
@@ -311,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));
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -26,8 +18,6 @@ public class PluginModule {
|
|||||||
public static class EagerSingletons {
|
public static class EagerSingletons {
|
||||||
@Inject
|
@Inject
|
||||||
PluginManager pluginManager;
|
PluginManager pluginManager;
|
||||||
@Inject
|
|
||||||
Poller poller;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -35,19 +25,6 @@ public class PluginModule {
|
|||||||
return new BackoffFactoryImpl();
|
return new BackoffFactoryImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
Poller providePoller(@IoExecutor Executor ioExecutor,
|
|
||||||
@Scheduler ScheduledExecutorService scheduler,
|
|
||||||
ConnectionManager connectionManager,
|
|
||||||
ConnectionRegistry connectionRegistry, PluginManager pluginManager,
|
|
||||||
SecureRandom random, Clock clock, EventBus eventBus) {
|
|
||||||
Poller poller = new Poller(ioExecutor, scheduler, connectionManager,
|
|
||||||
connectionRegistry, pluginManager, random, clock);
|
|
||||||
eventBus.addListener(poller);
|
|
||||||
return poller;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
ConnectionManager provideConnectionManager(
|
ConnectionManager provideConnectionManager(
|
||||||
|
|||||||
@@ -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,12 +20,17 @@ 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.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
@@ -33,10 +39,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,26 +55,30 @@ 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;
|
||||||
private final Map<TransportId, ScheduledPollTask> tasks; // Locking: lock
|
private final Map<TransportId, ScheduledPollTask> tasks; // Locking: lock
|
||||||
|
private final Set<TransportId> polling; // Locking: lock
|
||||||
|
|
||||||
@Inject
|
|
||||||
Poller(@IoExecutor Executor ioExecutor,
|
Poller(@IoExecutor Executor ioExecutor,
|
||||||
@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();
|
||||||
tasks = new HashMap<>();
|
tasks = new HashMap<>();
|
||||||
|
polling = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -120,10 +130,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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -131,10 +146,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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -186,7 +206,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 {
|
||||||
@@ -215,20 +245,33 @@ class Poller implements EventListener {
|
|||||||
@Override
|
@Override
|
||||||
@IoExecutor
|
@IoExecutor
|
||||||
public void run() {
|
public void run() {
|
||||||
|
TransportId t = plugin.getId();
|
||||||
|
boolean shouldPoll;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
TransportId t = plugin.getId();
|
|
||||||
ScheduledPollTask scheduled = tasks.get(t);
|
ScheduledPollTask scheduled = tasks.get(t);
|
||||||
if (scheduled != null && scheduled.task != this)
|
if (scheduled != null && scheduled.task != this)
|
||||||
return; // Replaced by another task
|
return; // Replaced by another task
|
||||||
tasks.remove(t);
|
tasks.remove(t);
|
||||||
|
// Don't poll again if last poll is still running
|
||||||
|
shouldPoll = polling.add(t);
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
int delay = plugin.getPollingInterval();
|
int delay = plugin.getPollingInterval();
|
||||||
if (randomiseNext) delay = (int) (delay * random.nextDouble());
|
if (randomiseNext) delay = (int) (delay * random.nextDouble());
|
||||||
schedule(plugin, delay, false);
|
schedule(plugin, delay, false);
|
||||||
poll(plugin);
|
if (shouldPoll) {
|
||||||
|
poll(plugin);
|
||||||
|
} else if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Last poll for " + t + " is still running");
|
||||||
|
}
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
polling.remove(t);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,10 @@ 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.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@@ -53,6 +54,12 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(BluetoothPlugin.class.getName());
|
Logger.getLogger(BluetoothPlugin.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many milliseconds to pause between connection attempts when
|
||||||
|
* polling, to avoid interfering with other Bluetooth or wifi connections.
|
||||||
|
*/
|
||||||
|
private static final int POLLING_PAUSE_MS = 1000;
|
||||||
|
|
||||||
final BluetoothConnectionLimiter connectionLimiter;
|
final BluetoothConnectionLimiter connectionLimiter;
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
@@ -250,30 +257,42 @@ 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 a random order
|
||||||
Map<ContactId, TransportProperties> remote =
|
List<ContactId> keys = new ArrayList<>(contacts.keySet());
|
||||||
callback.getRemoteProperties();
|
Collections.shuffle(keys);
|
||||||
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
ioExecutor.execute(() -> {
|
||||||
ContactId c = e.getKey();
|
boolean first = true;
|
||||||
if (connected.contains(c)) continue;
|
for (ContactId c : keys) {
|
||||||
String address = e.getValue().get(PROP_ADDRESS);
|
|
||||||
if (StringUtils.isNullOrEmpty(address)) continue;
|
|
||||||
String uuid = e.getValue().get(PROP_UUID);
|
|
||||||
if (StringUtils.isNullOrEmpty(uuid)) continue;
|
|
||||||
ioExecutor.execute(() -> {
|
|
||||||
if (!isRunning() || !shouldAllowContactConnections()) return;
|
if (!isRunning() || !shouldAllowContactConnections()) return;
|
||||||
if (!connectionLimiter.canOpenContactConnection()) return;
|
if (!connectionLimiter.canOpenContactConnection()) return;
|
||||||
|
TransportProperties p = contacts.get(c);
|
||||||
|
String address = p.get(PROP_ADDRESS);
|
||||||
|
if (StringUtils.isNullOrEmpty(address)) continue;
|
||||||
|
String uuid = p.get(PROP_UUID);
|
||||||
|
if (StringUtils.isNullOrEmpty(uuid)) continue;
|
||||||
|
// Pause between connection attempts
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Thread.sleep(POLLING_PAUSE_MS);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
LOG.info("Interrupted while polling");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
DuplexTransportConnection conn = connect(address, uuid);
|
DuplexTransportConnection conn = connect(address, uuid);
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
backoff.reset();
|
backoff.reset();
|
||||||
if (connectionLimiter.contactConnectionOpened(conn))
|
if (connectionLimiter.contactConnectionOpened(conn))
|
||||||
callback.outgoingConnectionCreated(c, conn);
|
callback.outgoingConnectionCreated(c, conn);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -308,10 +327,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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
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.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
import org.briarproject.bramble.api.plugin.PluginException;
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
@@ -13,16 +14,18 @@ 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 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.ui.UiCallback;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
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;
|
||||||
import org.jmock.lib.concurrent.Synchroniser;
|
import org.jmock.lib.concurrent.Synchroniser;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
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.ScheduledExecutorService;
|
||||||
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
|
|
||||||
@@ -34,15 +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);
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
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);
|
||||||
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 =
|
||||||
@@ -71,6 +79,8 @@ public class PluginManagerImplTest extends BrambleTestCase {
|
|||||||
will(returnValue(simplexFailId));
|
will(returnValue(simplexFailId));
|
||||||
allowing(duplexPlugin).getId();
|
allowing(duplexPlugin).getId();
|
||||||
will(returnValue(duplexId));
|
will(returnValue(duplexId));
|
||||||
|
allowing(pluginConfig).shouldPoll();
|
||||||
|
will(returnValue(false));
|
||||||
// start()
|
// start()
|
||||||
// First simplex plugin
|
// First simplex plugin
|
||||||
oneOf(pluginConfig).getSimplexFactories();
|
oneOf(pluginConfig).getSimplexFactories();
|
||||||
@@ -112,9 +122,9 @@ public class PluginManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(duplexPlugin).stop();
|
oneOf(duplexPlugin).stop();
|
||||||
}});
|
}});
|
||||||
|
|
||||||
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
|
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, scheduler,
|
||||||
pluginConfig, connectionManager, settingsManager,
|
eventBus, pluginConfig, connectionManager, connectionRegistry,
|
||||||
transportPropertyManager, uiCallback);
|
settingsManager, transportPropertyManager, random, clock);
|
||||||
|
|
||||||
// Two plugins should be started and stopped
|
// Two plugins should be started and stopped
|
||||||
p.startService();
|
p.startService();
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,17 @@ 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
|
||||||
|
public boolean shouldPoll() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return pluginConfig;
|
return pluginConfig;
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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,12 @@ public class DesktopPluginModule extends PluginModule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
||||||
return simplex;
|
return emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldPoll() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return pluginConfig;
|
return pluginConfig;
|
||||||
|
|||||||
@@ -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/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
|
|
||||||
@@ -8,12 +9,22 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
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.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
|
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.ui.UiCallback;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
|
import org.briarproject.bramble.api.system.Scheduler;
|
||||||
|
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
|
||||||
|
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
|
||||||
|
import org.briarproject.bramble.plugin.tor.TorPluginFactory;
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
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;
|
||||||
@@ -23,14 +34,21 @@ 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.util.Collection;
|
||||||
|
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;
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
import dagger.Module;
|
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;
|
||||||
|
|
||||||
@@ -47,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
|
||||||
@@ -76,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) {
|
||||||
@@ -97,6 +92,43 @@ public class AppModule {
|
|||||||
return databaseConfig;
|
return databaseConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
|
||||||
|
@Scheduler ScheduledExecutorService scheduler,
|
||||||
|
AndroidExecutor androidExecutor, SecureRandom random,
|
||||||
|
SocketFactory torSocketFactory, BackoffFactory backoffFactory,
|
||||||
|
Application app, LocationUtils locationUtils, EventBus eventBus) {
|
||||||
|
Context appContext = app.getApplicationContext();
|
||||||
|
DuplexPluginFactory bluetooth =
|
||||||
|
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
|
||||||
|
appContext, random, eventBus, backoffFactory);
|
||||||
|
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler,
|
||||||
|
appContext, locationUtils, eventBus,
|
||||||
|
torSocketFactory, backoffFactory);
|
||||||
|
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
|
||||||
|
scheduler, backoffFactory, appContext);
|
||||||
|
Collection<DuplexPluginFactory> duplex = asList(bluetooth, tor, lan);
|
||||||
|
@NotNullByDefault
|
||||||
|
PluginConfig pluginConfig = new PluginConfig() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<DuplexPluginFactory> getDuplexFactories() {
|
||||||
|
return duplex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
||||||
|
return emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldPoll() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return pluginConfig;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
DevConfig provideDevConfig(Application app, CryptoComponent crypto) {
|
DevConfig provideDevConfig(Application app, CryptoComponent crypto) {
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
package org.briarproject.briar.android.blog;
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.v4.app.ActivityCompat;
|
|
||||||
import android.support.v4.app.ActivityOptionsCompat;
|
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.view.ViewCompat;
|
import android.support.v4.view.ViewCompat;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
@@ -27,7 +23,6 @@ import org.briarproject.briar.api.blog.BlogPostHeader;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
|
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||||
@@ -35,7 +30,6 @@ import static org.briarproject.briar.android.blog.BasePostFragment.POST_ID;
|
|||||||
import static org.briarproject.briar.android.util.UiUtils.TEASER_LENGTH;
|
import static org.briarproject.briar.android.util.UiUtils.TEASER_LENGTH;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getSpanned;
|
import static org.briarproject.briar.android.util.UiUtils.getSpanned;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getTeaser;
|
import static org.briarproject.briar.android.util.UiUtils.getTeaser;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.isSamsung7;
|
|
||||||
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
|
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
|
||||||
import static org.briarproject.briar.api.blog.MessageType.POST;
|
import static org.briarproject.briar.api.blog.MessageType.POST;
|
||||||
|
|
||||||
@@ -135,18 +129,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
Intent i = new Intent(ctx, ReblogActivity.class);
|
Intent i = new Intent(ctx, ReblogActivity.class);
|
||||||
i.putExtra(GROUP_ID, item.getGroupId().getBytes());
|
i.putExtra(GROUP_ID, item.getGroupId().getBytes());
|
||||||
i.putExtra(POST_ID, item.getId().getBytes());
|
i.putExtra(POST_ID, item.getId().getBytes());
|
||||||
|
ctx.startActivity(i);
|
||||||
if (Build.VERSION.SDK_INT >= 23 && !isSamsung7()) {
|
|
||||||
ActivityOptionsCompat options =
|
|
||||||
makeSceneTransitionAnimation((Activity) ctx, layout,
|
|
||||||
getTransitionName(item.getId()));
|
|
||||||
ActivityCompat.startActivity(ctx, i,
|
|
||||||
options.toBundle());
|
|
||||||
} else {
|
|
||||||
// work-around for android bug #224270
|
|
||||||
// work-around for Samsung Android 7 bug #1007
|
|
||||||
ctx.startActivity(i);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// comments
|
// comments
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import java.util.logging.Logger;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -156,21 +155,16 @@ public class ConfigControllerImpl implements ConfigController {
|
|||||||
SharedPreferences defaultPrefs =
|
SharedPreferences defaultPrefs =
|
||||||
PreferenceManager.getDefaultSharedPreferences(ctx);
|
PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||||
AndroidUtils.deleteAppData(ctx, briarPrefs, defaultPrefs);
|
AndroidUtils.deleteAppData(ctx, briarPrefs, defaultPrefs);
|
||||||
AndroidUtils.logDataDirContents(ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean accountExists() {
|
public boolean accountExists() {
|
||||||
String hex = getEncryptedDatabaseKey();
|
String hex = getEncryptedDatabaseKey();
|
||||||
boolean exists = hex != null && databaseConfig.databaseExists();
|
return hex != null && databaseConfig.databaseExists();
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Account exists: " + exists);
|
|
||||||
return exists;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean accountSignedIn() {
|
public boolean accountSignedIn() {
|
||||||
boolean signedIn = databaseConfig.getEncryptionKey() != null;
|
return databaseConfig.getEncryptionKey() != null;
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Signed in: " + signedIn);
|
|
||||||
return signedIn;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
|
||||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
||||||
|
|
||||||
@@ -103,14 +102,11 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
|||||||
if (password == null) throw new IllegalStateException();
|
if (password == null) throw new IllegalStateException();
|
||||||
cryptoExecutor.execute(() -> {
|
cryptoExecutor.execute(() -> {
|
||||||
LOG.info("Creating account");
|
LOG.info("Creating account");
|
||||||
AndroidUtils.logDataDirContents(setupActivity);
|
|
||||||
databaseConfig.setLocalAuthorName(authorName);
|
databaseConfig.setLocalAuthorName(authorName);
|
||||||
SecretKey key = crypto.generateSecretKey();
|
SecretKey key = crypto.generateSecretKey();
|
||||||
databaseConfig.setEncryptionKey(key);
|
databaseConfig.setEncryptionKey(key);
|
||||||
String hex = encryptDatabaseKey(key, password);
|
String hex = encryptDatabaseKey(key, password);
|
||||||
storeEncryptedDatabaseKey(hex);
|
storeEncryptedDatabaseKey(hex);
|
||||||
LOG.info("Created account");
|
|
||||||
AndroidUtils.logDataDirContents(setupActivity);
|
|
||||||
resultHandler.onResult(null);
|
resultHandler.onResult(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import android.support.v7.preference.PreferenceManager;
|
|||||||
import android.transition.Fade;
|
import android.transition.Fade;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
@@ -45,11 +44,9 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
setContentView(R.layout.splash);
|
setContentView(R.layout.splash);
|
||||||
|
|
||||||
if (configController.accountSignedIn()) {
|
if (configController.accountSignedIn()) {
|
||||||
LOG.info("Already signed in, not showing splash screen");
|
|
||||||
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
LOG.info("Showing splash screen");
|
|
||||||
new Handler().postDelayed(() -> {
|
new Handler().postDelayed(() -> {
|
||||||
startNextActivity();
|
startNextActivity();
|
||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
@@ -67,7 +64,6 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
LOG.info("Expired");
|
LOG.info("Expired");
|
||||||
startActivity(new Intent(this, ExpiredActivity.class));
|
startActivity(new Intent(this, ExpiredActivity.class));
|
||||||
} else {
|
} else {
|
||||||
AndroidUtils.logDataDirContents(this);
|
|
||||||
if (configController.accountExists()) {
|
if (configController.accountExists()) {
|
||||||
LOG.info("Account exists");
|
LOG.info("Account exists");
|
||||||
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
||||||
@@ -80,10 +76,8 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setPreferencesDefaults() {
|
private void setPreferencesDefaults() {
|
||||||
androidExecutor.runOnBackgroundThread(() -> {
|
androidExecutor.runOnBackgroundThread(() ->
|
||||||
PreferenceManager.setDefaultValues(SplashScreenActivity.this,
|
PreferenceManager.setDefaultValues(SplashScreenActivity.this,
|
||||||
R.xml.panic_preferences, false);
|
R.xml.panic_preferences, false));
|
||||||
LOG.info("Finished setting panic preference defaults");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
|||||||
textInput.setListener(this);
|
textInput.setListener(this);
|
||||||
list = findViewById(R.id.list);
|
list = findViewById(R.id.list);
|
||||||
layoutManager = new LinearLayoutManager(this);
|
layoutManager = new LinearLayoutManager(this);
|
||||||
|
// FIXME pre-fetching messes with read state, find better solution #1289
|
||||||
|
layoutManager.setItemPrefetchEnabled(false);
|
||||||
list.setLayoutManager(layoutManager);
|
list.setLayoutManager(layoutManager);
|
||||||
adapter = createAdapter(layoutManager);
|
adapter = createAdapter(layoutManager);
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ buildscript {
|
|||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.1.2'
|
classpath 'com.android.tools.build:gradle:3.1.2'
|
||||||
classpath 'net.ltgt.gradle:gradle-apt-plugin:0.9'
|
classpath 'net.ltgt.gradle:gradle-apt-plugin:0.9'
|
||||||
classpath 'de.undercouch:gradle-download-task:3.2.0'
|
|
||||||
classpath 'ru.vyarus:gradle-animalsniffer-plugin:1.4.3'
|
classpath 'ru.vyarus:gradle-animalsniffer-plugin:1.4.3'
|
||||||
classpath files('libs/gradle-witness.jar')
|
classpath files('libs/gradle-witness.jar')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user