Compare commits

..

1 Commits

Author SHA1 Message Date
akwizgran
2bf3736b2d Avoid using the deprecated NetworkInfo API. 2020-06-29 15:54:16 +01:00
236 changed files with 2922 additions and 6875 deletions

View File

@@ -1,5 +1,8 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings>
<JavaCodeStyleSettings> <JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="1" /> <option name="ANNOTATION_PARAMETER_WRAP" value="1" />
<option name="IMPORT_LAYOUT_TABLE"> <option name="IMPORT_LAYOUT_TABLE">
@@ -28,20 +31,6 @@
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" /> <option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
</JavaCodeStyleSettings> </JavaCodeStyleSettings>
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<XML> <XML>

View File

@@ -1,9 +0,0 @@
Translations for this project are managed through Transifex:
https://transifex.com/otf/briar
If you'd like to volunteer as a translator, please create a Transifex account and request to be
added to the project's translation team. The Localization Lab has some instructions and advice for
translators here:
https://wiki.localizationlab.org/index.php/Briar

View File

@@ -3,4 +3,3 @@ gen
build build
.settings .settings
src/main/res/raw/*.zip src/main/res/raw/*.zip
src/main/jniLibs

View File

@@ -5,14 +5,14 @@ apply plugin: 'witness'
apply from: 'witness.gradle' apply from: 'witness.gradle'
android { android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion 29
buildToolsVersion rootProject.ext.buildToolsVersion buildToolsVersion '29.0.2'
defaultConfig { defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion 28
versionCode rootProject.ext.versionCode versionCode 10207
versionName rootProject.ext.versionName versionName "1.2.7"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -53,12 +53,10 @@ dependencies {
} }
def torBinariesDir = 'src/main/res/raw' def torBinariesDir = 'src/main/res/raw'
def torLibsDir = 'src/main/jniLibs'
task cleanTorBinaries { task cleanTorBinaries {
doLast { doLast {
delete fileTree(torBinariesDir) { include '*.zip' } delete fileTree(torBinariesDir) { include '*.zip' }
delete fileTree(torLibsDir) { include '**/*.so' }
} }
} }
@@ -69,36 +67,8 @@ task unpackTorBinaries {
copy { copy {
from configurations.tor.collect { zipTree(it) } from configurations.tor.collect { zipTree(it) }
into torBinariesDir into torBinariesDir
include 'geoip.zip' // TODO: Remove after next Tor upgrade, which won't include non-PIE binaries
} include 'geoip.zip', '*_pie.zip'
configurations.tor.each { outer ->
zipTree(outer).each { inner ->
if (inner.name.endsWith('_arm_pie.zip')) {
copy {
from zipTree(inner)
into torLibsDir
rename '(.*)', 'armeabi-v7a/lib$1.so'
}
} else if (inner.name.endsWith('_arm64_pie.zip')) {
copy {
from zipTree(inner)
into torLibsDir
rename '(.*)', 'arm64-v8a/lib$1.so'
}
} else if (inner.name.endsWith('_x86_pie.zip')) {
copy {
from zipTree(inner)
into torLibsDir
rename '(.*)', 'x86/lib$1.so'
}
} else if (inner.name.endsWith('_x86_64_pie.zip')) {
copy {
from zipTree(inner)
into torLibsDir
rename '(.*)', 'x86_64/lib$1.so'
}
}
}
} }
} }
dependsOn cleanTorBinaries dependsOn cleanTorBinaries
@@ -106,6 +76,5 @@ task unpackTorBinaries {
tasks.withType(MergeResources) { tasks.withType(MergeResources) {
inputs.dir torBinariesDir inputs.dir torBinariesDir
inputs.dir torLibsDir
dependsOn unpackTorBinaries dependsOn unpackTorBinaries
} }

View File

@@ -16,8 +16,6 @@
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true"> android:supportsRtl="true">
<receiver android:name=".system.AlarmReceiver" />
</application> </application>
</manifest> </manifest>

View File

@@ -6,8 +6,6 @@ import org.briarproject.bramble.plugin.tor.CircumventionModule;
import org.briarproject.bramble.reporting.ReportingModule; import org.briarproject.bramble.reporting.ReportingModule;
import org.briarproject.bramble.socks.SocksModule; import org.briarproject.bramble.socks.SocksModule;
import org.briarproject.bramble.system.AndroidSystemModule; import org.briarproject.bramble.system.AndroidSystemModule;
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
import dagger.Module; import dagger.Module;
@@ -15,8 +13,6 @@ import dagger.Module;
AndroidBatteryModule.class, AndroidBatteryModule.class,
AndroidNetworkModule.class, AndroidNetworkModule.class,
AndroidSystemModule.class, AndroidSystemModule.class,
AndroidTaskSchedulerModule.class,
AndroidWakefulIoExecutorModule.class,
CircumventionModule.class, CircumventionModule.class,
ReportingModule.class, ReportingModule.class,
SocksModule.class SocksModule.class

View File

@@ -1,8 +0,0 @@
package org.briarproject.bramble;
import org.briarproject.bramble.api.system.AlarmListener;
public interface BrambleAppComponent {
AlarmListener alarmListener();
}

View File

@@ -1,6 +0,0 @@
package org.briarproject.bramble;
public interface BrambleApplication {
BrambleAppComponent getBrambleAppComponent();
}

View File

@@ -1,11 +0,0 @@
package org.briarproject.bramble.api.system;
import android.content.Intent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface AlarmListener {
void onAlarm(Intent intent);
}

View File

@@ -1,19 +0,0 @@
package org.briarproject.bramble.api.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface AndroidWakeLock {
/**
* Acquires the wake lock. This has no effect if the wake lock has already
* been acquired.
*/
void acquire();
/**
* Releases the wake lock. This has no effect if the wake lock has already
* been released.
*/
void release();
}

View File

@@ -1,38 +0,0 @@
package org.briarproject.bramble.api.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.concurrent.Executor;
@NotNullByDefault
public interface AndroidWakeLockManager {
/**
* Creates a wake lock with the given tag. The tag is only used for
* logging; the underlying OS wake lock will use its own tag.
*/
AndroidWakeLock createWakeLock(String tag);
/**
* Runs the given task while holding a wake lock.
*/
void runWakefully(Runnable r, String tag);
/**
* Submits the given task to the given executor while holding a wake lock.
* The lock is released when the task completes, or if an exception is
* thrown while submitting or running the task.
*/
void executeWakefully(Runnable r, Executor executor, String tag);
/**
* Starts a dedicated thread to run the given task asynchronously. A wake
* lock is acquired before starting the thread and released when the task
* completes, or if an exception is thrown while starting the thread or
* running the task.
* <p>
* This method should only be used for lifecycle management tasks that
* can't be run on an executor.
*/
void executeWakefully(Runnable r, String tag);
}

View File

@@ -6,20 +6,21 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.network.NetworkStatus; import org.briarproject.bramble.api.network.NetworkStatus;
import org.briarproject.bramble.api.network.event.NetworkStatusEvent; import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
import java.util.concurrent.Executor; import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@@ -33,12 +34,15 @@ import static android.content.Intent.ACTION_SCREEN_OFF;
import static android.content.Intent.ACTION_SCREEN_ON; import static android.content.Intent.ACTION_SCREEN_ON;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION; import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED; import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -51,23 +55,24 @@ class AndroidNetworkManager implements NetworkManager, Service {
private static final String WIFI_AP_STATE_CHANGED_ACTION = private static final String WIFI_AP_STATE_CHANGED_ACTION =
"android.net.wifi.WIFI_AP_STATE_CHANGED"; "android.net.wifi.WIFI_AP_STATE_CHANGED";
private final TaskScheduler scheduler; private final ScheduledExecutorService scheduler;
private final EventBus eventBus; private final EventBus eventBus;
private final Executor eventExecutor; private final Application app;
private final Context appContext; private final ConnectivityManager connectivityManager;
private final AtomicReference<Cancellable> connectivityCheck = private final AtomicReference<Future<?>> connectivityCheck =
new AtomicReference<>(); new AtomicReference<>();
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private volatile BroadcastReceiver networkStateReceiver = null; private volatile BroadcastReceiver networkStateReceiver = null;
@Inject @Inject
AndroidNetworkManager(TaskScheduler scheduler, EventBus eventBus, AndroidNetworkManager(@Scheduler ScheduledExecutorService scheduler,
@EventExecutor Executor eventExecutor, Application app) { EventBus eventBus, Application app) {
this.scheduler = scheduler; this.scheduler = scheduler;
this.eventBus = eventBus; this.eventBus = eventBus;
this.eventExecutor = eventExecutor; this.app = app;
this.appContext = app.getApplicationContext(); connectivityManager = (ConnectivityManager)
requireNonNull(app.getSystemService(CONNECTIVITY_SERVICE));
} }
@Override @Override
@@ -82,24 +87,33 @@ class AndroidNetworkManager implements NetworkManager, Service {
filter.addAction(WIFI_AP_STATE_CHANGED_ACTION); filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); filter.addAction(WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED); if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
appContext.registerReceiver(networkStateReceiver, filter); app.registerReceiver(networkStateReceiver, filter);
} }
@Override @Override
public void stopService() { public void stopService() {
if (networkStateReceiver != null) if (networkStateReceiver != null) {
appContext.unregisterReceiver(networkStateReceiver); app.unregisterReceiver(networkStateReceiver);
}
} }
@Override @Override
public NetworkStatus getNetworkStatus() { public NetworkStatus getNetworkStatus() {
ConnectivityManager cm = (ConnectivityManager) if (SDK_INT >= 23) {
appContext.getSystemService(CONNECTIVITY_SERVICE); Network net = connectivityManager.getActiveNetwork();
if (cm == null) throw new AssertionError(); if (net == null) return new NetworkStatus(false, false);
NetworkInfo net = cm.getActiveNetworkInfo(); NetworkCapabilities caps =
boolean connected = net != null && net.isConnected(); connectivityManager.getNetworkCapabilities(net);
boolean wifi = connected && net.getType() == TYPE_WIFI; if (caps == null) return new NetworkStatus(false, false);
return new NetworkStatus(connected, wifi); boolean connected = caps.hasCapability(NET_CAPABILITY_INTERNET);
boolean wifi = caps.hasTransport(TRANSPORT_WIFI);
return new NetworkStatus(connected, wifi);
} else {
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
boolean connected = net != null && net.isConnected();
boolean wifi = connected && net.getType() == TYPE_WIFI;
return new NetworkStatus(connected, wifi);
}
} }
private void updateConnectionStatus() { private void updateConnectionStatus() {
@@ -107,12 +121,11 @@ class AndroidNetworkManager implements NetworkManager, Service {
} }
private void scheduleConnectionStatusUpdate(int delay, TimeUnit unit) { private void scheduleConnectionStatusUpdate(int delay, TimeUnit unit) {
Cancellable newConnectivityCheck = Future<?> newConnectivityCheck =
scheduler.schedule(this::updateConnectionStatus, eventExecutor, scheduler.schedule(this::updateConnectionStatus, delay, unit);
delay, unit); Future<?> oldConnectivityCheck =
Cancellable oldConnectivityCheck =
connectivityCheck.getAndSet(newConnectivityCheck); connectivityCheck.getAndSet(newConnectivityCheck);
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(); if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(false);
} }
private class NetworkStateReceiver extends BroadcastReceiver { private class NetworkStateReceiver extends BroadcastReceiver {

View File

@@ -1,36 +0,0 @@
package org.briarproject.bramble.plugin.bluetooth;
import android.bluetooth.BluetoothSocket;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import java.io.IOException;
@NotNullByDefault
class AndroidBluetoothConnectionFactory
implements BluetoothConnectionFactory<BluetoothSocket> {
private final BluetoothConnectionLimiter connectionLimiter;
private final AndroidWakeLockManager wakeLockManager;
private final TimeoutMonitor timeoutMonitor;
AndroidBluetoothConnectionFactory(
BluetoothConnectionLimiter connectionLimiter,
AndroidWakeLockManager wakeLockManager,
TimeoutMonitor timeoutMonitor) {
this.connectionLimiter = connectionLimiter;
this.wakeLockManager = wakeLockManager;
this.timeoutMonitor = timeoutMonitor;
}
@Override
public DuplexTransportConnection wrapSocket(DuplexPlugin plugin,
BluetoothSocket s) throws IOException {
return new AndroidBluetoothTransportConnection(plugin,
connectionLimiter, wakeLockManager, timeoutMonitor, s);
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import android.app.Application;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothServerSocket;
@@ -10,6 +9,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -31,6 +31,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -59,40 +60,35 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class AndroidBluetoothPlugin class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
extends BluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
private static final Logger LOG = private static final Logger LOG =
getLogger(AndroidBluetoothPlugin.class.getName()); getLogger(AndroidBluetoothPlugin.class.getName());
private static final int MAX_DISCOVERY_MS = 10_000; private static final int MAX_DISCOVERY_MS = 10_000;
private final ScheduledExecutorService scheduler;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final Application app; private final Context appContext;
private final Clock clock; private final Clock clock;
private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null; private volatile BluetoothStateReceiver receiver = null;
// Non-null if the plugin started successfully // Non-null if the plugin started successfully
private volatile BluetoothAdapter adapter = null; private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
BluetoothConnectionFactory<BluetoothSocket> connectionFactory, TimeoutMonitor timeoutMonitor, Executor ioExecutor,
Executor ioExecutor, SecureRandom secureRandom, ScheduledExecutorService scheduler,
Executor wakefulIoExecutor, AndroidExecutor androidExecutor, Context appContext, Clock clock,
SecureRandom secureRandom, Backoff backoff, PluginCallback callback, int maxLatency,
AndroidExecutor androidExecutor,
Application app,
Clock clock,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime) { int maxIdleTime) {
super(connectionLimiter, connectionFactory, ioExecutor, super(connectionLimiter, timeoutMonitor, ioExecutor, secureRandom,
wakefulIoExecutor, secureRandom, backoff, callback, backoff, callback, maxLatency, maxIdleTime);
maxLatency, maxIdleTime); this.scheduler = scheduler;
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.app = app; this.appContext = appContext;
this.clock = clock; this.clock = clock;
} }
@@ -104,13 +100,13 @@ class AndroidBluetoothPlugin
filter.addAction(ACTION_STATE_CHANGED); filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED); filter.addAction(ACTION_SCAN_MODE_CHANGED);
receiver = new BluetoothStateReceiver(); receiver = new BluetoothStateReceiver();
app.registerReceiver(receiver, filter); appContext.registerReceiver(receiver, filter);
} }
@Override @Override
public void stop() { public void stop() {
super.stop(); super.stop();
if (receiver != null) app.unregisterReceiver(receiver); if (receiver != null) appContext.unregisterReceiver(receiver);
} }
@Override @Override
@@ -132,11 +128,42 @@ class AndroidBluetoothPlugin
return adapter != null && adapter.isEnabled(); return adapter != null && adapter.isEnabled();
} }
@Override
void enableAdapter() {
if (adapter != null && !adapter.isEnabled()) {
if (adapter.enable()) {
LOG.info("Enabling Bluetooth");
wasEnabledByUs = true;
} else {
LOG.info("Could not enable Bluetooth");
}
}
}
@Override
void disableAdapterIfEnabledByUs() {
if (isAdapterEnabled() && wasEnabledByUs) {
if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth");
wasEnabledByUs = false;
}
}
@Override
void setEnabledByUs() {
wasEnabledByUs = true;
}
@Override
void onAdapterDisabled() {
super.onAdapterDisabled();
wasEnabledByUs = false;
}
@Override @Override
@Nullable @Nullable
String getBluetoothAddress() { String getBluetoothAddress() {
if (adapter == null) return null; String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
String address = AndroidUtils.getBluetoothAddress(app, adapter);
return address.isEmpty() ? null : address; return address.isEmpty() ? null : address;
} }
@@ -154,7 +181,13 @@ class AndroidBluetoothPlugin
@Override @Override
DuplexTransportConnection acceptConnection(BluetoothServerSocket ss) DuplexTransportConnection acceptConnection(BluetoothServerSocket ss)
throws IOException { throws IOException {
return connectionFactory.wrapSocket(this, ss.accept()); return wrapSocket(ss.accept());
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s)
throws IOException {
return new AndroidBluetoothTransportConnection(this, connectionLimiter,
timeoutMonitor, appContext, scheduler, s);
} }
@Override @Override
@@ -171,7 +204,7 @@ class AndroidBluetoothPlugin
try { try {
s = d.createInsecureRfcommSocketToServiceRecord(u); s = d.createInsecureRfcommSocketToServiceRecord(u);
s.connect(); s.connect();
return connectionFactory.wrapSocket(this, s); return wrapSocket(s);
} catch (IOException e) { } catch (IOException e) {
IoUtils.tryToClose(s, LOG, WARNING); IoUtils.tryToClose(s, LOG, WARNING);
throw e; throw e;
@@ -206,7 +239,7 @@ class AndroidBluetoothPlugin
filter.addAction(ACTION_DISCOVERY_STARTED); filter.addAction(ACTION_DISCOVERY_STARTED);
filter.addAction(ACTION_DISCOVERY_FINISHED); filter.addAction(ACTION_DISCOVERY_FINISHED);
filter.addAction(ACTION_FOUND); filter.addAction(ACTION_FOUND);
app.registerReceiver(receiver, filter); appContext.registerReceiver(receiver, filter);
try { try {
if (adapter.startDiscovery()) { if (adapter.startDiscovery()) {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
@@ -243,7 +276,7 @@ class AndroidBluetoothPlugin
} finally { } finally {
LOG.info("Cancelling discovery"); LOG.info("Cancelling discovery");
adapter.cancelDiscovery(); adapter.cancelDiscovery();
app.unregisterReceiver(receiver); appContext.unregisterReceiver(receiver);
} }
// Shuffle the addresses so we don't always try the same one first // Shuffle the addresses so we don't always try the same one first
shuffle(addresses); shuffle(addresses);

View File

@@ -1,11 +1,9 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import android.app.Application; import android.content.Context;
import android.bluetooth.BluetoothSocket;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor; import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -14,15 +12,13 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
@@ -36,32 +32,25 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final AndroidWakeLockManager wakeLockManager; private final Context appContext;
private final Application app;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final EventBus eventBus; private final EventBus eventBus;
private final Clock clock; private final Clock clock;
private final TimeoutMonitor timeoutMonitor; private final TimeoutMonitor timeoutMonitor;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
@Inject public AndroidBluetoothPluginFactory(Executor ioExecutor,
public AndroidBluetoothPluginFactory(@IoExecutor Executor ioExecutor, ScheduledExecutorService scheduler,
@WakefulIoExecutor Executor wakefulIoExecutor, AndroidExecutor androidExecutor, Context appContext,
AndroidExecutor androidExecutor, SecureRandom secureRandom, EventBus eventBus, Clock clock,
AndroidWakeLockManager wakeLockManager, TimeoutMonitor timeoutMonitor, BackoffFactory backoffFactory) {
Application app,
SecureRandom secureRandom,
EventBus eventBus,
Clock clock,
TimeoutMonitor timeoutMonitor,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor; this.scheduler = scheduler;
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.wakeLockManager = wakeLockManager; this.appContext = appContext;
this.app = app;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.eventBus = eventBus; this.eventBus = eventBus;
this.clock = clock; this.clock = clock;
@@ -83,15 +72,12 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(PluginCallback callback) { public DuplexPlugin createPlugin(PluginCallback callback) {
BluetoothConnectionLimiter connectionLimiter = BluetoothConnectionLimiter connectionLimiter =
new BluetoothConnectionLimiterImpl(eventBus); new BluetoothConnectionLimiterImpl(eventBus);
BluetoothConnectionFactory<BluetoothSocket> connectionFactory =
new AndroidBluetoothConnectionFactory(connectionLimiter,
wakeLockManager, timeoutMonitor);
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin( AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, connectionFactory, ioExecutor, connectionLimiter, timeoutMonitor, ioExecutor, secureRandom,
wakefulIoExecutor, secureRandom, androidExecutor, app, scheduler, androidExecutor, appContext, clock, backoff,
clock, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME); callback, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -1,19 +1,26 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.PowerManager;
import org.briarproject.bramble.api.io.TimeoutMonitor; import org.briarproject.bramble.api.io.TimeoutMonitor;
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.duplex.AbstractDuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidWakeLock; import org.briarproject.bramble.util.RenewableWakeLock;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
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.concurrent.ScheduledExecutorService;
import static android.content.Context.POWER_SERVICE;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.util.AndroidUtils.getWakeLockTag;
import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress; import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress;
@NotNullByDefault @NotNullByDefault
@@ -21,21 +28,25 @@ class AndroidBluetoothTransportConnection
extends AbstractDuplexTransportConnection { extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionLimiter; private final BluetoothConnectionLimiter connectionLimiter;
private final RenewableWakeLock wakeLock;
private final BluetoothSocket socket; private final BluetoothSocket socket;
private final InputStream in; private final InputStream in;
private final AndroidWakeLock wakeLock;
AndroidBluetoothTransportConnection(Plugin plugin, AndroidBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionLimiter, BluetoothConnectionLimiter connectionLimiter,
AndroidWakeLockManager wakeLockManager, TimeoutMonitor timeoutMonitor, Context appContext,
TimeoutMonitor timeoutMonitor, ScheduledExecutorService scheduler, BluetoothSocket socket)
BluetoothSocket socket) throws IOException { throws IOException {
super(plugin); super(plugin);
this.connectionLimiter = connectionLimiter; this.connectionLimiter = connectionLimiter;
this.socket = socket; this.socket = socket;
in = timeoutMonitor.createTimeoutInputStream( in = timeoutMonitor.createTimeoutInputStream(
socket.getInputStream(), plugin.getMaxIdleTime() * 2); socket.getInputStream(), plugin.getMaxIdleTime() * 2);
wakeLock = wakeLockManager.createWakeLock("BluetoothConnection"); PowerManager powerManager = (PowerManager)
requireNonNull(appContext.getSystemService(POWER_SERVICE));
String tag = getWakeLockTag(appContext);
wakeLock = new RenewableWakeLock(powerManager, scheduler,
PARTIAL_WAKE_LOCK, tag, 1, MINUTES);
wakeLock.acquire(); wakeLock.acquire();
String address = socket.getRemoteDevice().getAddress(); String address = socket.getRemoteDevice().getAddress();
if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address); if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address);
@@ -55,7 +66,6 @@ class AndroidBluetoothTransportConnection
protected void closeConnection(boolean exception) throws IOException { protected void closeConnection(boolean exception) throws IOException {
try { try {
socket.close(); socket.close();
in.close();
} finally { } finally {
wakeLock.release(); wakeLock.release();
connectionLimiter.connectionClosed(this); connectionLimiter.connectionClosed(this);

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Application; import android.content.Context;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.LinkAddress; import android.net.LinkAddress;
import android.net.LinkProperties; import android.net.LinkProperties;
@@ -42,8 +42,6 @@ import static java.util.Collections.list;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.DEFAULT_PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.IoUtils.tryToClose;
@@ -62,22 +60,20 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
private volatile SocketFactory socketFactory; private volatile SocketFactory socketFactory;
AndroidLanTcpPlugin(Executor ioExecutor, AndroidLanTcpPlugin(Executor ioExecutor, Context appContext,
Executor wakefulIoExecutor, Backoff backoff, PluginCallback callback, int maxLatency,
Application app, int maxIdleTime, int connectionTimeout) {
Backoff backoff, super(ioExecutor, backoff, callback, maxLatency, maxIdleTime,
PluginCallback callback, connectionTimeout);
int maxLatency,
int maxIdleTime,
int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,
maxIdleTime, connectionTimeout);
// Don't execute more than one connection status check at a time // Don't execute more than one connection status check at a time
connectionStatusExecutor = connectionStatusExecutor =
new PoliteExecutor("AndroidLanTcpPlugin", ioExecutor, 1); new PoliteExecutor("AndroidLanTcpPlugin", ioExecutor, 1);
connectivityManager = (ConnectivityManager) ConnectivityManager connectivityManager = (ConnectivityManager)
requireNonNull(app.getSystemService(CONNECTIVITY_SERVICE)); appContext.getSystemService(CONNECTIVITY_SERVICE);
wifiManager = (WifiManager) app.getSystemService(WIFI_SERVICE); if (connectivityManager == null) throw new AssertionError();
this.connectivityManager = connectivityManager;
wifiManager = (WifiManager) appContext.getApplicationContext()
.getSystemService(WIFI_SERVICE);
socketFactory = SocketFactory.getDefault(); socketFactory = SocketFactory.getDefault();
} }
@@ -86,8 +82,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
initialisePortProperty(); initialisePortProperty();
Settings settings = callback.getSettings(); Settings settings = callback.getSettings();
state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, false));
DEFAULT_PREF_PLUGIN_ENABLE));
updateConnectionStatus(); updateConnectionStatus();
} }
@@ -277,11 +272,11 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
// make outgoing connections on API 21+ if another network // make outgoing connections on API 21+ if another network
// has internet access // has internet access
socketFactory = SocketFactory.getDefault(); socketFactory = SocketFactory.getDefault();
bind(); if (s == INACTIVE) bind();
} else { } else {
LOG.info("Connected to wifi"); LOG.info("Connected to wifi");
socketFactory = getSocketFactory(); socketFactory = getSocketFactory();
bind(); if (s == INACTIVE) bind();
} }
}); });
} }

View File

@@ -1,9 +1,8 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
import android.app.Application; import android.content.Context;
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.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -11,12 +10,10 @@ import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID; import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@@ -31,22 +28,17 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
private final Application app; private final Context appContext;
@Inject public AndroidLanTcpPluginFactory(Executor ioExecutor, EventBus eventBus,
public AndroidLanTcpPluginFactory(@IoExecutor Executor ioExecutor, BackoffFactory backoffFactory, Context appContext) {
@WakefulIoExecutor Executor wakefulIoExecutor,
EventBus eventBus,
BackoffFactory backoffFactory,
Application app) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
this.app = app; this.appContext = appContext;
} }
@Override @Override
@@ -64,8 +56,8 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor, AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor,
wakefulIoExecutor, app, backoff, callback, appContext, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME,
MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT); CONNECTION_TIMEOUT);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -1,9 +1,10 @@
package org.briarproject.bramble.plugin.tor; package org.briarproject.bramble.plugin.tor;
import android.app.Application; import android.content.Context;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.os.PowerManager;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
@@ -11,76 +12,49 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.system.AndroidWakeLock;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.RenewableWakeLock;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.concurrent.ScheduledExecutorService;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import static android.os.Build.VERSION.SDK_INT; import static android.content.Context.MODE_PRIVATE;
import static java.util.Arrays.asList; import static android.content.Context.POWER_SERVICE;
import static java.util.logging.Level.INFO; import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.logging.Logger.getLogger; import static java.util.concurrent.TimeUnit.MINUTES;
import static org.briarproject.bramble.util.AndroidUtils.getWakeLockTag;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class AndroidTorPlugin extends TorPlugin { class AndroidTorPlugin extends TorPlugin {
private static final List<String> LIBRARY_ARCHITECTURES = private final Context appContext;
asList("armeabi-v7a", "arm64-v8a", "x86", "x86_64"); private final RenewableWakeLock wakeLock;
private static final String TOR_LIB_NAME = "libtor.so"; AndroidTorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
private static final String OBFS4_LIB_NAME = "libobfs4proxy.so"; Context appContext, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory,
private static final Logger LOG = Clock clock, ResourceProvider resourceProvider,
getLogger(AndroidTorPlugin.class.getName());
private final Application app;
private final AndroidWakeLock wakeLock;
private final File torLib, obfs4Lib;
AndroidTorPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
Application app,
NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, BatteryManager batteryManager, Backoff backoff,
AndroidWakeLockManager wakeLockManager,
Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, PluginCallback callback, String architecture, int maxLatency,
String architecture, int maxIdleTime) {
int maxLatency, super(ioExecutor, networkManager, locationUtils, torSocketFactory,
int maxIdleTime, clock, resourceProvider, circumventionProvider, batteryManager,
File torDirectory) { backoff, torRendezvousCrypto, callback, architecture, maxLatency, maxIdleTime,
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils, appContext.getDir("tor", MODE_PRIVATE));
torSocketFactory, clock, resourceProvider, this.appContext = appContext;
circumventionProvider, batteryManager, backoff, PowerManager pm = (PowerManager)
torRendezvousCrypto, callback, architecture, maxLatency, appContext.getSystemService(POWER_SERVICE);
maxIdleTime, torDirectory); if (pm == null) throw new AssertionError();
this.app = app; wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK,
wakeLock = wakeLockManager.createWakeLock("TorPlugin"); getWakeLockTag(appContext), 1, MINUTES);
String nativeLibDir = app.getApplicationInfo().nativeLibraryDir;
torLib = new File(nativeLibDir, TOR_LIB_NAME);
obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME);
} }
@Override @Override
@@ -91,8 +65,8 @@ class AndroidTorPlugin extends TorPlugin {
@Override @Override
protected long getLastUpdateTime() { protected long getLastUpdateTime() {
try { try {
PackageManager pm = app.getPackageManager(); PackageManager pm = appContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(app.getPackageName(), 0); PackageInfo pi = pm.getPackageInfo(appContext.getPackageName(), 0);
return pi.lastUpdateTime; return pi.lastUpdateTime;
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
throw new AssertionError(e); throw new AssertionError(e);
@@ -111,112 +85,4 @@ class AndroidTorPlugin extends TorPlugin {
super.stop(); super.stop();
wakeLock.release(); wakeLock.release();
} }
@Override
protected File getTorExecutableFile() {
return torLib.exists() ? torLib : super.getTorExecutableFile();
}
@Override
protected File getObfs4ExecutableFile() {
return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile();
}
@Override
protected void installTorExecutable() throws IOException {
File extracted = super.getTorExecutableFile();
if (torLib.exists()) {
// If an older version left behind a Tor binary, delete it
if (extracted.exists()) {
if (extracted.delete()) LOG.info("Deleted Tor binary");
else LOG.info("Failed to delete Tor binary");
}
} else if (SDK_INT < 29) {
// The binary wasn't extracted at install time. Try to extract it
extractLibraryFromApk(TOR_LIB_NAME, extracted);
} else {
// No point extracting the binary, we won't be allowed to execute it
throw new FileNotFoundException(torLib.getAbsolutePath());
}
}
@Override
protected void installObfs4Executable() throws IOException {
File extracted = super.getObfs4ExecutableFile();
if (obfs4Lib.exists()) {
// If an older version left behind an obfs4 binary, delete it
if (extracted.exists()) {
if (extracted.delete()) LOG.info("Deleted obfs4 binary");
else LOG.info("Failed to delete obfs4 binary");
}
} else if (SDK_INT < 29) {
// The binary wasn't extracted at install time. Try to extract it
extractLibraryFromApk(OBFS4_LIB_NAME, extracted);
} else {
// No point extracting the binary, we won't be allowed to execute it
throw new FileNotFoundException(obfs4Lib.getAbsolutePath());
}
}
private void extractLibraryFromApk(String libName, File dest)
throws IOException {
File sourceDir = new File(app.getApplicationInfo().sourceDir);
if (sourceDir.isFile()) {
// Look for other APK files in the same directory, if we're allowed
File parent = sourceDir.getParentFile();
if (parent != null) sourceDir = parent;
}
List<String> libPaths = getSupportedLibraryPaths(libName);
for (File apk : findApkFiles(sourceDir)) {
ZipInputStream zin = new ZipInputStream(new FileInputStream(apk));
for (ZipEntry e = zin.getNextEntry(); e != null;
e = zin.getNextEntry()) {
if (libPaths.contains(e.getName())) {
if (LOG.isLoggable(INFO)) {
LOG.info("Extracting " + e.getName()
+ " from " + apk.getAbsolutePath());
}
extract(zin, dest); // Zip input stream will be closed
return;
}
}
zin.close();
}
throw new FileNotFoundException(libName);
}
/**
* Returns all files with the extension .apk or .APK under the given root.
*/
private List<File> findApkFiles(File root) {
List<File> files = new ArrayList<>();
findApkFiles(root, files);
return files;
}
private void findApkFiles(File f, List<File> files) {
if (f.isFile() && f.getName().toLowerCase().endsWith(".apk")) {
files.add(f);
} else if (f.isDirectory()) {
File[] children = f.listFiles();
if (children != null) {
for (File child : children) findApkFiles(child, files);
}
}
}
/**
* Returns the paths at which libraries with the given name would be found
* inside an APK file, for all architectures supported by the device, in
* order of preference.
*/
private List<String> getSupportedLibraryPaths(String libName) {
List<String> architectures = new ArrayList<>();
for (String abi : AndroidUtils.getSupportedArchitectures()) {
if (LIBRARY_ARCHITECTURES.contains(abi)) {
architectures.add("lib/" + abi + "/" + libName);
}
}
return architectures;
}
} }

View File

@@ -1,33 +1,28 @@
package org.briarproject.bramble.plugin.tor; package org.briarproject.bramble.plugin.tor;
import android.app.Application; import android.content.Context;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
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.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.TorDirectory;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.AndroidUtils;
import java.io.File;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.net.SocketFactory; import javax.net.SocketFactory;
@Immutable @Immutable
@@ -43,8 +38,9 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final Application app; private final ScheduledExecutorService scheduler;
private final Context appContext;
private final NetworkManager networkManager; private final NetworkManager networkManager;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final EventBus eventBus; private final EventBus eventBus;
@@ -53,28 +49,18 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
private final ResourceProvider resourceProvider; private final ResourceProvider resourceProvider;
private final CircumventionProvider circumventionProvider; private final CircumventionProvider circumventionProvider;
private final BatteryManager batteryManager; private final BatteryManager batteryManager;
private final AndroidWakeLockManager wakeLockManager;
private final Clock clock; private final Clock clock;
private final File torDirectory;
@Inject public AndroidTorPluginFactory(Executor ioExecutor,
public AndroidTorPluginFactory(@IoExecutor Executor ioExecutor, ScheduledExecutorService scheduler, Context appContext,
@WakefulIoExecutor Executor wakefulIoExecutor, NetworkManager networkManager, LocationUtils locationUtils,
Application app, EventBus eventBus, SocketFactory torSocketFactory,
NetworkManager networkManager, BackoffFactory backoffFactory, ResourceProvider resourceProvider,
LocationUtils locationUtils,
EventBus eventBus,
SocketFactory torSocketFactory,
BackoffFactory backoffFactory,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, BatteryManager batteryManager, Clock clock) {
AndroidWakeLockManager wakeLockManager,
Clock clock,
@TorDirectory File torDirectory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor; this.scheduler = scheduler;
this.app = app; this.appContext = appContext;
this.networkManager = networkManager; this.networkManager = networkManager;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.eventBus = eventBus; this.eventBus = eventBus;
@@ -83,9 +69,7 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
this.resourceProvider = resourceProvider; this.resourceProvider = resourceProvider;
this.circumventionProvider = circumventionProvider; this.circumventionProvider = circumventionProvider;
this.batteryManager = batteryManager; this.batteryManager = batteryManager;
this.wakeLockManager = wakeLockManager;
this.clock = clock; this.clock = clock;
this.torDirectory = torDirectory;
} }
@Override @Override
@@ -128,12 +112,11 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl(); TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, scheduler,
wakefulIoExecutor, app, networkManager, locationUtils, appContext, networkManager, locationUtils, torSocketFactory,
torSocketFactory, clock, resourceProvider, clock, resourceProvider, circumventionProvider, batteryManager,
circumventionProvider, batteryManager, wakeLockManager,
backoff, torRendezvousCrypto, callback, architecture, backoff, torRendezvousCrypto, callback, architecture,
MAX_LATENCY, MAX_IDLE_TIME, torDirectory); MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
interface AlarmConstants {
/**
* Request code for the broadcast intent attached to the periodic alarm.
*/
int REQUEST_ALARM = 1;
/**
* Key for storing the process ID in the extras of the periodic alarm's
* intent. This allows us to ignore alarms scheduled by dead processes.
*/
String EXTRA_PID = "org.briarproject.bramble.EXTRA_PID";
}

View File

@@ -1,17 +0,0 @@
package org.briarproject.bramble.system;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import org.briarproject.bramble.BrambleApplication;
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
BrambleApplication app =
(BrambleApplication) ctx.getApplicationContext();
app.getBrambleAppComponent().alarmListener().onAlarm(intent);
}
}

View File

@@ -1,17 +1,12 @@
package org.briarproject.bramble.system; package org.briarproject.bramble.system;
import org.briarproject.bramble.api.event.EventExecutor; import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -21,23 +16,6 @@ import dagger.Provides;
@Module @Module
public class AndroidSystemModule { public class AndroidSystemModule {
private final ScheduledExecutorService scheduledExecutorService;
public AndroidSystemModule() {
// Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy =
new ScheduledThreadPoolExecutor.DiscardPolicy();
scheduledExecutorService = new ScheduledThreadPoolExecutor(1, policy);
}
@Provides
@Singleton
ScheduledExecutorService provideScheduledExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(scheduledExecutorService);
return scheduledExecutorService;
}
@Provides @Provides
@Singleton @Singleton
SecureRandomProvider provideSecureRandomProvider( SecureRandomProvider provideSecureRandomProvider(
@@ -69,11 +47,4 @@ public class AndroidSystemModule {
ResourceProvider provideResourceProvider(AndroidResourceProvider provider) { ResourceProvider provideResourceProvider(AndroidResourceProvider provider) {
return provider; return provider;
} }
@Provides
@Singleton
AndroidWakeLockManager provideWakeLockManager(
AndroidWakeLockManagerImpl wakeLockManager) {
return wakeLockManager;
}
} }

View File

@@ -1,242 +0,0 @@
package org.briarproject.bramble.system;
import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.Application;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Process;
import android.os.SystemClock;
import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AlarmListener;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.api.system.Wakeful;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.INTERVAL_FIFTEEN_MINUTES;
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
import static android.content.Context.ALARM_SERVICE;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.system.AlarmConstants.EXTRA_PID;
import static org.briarproject.bramble.system.AlarmConstants.REQUEST_ALARM;
@ThreadSafe
@NotNullByDefault
class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener {
private static final Logger LOG =
getLogger(AndroidTaskScheduler.class.getName());
private static final long ALARM_MS = INTERVAL_FIFTEEN_MINUTES;
private final Application app;
private final AndroidWakeLockManager wakeLockManager;
private final ScheduledExecutorService scheduledExecutorService;
private final AlarmManager alarmManager;
private final Object lock = new Object();
@GuardedBy("lock")
private final Queue<ScheduledTask> tasks = new PriorityQueue<>();
AndroidTaskScheduler(Application app,
AndroidWakeLockManager wakeLockManager,
ScheduledExecutorService scheduledExecutorService) {
this.app = app;
this.wakeLockManager = wakeLockManager;
this.scheduledExecutorService = scheduledExecutorService;
alarmManager = (AlarmManager)
requireNonNull(app.getSystemService(ALARM_SERVICE));
}
@Override
public void startService() {
scheduleAlarm();
}
@Override
public void stopService() {
cancelAlarm();
}
@Override
public Cancellable schedule(Runnable task, Executor executor, long delay,
TimeUnit unit) {
AtomicBoolean cancelled = new AtomicBoolean(false);
return schedule(task, executor, delay, unit, cancelled);
}
@Override
public Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
long delay, long interval, TimeUnit unit) {
AtomicBoolean cancelled = new AtomicBoolean(false);
return scheduleWithFixedDelay(task, executor, delay, interval, unit,
cancelled);
}
@Override
public void onAlarm(Intent intent) {
wakeLockManager.runWakefully(() -> {
int extraPid = intent.getIntExtra(EXTRA_PID, -1);
int currentPid = Process.myPid();
if (extraPid == currentPid) {
LOG.info("Alarm");
rescheduleAlarm();
runDueTasks();
} else if (LOG.isLoggable(INFO)) {
LOG.info("Ignoring alarm with PID " + extraPid
+ ", current PID is " + currentPid);
}
}, "TaskAlarm");
}
private Cancellable schedule(Runnable task, Executor executor, long delay,
TimeUnit unit, AtomicBoolean cancelled) {
long now = SystemClock.elapsedRealtime();
long dueMillis = now + MILLISECONDS.convert(delay, unit);
Runnable wakeful = () ->
wakeLockManager.executeWakefully(task, executor, "TaskHandoff");
Future<?> check = scheduleCheckForDueTasks(delay, unit);
ScheduledTask s = new ScheduledTask(wakeful, dueMillis, check,
cancelled);
synchronized (lock) {
tasks.add(s);
}
return s;
}
private Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
long delay, long interval, TimeUnit unit, AtomicBoolean cancelled) {
// All executions of this periodic task share a cancelled flag
Runnable wrapped = () -> {
task.run();
scheduleWithFixedDelay(task, executor, interval, interval, unit,
cancelled);
};
return schedule(wrapped, executor, delay, unit, cancelled);
}
private Future<?> scheduleCheckForDueTasks(long delay, TimeUnit unit) {
Runnable wakeful = () -> wakeLockManager.runWakefully(
this::runDueTasks, "TaskScheduler");
return scheduledExecutorService.schedule(wakeful, delay, unit);
}
@Wakeful
private void runDueTasks() {
long now = SystemClock.elapsedRealtime();
List<ScheduledTask> due = new ArrayList<>();
synchronized (lock) {
while (true) {
ScheduledTask s = tasks.peek();
if (s == null || s.dueMillis > now) break;
due.add(tasks.remove());
}
}
if (LOG.isLoggable(INFO)) {
LOG.info("Running " + due.size() + " due tasks");
}
for (ScheduledTask s : due) {
if (LOG.isLoggable(INFO)) {
LOG.info("Task is " + (now - s.dueMillis) + " ms overdue");
}
s.run();
}
}
private void scheduleAlarm() {
if (SDK_INT >= 23) scheduleIdleAlarm();
else scheduleInexactRepeatingAlarm();
}
private void rescheduleAlarm() {
// If SDK_INT < 23 the alarm repeats automatically
if (SDK_INT >= 23) scheduleIdleAlarm();
}
private void cancelAlarm() {
alarmManager.cancel(getAlarmPendingIntent());
}
private void scheduleInexactRepeatingAlarm() {
alarmManager.setInexactRepeating(ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + ALARM_MS, ALARM_MS,
getAlarmPendingIntent());
}
@TargetApi(23)
private void scheduleIdleAlarm() {
alarmManager.setAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + ALARM_MS,
getAlarmPendingIntent());
}
private PendingIntent getAlarmPendingIntent() {
Intent i = new Intent(app, AlarmReceiver.class);
i.putExtra(EXTRA_PID, android.os.Process.myPid());
return PendingIntent.getBroadcast(app, REQUEST_ALARM, i,
FLAG_CANCEL_CURRENT);
}
private class ScheduledTask
implements Runnable, Cancellable, Comparable<ScheduledTask> {
private final Runnable task;
private final long dueMillis;
private final Future<?> check;
private final AtomicBoolean cancelled;
public ScheduledTask(Runnable task, long dueMillis,
Future<?> check, AtomicBoolean cancelled) {
this.task = task;
this.dueMillis = dueMillis;
this.check = check;
this.cancelled = cancelled;
}
@Override
public void run() {
if (!cancelled.get()) task.run();
}
@Override
public void cancel() {
// Cancel any future executions of this task
cancelled.set(true);
// Cancel the scheduled check for due tasks
check.cancel(false);
// Remove the task from the queue
synchronized (lock) {
tasks.remove(this);
}
}
@Override
public int compareTo(ScheduledTask s) {
//noinspection UseCompareMethod
if (dueMillis < s.dueMillis) return -1;
if (dueMillis > s.dueMillis) return 1;
return 0;
}
}
}

View File

@@ -1,49 +0,0 @@
package org.briarproject.bramble.system;
import android.app.Application;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.AlarmListener;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.TaskScheduler;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class AndroidTaskSchedulerModule {
public static class EagerSingletons {
@Inject
AndroidTaskScheduler scheduler;
}
@Provides
@Singleton
AndroidTaskScheduler provideAndroidTaskScheduler(
LifecycleManager lifecycleManager, Application app,
AndroidWakeLockManager wakeLockManager,
ScheduledExecutorService scheduledExecutorService) {
AndroidTaskScheduler scheduler = new AndroidTaskScheduler(app,
wakeLockManager, scheduledExecutorService);
lifecycleManager.registerService(scheduler);
return scheduler;
}
@Provides
@Singleton
AlarmListener provideAlarmListener(AndroidTaskScheduler scheduler) {
return scheduler;
}
@Provides
@Singleton
TaskScheduler provideTaskScheduler(AndroidTaskScheduler scheduler) {
return scheduler;
}
}

View File

@@ -1,74 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidWakeLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.logging.Level.FINE;
import static java.util.logging.Logger.getLogger;
/**
* A wrapper around a {@link SharedWakeLock} that provides the more convenient
* semantics of {@link AndroidWakeLock} (i.e. calls to acquire() and release()
* don't need to be balanced).
*/
@ThreadSafe
@NotNullByDefault
class AndroidWakeLockImpl implements AndroidWakeLock {
private static final Logger LOG =
getLogger(AndroidWakeLockImpl.class.getName());
private static final AtomicInteger INSTANCE_ID = new AtomicInteger(0);
private final SharedWakeLock sharedWakeLock;
private final String tag;
private final Object lock = new Object();
@GuardedBy("lock")
private boolean held = false;
AndroidWakeLockImpl(SharedWakeLock sharedWakeLock, String tag) {
this.sharedWakeLock = sharedWakeLock;
this.tag = tag + "_" + INSTANCE_ID.getAndIncrement();
}
@Override
public void acquire() {
synchronized (lock) {
if (held) {
if (LOG.isLoggable(FINE)) {
LOG.fine(tag + " already acquired");
}
} else {
if (LOG.isLoggable(FINE)) {
LOG.fine(tag + " acquiring shared wake lock");
}
held = true;
sharedWakeLock.acquire();
}
}
}
@Override
public void release() {
synchronized (lock) {
if (held) {
if (LOG.isLoggable(FINE)) {
LOG.fine(tag + " releasing shared wake lock");
}
held = false;
sharedWakeLock.release();
} else {
if (LOG.isLoggable(FINE)) {
LOG.fine(tag + " already released");
}
}
}
}
}

View File

@@ -1,119 +0,0 @@
package org.briarproject.bramble.system;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.PowerManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidWakeLock;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import static android.content.Context.POWER_SERVICE;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@NotNullByDefault
class AndroidWakeLockManagerImpl implements AndroidWakeLockManager {
/**
* How often to replace the wake lock.
*/
private static final long LOCK_DURATION_MS = MINUTES.toMillis(1);
/**
* Automatically release the lock this many milliseconds after it's due
* to have been replaced and released.
*/
private static final long SAFETY_MARGIN_MS = SECONDS.toMillis(30);
private final SharedWakeLock sharedWakeLock;
@Inject
AndroidWakeLockManagerImpl(Application app,
ScheduledExecutorService scheduledExecutorService) {
PowerManager powerManager = (PowerManager)
requireNonNull(app.getSystemService(POWER_SERVICE));
String tag = getWakeLockTag(app);
sharedWakeLock = new RenewableWakeLock(powerManager,
scheduledExecutorService, PARTIAL_WAKE_LOCK, tag,
LOCK_DURATION_MS, SAFETY_MARGIN_MS);
}
@Override
public AndroidWakeLock createWakeLock(String tag) {
return new AndroidWakeLockImpl(sharedWakeLock, tag);
}
@Override
public void runWakefully(Runnable r, String tag) {
AndroidWakeLock wakeLock = createWakeLock(tag);
wakeLock.acquire();
try {
r.run();
} finally {
wakeLock.release();
}
}
@Override
public void executeWakefully(Runnable r, Executor executor, String tag) {
AndroidWakeLock wakeLock = createWakeLock(tag);
wakeLock.acquire();
try {
executor.execute(() -> {
try {
r.run();
} finally {
// Release the wake lock if the task throws an exception
wakeLock.release();
}
});
} catch (Exception e) {
// Release the wake lock if the executor throws an exception when
// we submit the task (in which case the release() call above won't
// happen)
wakeLock.release();
throw e;
}
}
@Override
public void executeWakefully(Runnable r, String tag) {
AndroidWakeLock wakeLock = createWakeLock(tag);
wakeLock.acquire();
try {
new Thread(() -> {
try {
r.run();
} finally {
wakeLock.release();
}
}).start();
} catch (Exception e) {
wakeLock.release();
throw e;
}
}
private String getWakeLockTag(Context ctx) {
PackageManager pm = ctx.getPackageManager();
for (PackageInfo info : pm.getInstalledPackages(0)) {
String name = info.packageName.toLowerCase();
if (name.startsWith("com.huawei.powergenie")) {
return "LocationManagerService";
} else if (name.startsWith("com.evenwell.powermonitor")) {
return "AudioIn";
}
}
return ctx.getPackageName();
}
}

View File

@@ -1,23 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.util.concurrent.Executor;
import dagger.Module;
import dagger.Provides;
@Module
public
class AndroidWakefulIoExecutorModule {
@Provides
@WakefulIoExecutor
Executor provideWakefulIoExecutor(@IoExecutor Executor ioExecutor,
AndroidWakeLockManager wakeLockManager) {
return r -> wakeLockManager.executeWakefully(r, ioExecutor,
"WakefulIoExecutor");
}
}

View File

@@ -1,130 +0,0 @@
package org.briarproject.bramble.system;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@ThreadSafe
@NotNullByDefault
class RenewableWakeLock implements SharedWakeLock {
private static final Logger LOG =
getLogger(RenewableWakeLock.class.getName());
private final PowerManager powerManager;
private final ScheduledExecutorService scheduledExecutorService;
private final int levelAndFlags;
private final String tag;
private final long durationMs, safetyMarginMs;
private final Object lock = new Object();
@GuardedBy("lock")
@Nullable
private WakeLock wakeLock;
@GuardedBy("lock")
@Nullable
private Future<?> future;
@GuardedBy("lock")
private int refCount = 0;
@GuardedBy("lock")
private long acquired = 0;
RenewableWakeLock(PowerManager powerManager,
ScheduledExecutorService scheduledExecutorService,
int levelAndFlags,
String tag,
long durationMs,
long safetyMarginMs) {
this.powerManager = powerManager;
this.scheduledExecutorService = scheduledExecutorService;
this.levelAndFlags = levelAndFlags;
this.tag = tag;
this.durationMs = durationMs;
this.safetyMarginMs = safetyMarginMs;
}
@Override
public void acquire() {
synchronized (lock) {
refCount++;
if (refCount == 1) {
if (LOG.isLoggable(INFO)) {
LOG.info("Acquiring wake lock " + tag);
}
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
// We do our own reference counting so we can replace the lock
// TODO: Check whether using a ref-counted wake lock affects
// power management apps
wakeLock.setReferenceCounted(false);
wakeLock.acquire(durationMs + safetyMarginMs);
future = scheduledExecutorService.schedule(this::renew,
durationMs, MILLISECONDS);
acquired = android.os.SystemClock.elapsedRealtime();
} else if (LOG.isLoggable(FINE)) {
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
}
}
}
private void renew() {
if (LOG.isLoggable(INFO)) LOG.info("Renewing wake lock " + tag);
synchronized (lock) {
if (wakeLock == null) {
LOG.info("Already released");
return;
}
if (LOG.isLoggable(FINE)) {
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
}
long now = android.os.SystemClock.elapsedRealtime();
long expiry = acquired + durationMs + safetyMarginMs;
if (now > expiry && LOG.isLoggable(WARNING)) {
LOG.warning("Wake lock expired " + (now - expiry) + " ms ago");
}
WakeLock oldWakeLock = wakeLock;
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
wakeLock.setReferenceCounted(false);
wakeLock.acquire(durationMs + safetyMarginMs);
oldWakeLock.release();
future = scheduledExecutorService.schedule(this::renew, durationMs,
MILLISECONDS);
acquired = now;
}
}
@Override
public void release() {
synchronized (lock) {
refCount--;
if (refCount == 0) {
if (LOG.isLoggable(INFO)) {
LOG.info("Releasing wake lock " + tag);
}
requireNonNull(future).cancel(false);
future = null;
requireNonNull(wakeLock).release();
wakeLock = null;
acquired = 0;
} else if (LOG.isLoggable(FINE)) {
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
}
}
}
}

View File

@@ -1,22 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidWakeLock;
@NotNullByDefault
interface SharedWakeLock {
/**
* Acquires the wake lock. This increments the wake lock's reference count,
* so unlike {@link AndroidWakeLock#acquire()} every call to this method
* must be followed by a balancing call to {@link #release()}.
*/
void acquire();
/**
* Releases the wake lock. This decrements the wake lock's reference count,
* so unlike {@link AndroidWakeLock#release()} every call to this method
* must follow a balancing call to {@link #acquire()}.
*/
void release();
}

View File

@@ -3,6 +3,8 @@ package org.briarproject.bramble.util;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.provider.Settings; import android.provider.Settings;
@@ -117,4 +119,17 @@ public class AndroidUtils {
if (SDK_INT < 24) return new String[] {"image/jpeg", "image/png"}; if (SDK_INT < 24) return new String[] {"image/jpeg", "image/png"};
else return new String[] {"image/jpeg", "image/png", "image/gif"}; else return new String[] {"image/jpeg", "image/png", "image/gif"};
} }
public static String getWakeLockTag(Context ctx) {
PackageManager pm = ctx.getPackageManager();
for (PackageInfo info : pm.getInstalledPackages(0)) {
String name = info.packageName.toLowerCase();
if (name.startsWith("com.huawei.powergenie")) {
return "LocationManagerService";
} else if (name.startsWith("com.evenwell.powermonitor")) {
return "AudioIn";
}
}
return ctx.getPackageName();
}
} }

View File

@@ -0,0 +1,100 @@
package org.briarproject.bramble.util;
import android.os.PowerManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
@ThreadSafe
@NotNullByDefault
public class RenewableWakeLock {
private static final Logger LOG =
Logger.getLogger(RenewableWakeLock.class.getName());
/**
* Automatically release the lock this many milliseconds after it's due
* to have been replaced and released.
*/
private static final int SAFETY_MARGIN_MS = 10_000;
private final PowerManager powerManager;
private final ScheduledExecutorService scheduler;
private final int levelAndFlags;
private final String tag;
private final long durationMs;
private final Runnable renewTask;
private final Object lock = new Object();
@Nullable
private PowerManager.WakeLock wakeLock; // Locking: lock
@Nullable
private ScheduledFuture future; // Locking: lock
public RenewableWakeLock(PowerManager powerManager,
ScheduledExecutorService scheduler, int levelAndFlags, String tag,
long duration, TimeUnit timeUnit) {
this.powerManager = powerManager;
this.scheduler = scheduler;
this.levelAndFlags = levelAndFlags;
this.tag = tag;
durationMs = MILLISECONDS.convert(duration, timeUnit);
renewTask = this::renew;
}
public void acquire() {
if (LOG.isLoggable(INFO)) LOG.info("Acquiring wake lock " + tag);
synchronized (lock) {
if (wakeLock != null) {
LOG.info("Already acquired");
return;
}
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
wakeLock.setReferenceCounted(false);
wakeLock.acquire(durationMs + SAFETY_MARGIN_MS);
future = scheduler.schedule(renewTask, durationMs, MILLISECONDS);
}
}
private void renew() {
if (LOG.isLoggable(INFO)) LOG.info("Renewing wake lock " + tag);
synchronized (lock) {
if (wakeLock == null) {
LOG.info("Already released");
return;
}
PowerManager.WakeLock oldWakeLock = wakeLock;
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
wakeLock.setReferenceCounted(false);
wakeLock.acquire(durationMs + SAFETY_MARGIN_MS);
oldWakeLock.release();
future = scheduler.schedule(renewTask, durationMs, MILLISECONDS);
}
}
public void release() {
if (LOG.isLoggable(INFO)) LOG.info("Releasing wake lock " + tag);
synchronized (lock) {
if (wakeLock == null) {
LOG.info("Already released");
return;
}
if (future == null) throw new AssertionError();
future.cancel(false);
future = null;
wakeLock.release();
wakeLock = null;
}
}
}

View File

@@ -7,10 +7,6 @@ public interface TimeoutMonitor {
/** /**
* Returns an {@link InputStream} that wraps the given stream and allows * Returns an {@link InputStream} that wraps the given stream and allows
* read timeouts to be detected. * read timeouts to be detected.
* <p>
* The returned stream must be {@link InputStream#close() closed} when it's
* no longer needed to ensure that resources held by the timeout monitor
* are released.
* *
* @param timeoutMs The read timeout in milliseconds. Timeouts will be * @param timeoutMs The read timeout in milliseconds. Timeouts will be
* detected eventually but are not guaranteed to be detected immediately. * detected eventually but are not guaranteed to be detected immediately.

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Wakeful;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -66,7 +65,6 @@ public interface LifecycleManager {
* Opens the {@link DatabaseComponent} using the given key and starts any * Opens the {@link DatabaseComponent} using the given key and starts any
* registered {@link Service Services}. * registered {@link Service Services}.
*/ */
@Wakeful
StartResult startServices(SecretKey dbKey); StartResult startServices(SecretKey dbKey);
/** /**
@@ -74,7 +72,6 @@ public interface LifecycleManager {
* registered {@link ExecutorService ExecutorServices}, and closes the * registered {@link ExecutorService ExecutorServices}, and closes the
* {@link DatabaseComponent}. * {@link DatabaseComponent}.
*/ */
@Wakeful
void stopServices(); void stopServices();
/** /**
@@ -107,7 +104,6 @@ public interface LifecycleManager {
* *
* @param txn A read-write transaction * @param txn A read-write transaction
*/ */
@Wakeful
void onDatabaseOpened(Transaction txn) throws DbException; void onDatabaseOpened(Transaction txn) throws DbException;
} }
} }

View File

@@ -1,20 +1,16 @@
package org.briarproject.bramble.api.lifecycle; package org.briarproject.bramble.api.lifecycle;
import org.briarproject.bramble.api.system.Wakeful;
public interface Service { public interface Service {
/** /**
* Starts the service. This method must not be called concurrently with * Starts the service.This method must not be called concurrently with
* {@link #stopService()}. * {@link #stopService()}.
*/ */
@Wakeful
void startService() throws ServiceException; void startService() throws ServiceException;
/** /**
* Stops the service. This method must not be called concurrently with * Stops the service. This method must not be called concurrently with
* {@link #startService()}. * {@link #startService()}.
*/ */
@Wakeful
void stopService() throws ServiceException; void stopService() throws ServiceException;
} }

View File

@@ -6,16 +6,6 @@ public interface BluetoothConstants {
int UUID_BYTES = 16; int UUID_BYTES = 16;
// Transport properties
String PROP_ADDRESS = "address"; String PROP_ADDRESS = "address";
String PROP_UUID = "uuid"; String PROP_UUID = "uuid";
// Local settings (not shared with contacts)
String PREF_ADDRESS_IS_REFLECTED = "addressIsReflected";
String PREF_EVER_CONNECTED = "everConnected";
// Default values for local settings
boolean DEFAULT_PREF_PLUGIN_ENABLE = false;
boolean DEFAULT_PREF_ADDRESS_IS_REFLECTED = false;
boolean DEFAULT_PREF_EVER_CONNECTED = false;
} }

View File

@@ -12,7 +12,4 @@ public interface LanTcpConstants {
// Local settings (not shared with contacts) // Local settings (not shared with contacts)
String PREF_LAN_IP_PORTS = "ipPorts"; String PREF_LAN_IP_PORTS = "ipPorts";
String PREF_IPV6 = "ipv6"; String PREF_IPV6 = "ipv6";
// Default value for PREF_PLUGIN_ENABLE
boolean DEFAULT_PREF_PLUGIN_ENABLE = true;
} }

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.Pair;
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.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.Wakeful;
import java.util.Collection; import java.util.Collection;
@@ -71,13 +70,11 @@ public interface Plugin {
/** /**
* Starts the plugin. * Starts the plugin.
*/ */
@Wakeful
void start() throws PluginException; void start() throws PluginException;
/** /**
* Stops the plugin. * Stops the plugin.
*/ */
@Wakeful
void stop() throws PluginException; void stop() throws PluginException;
/** /**
@@ -109,7 +106,6 @@ public interface Plugin {
* Attempts to create connections using the given transport properties, * Attempts to create connections using the given transport properties,
* passing any created connections to the corresponding handlers. * passing any created connections to the corresponding handlers.
*/ */
@Wakeful
void poll(Collection<Pair<TransportProperties, ConnectionHandler>> void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
properties); properties);
} }

View File

@@ -8,8 +8,6 @@ import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
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.Collection;
/** /**
* 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.
@@ -27,11 +25,6 @@ public interface PluginCallback extends ConnectionHandler {
*/ */
TransportProperties getLocalProperties(); TransportProperties getLocalProperties();
/**
* Returns the plugin's remote transport properties.
*/
Collection<TransportProperties> getRemoteProperties();
/** /**
* Merges the given settings with the plugin's settings * Merges the given settings with the plugin's settings
*/ */

View File

@@ -1,12 +1,9 @@
package org.briarproject.bramble.api.plugin; package org.briarproject.bramble.api.plugin;
import static java.util.concurrent.TimeUnit.DAYS;
public interface TorConstants { public interface TorConstants {
TransportId ID = new TransportId("org.briarproject.bramble.tor"); TransportId ID = new TransportId("org.briarproject.bramble.tor");
// Transport properties
String PROP_ONION_V2 = "onion"; String PROP_ONION_V2 = "onion";
String PROP_ONION_V3 = "onion3"; String PROP_ONION_V3 = "onion3";
@@ -16,33 +13,17 @@ public interface TorConstants {
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds
// Local settings (not shared with contacts)
String PREF_TOR_NETWORK = "network2"; String PREF_TOR_NETWORK = "network2";
String PREF_TOR_PORT = "port"; String PREF_TOR_PORT = "port";
String PREF_TOR_MOBILE = "useMobileData"; String PREF_TOR_MOBILE = "useMobileData";
String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging"; String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging";
String HS_PRIVATE_KEY_V2 = "onionPrivKey";
String HS_PRIVATE_KEY_V3 = "onionPrivKey3";
String HS_V3_CREATED = "onionPrivKey3Created";
/**
* How long to publish a v3 hidden service before retiring the v2 service.
*/
long V3_MIGRATION_PERIOD_MS = DAYS.toMillis(180);
// Values for PREF_TOR_NETWORK
int PREF_TOR_NETWORK_AUTOMATIC = 0; int PREF_TOR_NETWORK_AUTOMATIC = 0;
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1; int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;
int PREF_TOR_NETWORK_WITH_BRIDGES = 2; int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
// TODO: Remove when settings migration code is removed // TODO: Remove when settings migration code is removed
int PREF_TOR_NETWORK_NEVER = 3; int PREF_TOR_NETWORK_NEVER = 3;
// Default values for local settings
boolean DEFAULT_PREF_PLUGIN_ENABLE = true;
int DEFAULT_PREF_TOR_NETWORK = PREF_TOR_NETWORK_AUTOMATIC;
boolean DEFAULT_PREF_TOR_MOBILE = true;
boolean DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING = false;
/** /**
* Reason flag returned by {@link Plugin#getReasonsDisabled()}. * Reason flag returned by {@link Plugin#getReasonsDisabled()}.
*/ */

View File

@@ -4,7 +4,4 @@ public interface WanTcpConstants {
TransportId ID = new TransportId("org.briarproject.bramble.wan"); TransportId ID = new TransportId("org.briarproject.bramble.wan");
// Default value for PREF_PLUGIN_ENABLE
boolean DEFAULT_PREF_PLUGIN_ENABLE = false;
} }

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
import org.briarproject.bramble.api.system.Wakeful;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -22,7 +21,6 @@ public interface DuplexPlugin extends Plugin {
* Attempts to create and return a connection using the given transport * Attempts to create and return a connection using the given transport
* properties. Returns null if a connection cannot be created. * properties. Returns null if a connection cannot be created.
*/ */
@Wakeful
@Nullable @Nullable
DuplexTransportConnection createConnection(TransportProperties p); DuplexTransportConnection createConnection(TransportProperties p);

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that informs the Bluetooth plugin that we have enabled the
* Bluetooth adapter.
*/
@Immutable
@NotNullByDefault
public class BluetoothEnabledEvent extends Event {
}

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that asks the Bluetooth plugin to disable the Bluetooth adapter if
* we previously enabled it.
*/
@Immutable
@NotNullByDefault
public class DisableBluetoothEvent extends Event {
}

View File

@@ -0,0 +1,14 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that asks the Bluetooth plugin to enable the Bluetooth adapter.
*/
@Immutable
@NotNullByDefault
public class EnableBluetoothEvent extends Event {
}

View File

@@ -5,7 +5,6 @@ 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 org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.system.Wakeful;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -19,7 +18,6 @@ public interface SimplexPlugin extends Plugin {
* Attempts to create and return a reader for the given transport * Attempts to create and return a reader for the given transport
* properties. Returns null if a reader cannot be created. * properties. Returns null if a reader cannot be created.
*/ */
@Wakeful
@Nullable @Nullable
TransportConnectionReader createReader(TransportProperties p); TransportConnectionReader createReader(TransportProperties p);
@@ -27,7 +25,6 @@ public interface SimplexPlugin extends Plugin {
* Attempts to create and return a writer for the given transport * Attempts to create and return a writer for the given transport
* properties. Returns null if a writer cannot be created. * properties. Returns null if a writer cannot be created.
*/ */
@Wakeful
@Nullable @Nullable
TransportConnectionWriter createWriter(TransportProperties p); TransportConnectionWriter createWriter(TransportProperties p);
} }

View File

@@ -12,11 +12,6 @@ public interface TransportPropertyConstants {
*/ */
int MAX_PROPERTY_LENGTH = 100; int MAX_PROPERTY_LENGTH = 100;
/**
* Prefix for keys that represent reflected properties.
*/
String REFLECTED_PROPERTY_PREFIX = "u:";
/** /**
* Message metadata key for the transport ID of a local or remote update, * Message metadata key for the transport ID of a local or remote update,
* as a BDF string. * as a BDF string.

View File

@@ -1,27 +0,0 @@
package org.briarproject.bramble.api.properties.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when {@link TransportProperties} are received
* from a contact.
*/
@Immutable
@NotNullByDefault
public class RemoteTransportPropertiesUpdatedEvent extends Event {
private final TransportId transportId;
public RemoteTransportPropertiesUpdatedEvent(TransportId transportId) {
this.transportId = transportId;
}
public TransportId getTransportId() {
return transportId;
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.api.plugin; package org.briarproject.bramble.api.system;
import java.io.File;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@@ -12,11 +11,15 @@ import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Annotation for injecting the {@link File directory} where the Tor plugin * Annotation for injecting a scheduled executor service
* should store its state. * that can be used to schedule the execution of tasks.
* <p>
* The service should <b>only</b> be used for running tasks on other executors
* at scheduled times.
* No significant work should be run by the service itself!
*/ */
@Qualifier @Qualifier
@Target({FIELD, METHOD, PARAMETER}) @Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface TorDirectory { public @interface Scheduler {
} }

View File

@@ -1,43 +0,0 @@
package org.briarproject.bramble.api.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
* A service that can be used to schedule the execution of tasks.
*/
@NotNullByDefault
public interface TaskScheduler {
/**
* Submits the given task to the given executor after the given delay.
* <p>
* If the platform supports wake locks, a wake lock will be held while
* submitting and running the task.
*/
Cancellable schedule(Runnable task, Executor executor, long delay,
TimeUnit unit);
/**
* Submits the given task to the given executor after the given delay,
* and then repeatedly with the given interval between executions
* (measured from the end of one execution to the beginning of the next).
* <p>
* If the platform supports wake locks, a wake lock will be held while
* submitting and running the task.
*/
Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
long delay, long interval, TimeUnit unit);
interface Cancellable {
/**
* Cancels the task if it has not already started running. If the task
* is {@link #scheduleWithFixedDelay(Runnable, Executor, long, long, TimeUnit) periodic},
* all future executions of the task are cancelled.
*/
void cancel();
}
}

View File

@@ -1,19 +0,0 @@
package org.briarproject.bramble.api.system;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Annotation for methods that must be called while holding a wake lock, if
* the platform supports wake locks.
*/
@Qualifier
@Target(METHOD)
@Retention(RUNTIME)
public @interface Wakeful {
}

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.api.system;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Annotation for injecting the executor for long-running IO tasks that should
* run without sleeping. Also used for annotating methods that should run on
* this executor.
* <p>
* The contract of this executor is that tasks may be run concurrently, and
* submitting a task will never block. Tasks must not run indefinitely. Tasks
* submitted during shutdown are discarded.
*/
@Qualifier
@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
public @interface WakefulIoExecutor {
}

View File

@@ -9,6 +9,7 @@ import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.properties.PropertiesModule; import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.rendezvous.RendezvousModule; import org.briarproject.bramble.rendezvous.RendezvousModule;
import org.briarproject.bramble.sync.validation.ValidationModule; import org.briarproject.bramble.sync.validation.ValidationModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule; import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule; import org.briarproject.bramble.versioning.VersioningModule;
@@ -30,6 +31,8 @@ public interface BrambleCoreEagerSingletons {
void inject(RendezvousModule.EagerSingletons init); void inject(RendezvousModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init); void inject(TransportModule.EagerSingletons init);
void inject(ValidationModule.EagerSingletons init); void inject(ValidationModule.EagerSingletons init);
@@ -47,6 +50,7 @@ public interface BrambleCoreEagerSingletons {
c.inject(new RendezvousModule.EagerSingletons()); c.inject(new RendezvousModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons()); c.inject(new PluginModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons()); c.inject(new PropertiesModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons()); c.inject(new TransportModule.EagerSingletons());
c.inject(new ValidationModule.EagerSingletons()); c.inject(new ValidationModule.EagerSingletons());
c.inject(new VersioningModule.EagerSingletons()); c.inject(new VersioningModule.EagerSingletons());

View File

@@ -21,7 +21,7 @@ import org.briarproject.bramble.rendezvous.RendezvousModule;
import org.briarproject.bramble.settings.SettingsModule; import org.briarproject.bramble.settings.SettingsModule;
import org.briarproject.bramble.sync.SyncModule; import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.sync.validation.ValidationModule; import org.briarproject.bramble.sync.validation.ValidationModule;
import org.briarproject.bramble.system.ClockModule; import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule; import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule; import org.briarproject.bramble.versioning.VersioningModule;
@@ -29,7 +29,6 @@ import dagger.Module;
@Module(includes = { @Module(includes = {
ClientModule.class, ClientModule.class,
ClockModule.class,
ConnectionModule.class, ConnectionModule.class,
ContactModule.class, ContactModule.class,
CryptoModule.class, CryptoModule.class,
@@ -49,6 +48,7 @@ import dagger.Module;
RendezvousModule.class, RendezvousModule.class,
SettingsModule.class, SettingsModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class,
TransportModule.class, TransportModule.class,
ValidationModule.class, ValidationModule.class,
VersioningModule.class VersioningModule.class

View File

@@ -3,15 +3,15 @@ package org.briarproject.bramble.io;
import org.briarproject.bramble.api.io.TimeoutMonitor; import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
import org.briarproject.bramble.api.system.Wakeful;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
@@ -30,7 +30,7 @@ class TimeoutMonitorImpl implements TimeoutMonitor {
private static final long CHECK_INTERVAL_MS = SECONDS.toMillis(10); private static final long CHECK_INTERVAL_MS = SECONDS.toMillis(10);
private final TaskScheduler scheduler; private final ScheduledExecutorService scheduler;
private final Executor ioExecutor; private final Executor ioExecutor;
private final Clock clock; private final Clock clock;
private final Object lock = new Object(); private final Object lock = new Object();
@@ -38,10 +38,10 @@ class TimeoutMonitorImpl implements TimeoutMonitor {
private final List<TimeoutInputStream> streams = new ArrayList<>(); private final List<TimeoutInputStream> streams = new ArrayList<>();
@GuardedBy("lock") @GuardedBy("lock")
private Cancellable cancellable = null; private Future<?> task = null;
@Inject @Inject
TimeoutMonitorImpl(TaskScheduler scheduler, TimeoutMonitorImpl(@Scheduler ScheduledExecutorService scheduler,
@IoExecutor Executor ioExecutor, Clock clock) { @IoExecutor Executor ioExecutor, Clock clock) {
this.scheduler = scheduler; this.scheduler = scheduler;
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
@@ -55,9 +55,8 @@ class TimeoutMonitorImpl implements TimeoutMonitor {
timeoutMs, this::removeStream); timeoutMs, this::removeStream);
synchronized (lock) { synchronized (lock) {
if (streams.isEmpty()) { if (streams.isEmpty()) {
cancellable = scheduler.scheduleWithFixedDelay( task = scheduler.scheduleWithFixedDelay(this::checkTimeouts,
this::checkTimeouts, ioExecutor, CHECK_INTERVAL_MS, CHECK_INTERVAL_MS, CHECK_INTERVAL_MS, MILLISECONDS);
CHECK_INTERVAL_MS, MILLISECONDS);
} }
streams.add(stream); streams.add(stream);
} }
@@ -65,35 +64,33 @@ class TimeoutMonitorImpl implements TimeoutMonitor {
} }
private void removeStream(TimeoutInputStream stream) { private void removeStream(TimeoutInputStream stream) {
Cancellable toCancel = null; Future<?> toCancel = null;
synchronized (lock) { synchronized (lock) {
if (streams.remove(stream) && streams.isEmpty()) { if (streams.remove(stream) && streams.isEmpty()) {
toCancel = cancellable; toCancel = task;
cancellable = null; task = null;
} }
} }
if (toCancel != null) { if (toCancel != null) toCancel.cancel(false);
LOG.info("Cancelling timeout monitor task");
toCancel.cancel();
}
} }
@IoExecutor @Scheduler
@Wakeful
private void checkTimeouts() { private void checkTimeouts() {
List<TimeoutInputStream> snapshot; ioExecutor.execute(() -> {
synchronized (lock) { List<TimeoutInputStream> snapshot;
snapshot = new ArrayList<>(streams); synchronized (lock) {
} snapshot = new ArrayList<>(streams);
for (TimeoutInputStream stream : snapshot) { }
if (stream.hasTimedOut()) { for (TimeoutInputStream stream : snapshot) {
LOG.info("Input stream has timed out"); if (stream.hasTimedOut()) {
try { LOG.info("Input stream has timed out");
stream.close(); try {
} catch (IOException e) { stream.close();
logException(LOG, INFO, e); } catch (IOException e) {
logException(LOG, INFO, e);
}
} }
} }
} });
} }
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.keyagreement; package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto; import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
@@ -9,8 +8,6 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.keyagreement.Payload; import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.TransportDescriptor; import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -22,9 +19,7 @@ import org.briarproject.bramble.api.record.RecordWriterFactory;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@@ -33,10 +28,8 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.util.Arrays.asList;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.CONNECTION_TIMEOUT; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.CONNECTION_TIMEOUT;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@@ -48,10 +41,7 @@ class KeyAgreementConnector {
} }
private static final Logger LOG = private static final Logger LOG =
getLogger(KeyAgreementConnector.class.getName()); Logger.getLogger(KeyAgreementConnector.class.getName());
private static final List<TransportId> PREFERRED_TRANSPORTS =
asList(BluetoothConstants.ID, LanTcpConstants.ID);
private final Callbacks callbacks; private final Callbacks callbacks;
private final KeyAgreementCrypto keyAgreementCrypto; private final KeyAgreementCrypto keyAgreementCrypto;
@@ -115,35 +105,24 @@ class KeyAgreementConnector {
this.alice = alice; this.alice = alice;
aliceLatch.countDown(); aliceLatch.countDown();
// Start connecting over supported transports in order of preference // Start connecting over supported transports
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Starting outgoing BQP connections as " LOG.info("Starting outgoing BQP connections as "
+ (alice ? "Alice" : "Bob")); + (alice ? "Alice" : "Bob"));
} }
Map<TransportId, TransportDescriptor> descriptors = new HashMap<>();
for (TransportDescriptor d : remotePayload.getTransportDescriptors()) { for (TransportDescriptor d : remotePayload.getTransportDescriptors()) {
descriptors.put(d.getId(), d); Plugin p = pluginManager.getPlugin(d.getId());
} if (p instanceof DuplexPlugin) {
List<Pair<DuplexPlugin, BdfList>> transports = new ArrayList<>();
for (TransportId id : PREFERRED_TRANSPORTS) {
TransportDescriptor d = descriptors.get(id);
Plugin p = pluginManager.getPlugin(id);
if (d != null && p instanceof DuplexPlugin) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connecting via " + id); LOG.info("Connecting via " + d.getId());
transports.add(new Pair<>((DuplexPlugin) p, d.getDescriptor())); DuplexPlugin plugin = (DuplexPlugin) p;
byte[] commitment = remotePayload.getCommitment();
BdfList descriptor = d.getDescriptor();
connectionChooser.submit(new ReadableTask(
new ConnectorTask(plugin, commitment, descriptor)));
} }
} }
// TODO: If we don't have any transports in common with the peer,
// warn the user and give up (#1224)
if (!transports.isEmpty()) {
byte[] commitment = remotePayload.getCommitment();
connectionChooser.submit(new ReadableTask(new ConnectorTask(
transports, commitment)));
}
// Get chosen connection // Get chosen connection
try { try {
KeyAgreementConnection chosen = KeyAgreementConnection chosen =
@@ -169,13 +148,15 @@ class KeyAgreementConnector {
private class ConnectorTask implements Callable<KeyAgreementConnection> { private class ConnectorTask implements Callable<KeyAgreementConnection> {
private final List<Pair<DuplexPlugin, BdfList>> transports;
private final byte[] commitment; private final byte[] commitment;
private final BdfList descriptor;
private final DuplexPlugin plugin;
private ConnectorTask(List<Pair<DuplexPlugin, BdfList>> transports, private ConnectorTask(DuplexPlugin plugin, byte[] commitment,
byte[] commitment) { BdfList descriptor) {
this.transports = transports; this.plugin = plugin;
this.commitment = commitment; this.commitment = commitment;
this.descriptor = descriptor;
} }
@Nullable @Nullable
@@ -183,18 +164,13 @@ class KeyAgreementConnector {
public KeyAgreementConnection call() throws Exception { public KeyAgreementConnection call() throws Exception {
// Repeat attempts until we connect, get stopped, or get interrupted // Repeat attempts until we connect, get stopped, or get interrupted
while (!stopped) { while (!stopped) {
for (Pair<DuplexPlugin, BdfList> pair : transports) { DuplexTransportConnection conn =
if (stopped) return null; plugin.createKeyAgreementConnection(commitment,
DuplexPlugin plugin = pair.getFirst(); descriptor);
BdfList descriptor = pair.getSecond(); if (conn != null) {
DuplexTransportConnection conn = if (LOG.isLoggable(INFO))
plugin.createKeyAgreementConnection(commitment, LOG.info(plugin.getId() + ": Outgoing connection");
descriptor); return new KeyAgreementConnection(conn, plugin.getId());
if (conn != null) {
if (LOG.isLoggable(INFO))
LOG.info(plugin.getId() + ": Outgoing connection");
return new KeyAgreementConnection(conn, plugin.getId());
}
} }
// Wait 2s before retry (to circumvent transient failures) // Wait 2s before retry (to circumvent transient failures)
Thread.sleep(2000); Thread.sleep(2000);

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.connection.ConnectionManager; import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
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.IoExecutor;
@@ -29,7 +28,6 @@ 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.system.WakefulIoExecutor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -46,14 +44,12 @@ import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.Collections.emptyList;
import static java.util.logging.Level.FINE; import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@@ -66,7 +62,7 @@ class PluginManagerImpl implements PluginManager, Service {
private static final Logger LOG = private static final Logger LOG =
getLogger(PluginManagerImpl.class.getName()); getLogger(PluginManagerImpl.class.getName());
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
@@ -79,15 +75,11 @@ 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, PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus,
@WakefulIoExecutor Executor wakefulIoExecutor, PluginConfig pluginConfig, ConnectionManager connectionManager,
EventBus eventBus,
PluginConfig pluginConfig,
ConnectionManager connectionManager,
SettingsManager settingsManager, SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager) { TransportPropertyManager transportPropertyManager) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
@@ -115,7 +107,7 @@ class PluginManagerImpl implements PluginManager, Service {
simplexPlugins.add(s); simplexPlugins.add(s);
CountDownLatch startLatch = new CountDownLatch(1); CountDownLatch startLatch = new CountDownLatch(1);
startLatches.put(t, startLatch); startLatches.put(t, startLatch);
wakefulIoExecutor.execute(new PluginStarter(s, startLatch)); ioExecutor.execute(new PluginStarter(s, startLatch));
} }
} }
// Instantiate the duplex plugins and start them asynchronously // Instantiate the duplex plugins and start them asynchronously
@@ -131,7 +123,7 @@ class PluginManagerImpl implements PluginManager, Service {
duplexPlugins.add(d); duplexPlugins.add(d);
CountDownLatch startLatch = new CountDownLatch(1); CountDownLatch startLatch = new CountDownLatch(1);
startLatches.put(t, startLatch); startLatches.put(t, startLatch);
wakefulIoExecutor.execute(new PluginStarter(d, startLatch)); ioExecutor.execute(new PluginStarter(d, startLatch));
} }
} }
} }
@@ -143,16 +135,12 @@ class PluginManagerImpl implements PluginManager, Service {
LOG.info("Stopping simplex plugins"); LOG.info("Stopping simplex plugins");
for (SimplexPlugin s : simplexPlugins) { for (SimplexPlugin s : simplexPlugins) {
CountDownLatch startLatch = startLatches.get(s.getId()); CountDownLatch startLatch = startLatches.get(s.getId());
// Don't need the wakeful executor here as we wait for the plugin
// to stop before returning
ioExecutor.execute(new PluginStopper(s, startLatch, stopLatch)); ioExecutor.execute(new PluginStopper(s, startLatch, stopLatch));
} }
// Stop the duplex plugins // Stop the duplex plugins
LOG.info("Stopping duplex plugins"); LOG.info("Stopping duplex plugins");
for (DuplexPlugin d : duplexPlugins) { for (DuplexPlugin d : duplexPlugins) {
CountDownLatch startLatch = startLatches.get(d.getId()); CountDownLatch startLatch = startLatches.get(d.getId());
// Don't need the wakeful executor here as we wait for the plugin
// to stop before returning
ioExecutor.execute(new PluginStopper(d, startLatch, stopLatch)); ioExecutor.execute(new PluginStopper(d, startLatch, stopLatch));
} }
// Wait for all the plugins to stop // Wait for all the plugins to stop
@@ -215,7 +203,7 @@ class PluginManagerImpl implements PluginManager, Service {
} }
} }
private static class PluginStarter implements Runnable { private class PluginStarter implements Runnable {
private final Plugin plugin; private final Plugin plugin;
private final CountDownLatch startLatch; private final CountDownLatch startLatch;
@@ -245,7 +233,7 @@ class PluginManagerImpl implements PluginManager, Service {
} }
} }
private static class PluginStopper implements Runnable { private class PluginStopper implements Runnable {
private final Plugin plugin; private final Plugin plugin;
private final CountDownLatch startLatch, stopLatch; private final CountDownLatch startLatch, stopLatch;
@@ -315,18 +303,6 @@ class PluginManagerImpl implements PluginManager, Service {
} }
} }
@Override
public Collection<TransportProperties> getRemoteProperties() {
try {
Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(id);
return remote.values();
} catch (DbException e) {
logException(LOG, WARNING, e);
return emptyList();
}
}
@Override @Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
PluginManagerImpl.this.mergeSettings(s, id.getString()); PluginManagerImpl.this.mergeSettings(s, id.getString());
@@ -355,10 +331,6 @@ class PluginManagerImpl implements PluginManager, Service {
} else if (oldState == ACTIVE) { } else if (oldState == ACTIVE) {
eventBus.broadcast(new TransportInactiveEvent(id)); eventBus.broadcast(new TransportInactiveEvent(id));
} }
} else if (newState == DISABLED) {
// Broadcast an event even though the state hasn't changed, as
// the reasons for the plugin being disabled may have changed
eventBus.broadcast(new TransportStateEvent(id, newState));
} }
} }

View File

@@ -26,10 +26,7 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties; 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.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
import org.briarproject.bramble.api.system.Wakeful;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
@@ -38,6 +35,8 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -58,8 +57,8 @@ class PollerImpl implements Poller, EventListener {
private static final Logger LOG = getLogger(PollerImpl.class.getName()); private static final Logger LOG = getLogger(PollerImpl.class.getName());
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final TaskScheduler scheduler; private final ScheduledExecutorService scheduler;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry; private final ConnectionRegistry connectionRegistry;
private final PluginManager pluginManager; private final PluginManager pluginManager;
@@ -72,16 +71,12 @@ class PollerImpl implements Poller, EventListener {
@Inject @Inject
PollerImpl(@IoExecutor Executor ioExecutor, PollerImpl(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor, @Scheduler ScheduledExecutorService scheduler,
TaskScheduler scheduler,
ConnectionManager connectionManager, ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, ConnectionRegistry connectionRegistry, PluginManager pluginManager,
PluginManager pluginManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
SecureRandom random, SecureRandom random, Clock clock) {
Clock clock) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.scheduler = scheduler; this.scheduler = scheduler;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry; this.connectionRegistry = connectionRegistry;
@@ -138,7 +133,7 @@ class PollerImpl implements Poller, EventListener {
} }
private void connectToContact(ContactId c, SimplexPlugin p) { private void connectToContact(ContactId c, SimplexPlugin p) {
wakefulIoExecutor.execute(() -> { ioExecutor.execute(() -> {
TransportId t = p.getId(); TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return; if (connectionRegistry.isConnected(c, t)) return;
try { try {
@@ -154,7 +149,7 @@ class PollerImpl implements Poller, EventListener {
} }
private void connectToContact(ContactId c, DuplexPlugin p) { private void connectToContact(ContactId c, DuplexPlugin p) {
wakefulIoExecutor.execute(() -> { ioExecutor.execute(() -> {
TransportId t = p.getId(); TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return; if (connectionRegistry.isConnected(c, t)) return;
try { try {
@@ -191,11 +186,11 @@ class PollerImpl implements Poller, EventListener {
if (scheduled == null || due < scheduled.task.due) { if (scheduled == null || due < scheduled.task.due) {
// If a later task exists, cancel it. If it's already started // If a later task exists, cancel it. If it's already started
// it will abort safely when it finds it's been replaced // it will abort safely when it finds it's been replaced
if (scheduled != null) scheduled.cancellable.cancel(); if (scheduled != null) scheduled.future.cancel(false);
PollTask task = new PollTask(p, due, randomiseNext); PollTask task = new PollTask(p, due, randomiseNext);
Cancellable cancellable = scheduler.schedule(task, ioExecutor, Future future = scheduler.schedule(() ->
delay, MILLISECONDS); ioExecutor.execute(task), delay, MILLISECONDS);
tasks.put(t, new ScheduledPollTask(task, cancellable)); tasks.put(t, new ScheduledPollTask(task, future));
} }
} finally { } finally {
lock.unlock(); lock.unlock();
@@ -206,7 +201,7 @@ class PollerImpl implements Poller, EventListener {
lock.lock(); lock.lock();
try { try {
ScheduledPollTask scheduled = tasks.remove(t); ScheduledPollTask scheduled = tasks.remove(t);
if (scheduled != null) scheduled.cancellable.cancel(); if (scheduled != null) scheduled.future.cancel(false);
} finally { } finally {
lock.unlock(); lock.unlock();
} }
@@ -237,11 +232,11 @@ class PollerImpl implements Poller, EventListener {
private class ScheduledPollTask { private class ScheduledPollTask {
private final PollTask task; private final PollTask task;
private final Cancellable cancellable; private final Future future;
private ScheduledPollTask(PollTask task, Cancellable cancellable) { private ScheduledPollTask(PollTask task, Future future) {
this.task = task; this.task = task;
this.cancellable = cancellable; this.future = future;
} }
} }
@@ -259,7 +254,6 @@ class PollerImpl implements Poller, EventListener {
@Override @Override
@IoExecutor @IoExecutor
@Wakeful
public void run() { public void run() {
lock.lock(); lock.lock();
try { try {

View File

@@ -1,14 +0,0 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
@NotNullByDefault
interface BluetoothConnectionFactory<S> {
DuplexTransportConnection wrapSocket(DuplexPlugin plugin, S socket)
throws IOException;
}

View File

@@ -1,11 +1,11 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Multiset;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
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.io.TimeoutMonitor;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection; import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
@@ -21,8 +21,10 @@ import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
import org.briarproject.bramble.api.plugin.event.DisableBluetoothEvent;
import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.event.RemoteTransportPropertiesUpdatedEvent;
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
@@ -44,12 +46,7 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_ADDRESS_IS_REFLECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_EVER_CONNECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_ADDRESS_IS_REFLECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_EVER_CONNECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
@@ -57,7 +54,6 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.REFLECTED_PROPERTY_PREFIX;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@@ -66,21 +62,20 @@ import static org.briarproject.bramble.util.StringUtils.macToString;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener { abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private static final Logger LOG = private static final Logger LOG =
getLogger(BluetoothPlugin.class.getName()); getLogger(BluetoothPlugin.class.getName());
final BluetoothConnectionLimiter connectionLimiter; final BluetoothConnectionLimiter connectionLimiter;
final BluetoothConnectionFactory<S> connectionFactory; final TimeoutMonitor timeoutMonitor;
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final Backoff backoff; private final Backoff backoff;
private final PluginCallback callback; private final PluginCallback callback;
private final int maxLatency, maxIdleTime; private final int maxLatency, maxIdleTime;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private final AtomicBoolean everConnected = new AtomicBoolean(false);
protected final PluginState state = new PluginState(); protected final PluginState state = new PluginState();
@@ -90,6 +85,12 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
abstract boolean isAdapterEnabled(); abstract boolean isAdapterEnabled();
abstract void enableAdapter();
abstract void disableAdapterIfEnabledByUs();
abstract void setEnabledByUs();
/** /**
* Returns the local Bluetooth address, or null if no valid address can * Returns the local Bluetooth address, or null if no valid address can
* be found. * be found.
@@ -113,18 +114,12 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection discoverAndConnect(String uuid); abstract DuplexTransportConnection discoverAndConnect(String uuid);
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
BluetoothConnectionFactory<S> connectionFactory, TimeoutMonitor timeoutMonitor, Executor ioExecutor,
Executor ioExecutor, SecureRandom secureRandom, Backoff backoff,
Executor wakefulIoExecutor, PluginCallback callback, int maxLatency, int maxIdleTime) {
SecureRandom secureRandom,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime) {
this.connectionLimiter = connectionLimiter; this.connectionLimiter = connectionLimiter;
this.connectionFactory = connectionFactory; this.timeoutMonitor = timeoutMonitor;
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.backoff = backoff; this.backoff = backoff;
this.callback = callback; this.callback = callback;
@@ -169,10 +164,7 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
public void start() throws PluginException { public void start() throws PluginException {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
Settings settings = callback.getSettings(); Settings settings = callback.getSettings();
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false);
DEFAULT_PREF_PLUGIN_ENABLE);
everConnected.set(settings.getBoolean(PREF_EVER_CONNECTED,
DEFAULT_PREF_EVER_CONNECTED));
state.setStarted(enabledByUser); state.setStarted(enabledByUser);
try { try {
initialiseAdapter(); initialiseAdapter();
@@ -180,7 +172,10 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
throw new PluginException(e); throw new PluginException(e);
} }
updateProperties(); updateProperties();
if (enabledByUser && isAdapterEnabled()) bind(); if (enabledByUser) {
if (isAdapterEnabled()) bind();
else enableAdapter();
}
} }
private void bind() { private void bind() {
@@ -208,68 +203,25 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
TransportProperties p = callback.getLocalProperties(); TransportProperties p = callback.getLocalProperties();
String address = p.get(PROP_ADDRESS); String address = p.get(PROP_ADDRESS);
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);
Settings s = callback.getSettings();
boolean isReflected = s.getBoolean(PREF_ADDRESS_IS_REFLECTED,
DEFAULT_PREF_ADDRESS_IS_REFLECTED);
boolean changed = false; boolean changed = false;
if (address == null || isReflected) { if (address == null) {
address = getBluetoothAddress(); address = getBluetoothAddress();
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO))
LOG.info("Local address " + scrubMacAddress(address)); LOG.info("Local address " + scrubMacAddress(address));
} if (!isNullOrEmpty(address)) {
if (address == null) { p.put(PROP_ADDRESS, address);
if (everConnected.get()) {
address = getReflectedAddress();
if (LOG.isLoggable(INFO)) {
LOG.info("Reflected address " +
scrubMacAddress(address));
}
if (address != null) {
changed = true;
isReflected = true;
}
}
} else {
changed = true; changed = true;
isReflected = false;
} }
} }
if (uuid == null) { if (uuid == null) {
byte[] random = new byte[UUID_BYTES]; byte[] random = new byte[UUID_BYTES];
secureRandom.nextBytes(random); secureRandom.nextBytes(random);
uuid = UUID.nameUUIDFromBytes(random).toString(); uuid = UUID.nameUUIDFromBytes(random).toString();
p.put(PROP_UUID, uuid);
changed = true; changed = true;
} }
contactConnectionsUuid = uuid; contactConnectionsUuid = uuid;
if (changed) { if (changed) callback.mergeLocalProperties(p);
p = new TransportProperties();
// If we previously used a reflected address and there's no longer
// a reflected address with enough votes to be used, we'll continue
// to use the old reflected address until there's a new winner
if (address != null) p.put(PROP_ADDRESS, address);
p.put(PROP_UUID, uuid);
callback.mergeLocalProperties(p);
s = new Settings();
s.putBoolean(PREF_ADDRESS_IS_REFLECTED, isReflected);
callback.mergeSettings(s);
}
}
@Nullable
private String getReflectedAddress() {
// Count the number of votes for each reflected address
String key = REFLECTED_PROPERTY_PREFIX + PROP_ADDRESS;
Multiset<String> votes = new Multiset<>();
for (TransportProperties p : callback.getRemoteProperties()) {
String address = p.get(key);
if (address != null && isValidAddress(address)) votes.add(address);
}
// If an address gets more than half of the votes, accept it
int total = votes.getTotal();
for (String address : votes.keySet()) {
if (votes.getCount(address) * 2 > total) return address;
}
return null;
} }
private void acceptContactConnections(SS ss) { private void acceptContactConnections(SS ss) {
@@ -286,27 +238,15 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
LOG.info("Connection received"); LOG.info("Connection received");
connectionLimiter.connectionOpened(conn); connectionLimiter.connectionOpened(conn);
backoff.reset(); backoff.reset();
setEverConnected();
callback.handleConnection(conn); callback.handleConnection(conn);
} }
} }
private void setEverConnected() {
if (!everConnected.getAndSet(true)) {
ioExecutor.execute(() -> {
Settings s = new Settings();
s.putBoolean(PREF_EVER_CONNECTED, true);
callback.mergeSettings(s);
// Contacts may already have sent a reflected address
updateProperties();
});
}
}
@Override @Override
public void stop() { public void stop() {
SS ss = state.setStopped(); SS ss = state.setStopped();
tryToClose(ss); tryToClose(ss);
disableAdapterIfEnabledByUs();
} }
@Override @Override
@@ -344,11 +284,10 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
if (isNullOrEmpty(address)) return; if (isNullOrEmpty(address)) return;
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);
if (isNullOrEmpty(uuid)) return; if (isNullOrEmpty(uuid)) return;
wakefulIoExecutor.execute(() -> { ioExecutor.execute(() -> {
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
setEverConnected();
h.handleConnection(d); h.handleConnection(d);
} }
}); });
@@ -436,10 +375,8 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
String uuid = UUID.nameUUIDFromBytes(commitment).toString(); String uuid = UUID.nameUUIDFromBytes(commitment).toString();
DuplexTransportConnection conn; DuplexTransportConnection conn;
if (descriptor.size() == 1) { if (descriptor.size() == 1) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO))
LOG.info("Discovering address for key agreement UUID " + LOG.info("Discovering address for key agreement UUID " + uuid);
uuid);
}
conn = discoverAndConnect(uuid); conn = discoverAndConnect(uuid);
} else { } else {
String address; String address;
@@ -453,10 +390,7 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
LOG.info("Connecting to key agreement UUID " + uuid); LOG.info("Connecting to key agreement UUID " + uuid);
conn = connect(address, uuid); conn = connect(address, uuid);
} }
if (conn != null) { if (conn != null) connectionLimiter.connectionOpened(conn);
connectionLimiter.connectionOpened(conn);
setEverConnected();
}
return conn; return conn;
} }
@@ -479,7 +413,13 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof SettingsUpdatedEvent) { if (e instanceof EnableBluetoothEvent) {
ioExecutor.execute(this::enableAdapter);
} else if (e instanceof DisableBluetoothEvent) {
ioExecutor.execute(this::disableAdapterIfEnabledByUs);
} else if (e instanceof BluetoothEnabledEvent) {
setEnabledByUs();
} else if (e instanceof SettingsUpdatedEvent) {
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e; SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
if (s.getNamespace().equals(ID.getString())) if (s.getNamespace().equals(ID.getString()))
ioExecutor.execute(() -> onSettingsUpdated(s.getSettings())); ioExecutor.execute(() -> onSettingsUpdated(s.getSettings()));
@@ -487,31 +427,22 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
ioExecutor.execute(connectionLimiter::keyAgreementStarted); ioExecutor.execute(connectionLimiter::keyAgreementStarted);
} else if (e instanceof KeyAgreementStoppedListeningEvent) { } else if (e instanceof KeyAgreementStoppedListeningEvent) {
ioExecutor.execute(connectionLimiter::keyAgreementEnded); ioExecutor.execute(connectionLimiter::keyAgreementEnded);
} else if (e instanceof RemoteTransportPropertiesUpdatedEvent) {
RemoteTransportPropertiesUpdatedEvent r =
(RemoteTransportPropertiesUpdatedEvent) e;
if (r.getTransportId().equals(ID)) {
ioExecutor.execute(this::updateProperties);
}
} }
} }
@IoExecutor @IoExecutor
private void onSettingsUpdated(Settings settings) { private void onSettingsUpdated(Settings settings) {
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false);
DEFAULT_PREF_PLUGIN_ENABLE);
SS ss = state.setEnabledByUser(enabledByUser); SS ss = state.setEnabledByUser(enabledByUser);
State s = getState(); State s = getState();
if (ss != null) { if (ss != null) {
LOG.info("Disabled by user, closing server socket"); LOG.info("Disabled by user, closing server socket");
tryToClose(ss); tryToClose(ss);
disableAdapterIfEnabledByUs();
} else if (s == INACTIVE) { } else if (s == INACTIVE) {
if (isAdapterEnabled()) { LOG.info("Enabled by user, opening server socket");
LOG.info("Enabled by user, opening server socket"); if (isAdapterEnabled()) bind();
bind(); else enableAdapter();
} else {
LOG.info("Enabled by user but adapter is disabled");
}
} }
} }

View File

@@ -38,7 +38,6 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.DEFAULT_PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID; import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_IPV6; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_IPV6;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
@@ -88,15 +87,10 @@ class LanTcpPlugin extends TcpPlugin {
} }
} }
LanTcpPlugin(Executor ioExecutor, LanTcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback,
Executor wakefulIoExecutor, int maxLatency, int maxIdleTime, int connectionTimeout) {
Backoff backoff, super(ioExecutor, backoff, callback, maxLatency, maxIdleTime,
PluginCallback callback, connectionTimeout);
int maxLatency,
int maxIdleTime,
int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,
maxIdleTime, connectionTimeout);
} }
@Override @Override
@@ -109,8 +103,7 @@ class LanTcpPlugin extends TcpPlugin {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
initialisePortProperty(); initialisePortProperty();
Settings settings = callback.getSettings(); Settings settings = callback.getSettings();
state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, false));
DEFAULT_PREF_PLUGIN_ENABLE));
bind(); bind();
} }
@@ -123,11 +116,6 @@ class LanTcpPlugin extends TcpPlugin {
} }
} }
@Override
protected boolean isEnabledByDefault() {
return DEFAULT_PREF_PLUGIN_ENABLE;
}
@Override @Override
protected List<InetSocketAddress> getLocalSocketAddresses(boolean ipv4) { protected List<InetSocketAddress> getLocalSocketAddresses(boolean ipv4) {
TransportProperties p = callback.getLocalProperties(); TransportProperties p = callback.getLocalProperties();
@@ -354,7 +342,7 @@ class LanTcpPlugin extends TcpPlugin {
tryToClose(ss, LOG, WARNING); tryToClose(ss, LOG, WARNING);
} }
} }
if (ss == null || !ss.isBound()) { if (ss == null) {
LOG.info("Could not bind server socket for key agreement"); LOG.info("Could not bind server socket for key agreement");
return null; return null;
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
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.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -9,12 +8,10 @@ import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID; import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@@ -29,17 +26,13 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
@Inject public LanTcpPluginFactory(Executor ioExecutor, EventBus eventBus,
public LanTcpPluginFactory(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor,
EventBus eventBus,
BackoffFactory backoffFactory) { BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
} }
@@ -58,9 +51,8 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(PluginCallback callback) { public DuplexPlugin createPlugin(PluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, wakefulIoExecutor, LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback, MAX_LATENCY,
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME, MAX_IDLE_TIME, CONNECTION_TIMEOUT);
CONNECTION_TIMEOUT);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -66,7 +66,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
private static final Pattern DOTTED_QUAD = private static final Pattern DOTTED_QUAD =
Pattern.compile("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"); Pattern.compile("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$");
protected final Executor ioExecutor, wakefulIoExecutor, bindExecutor; protected final Executor ioExecutor, bindExecutor;
protected final Backoff backoff; protected final Backoff backoff;
protected final PluginCallback callback; protected final PluginCallback callback;
protected final int maxLatency, maxIdleTime; protected final int maxLatency, maxIdleTime;
@@ -102,20 +102,9 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
protected abstract boolean isConnectable(InterfaceAddress local, protected abstract boolean isConnectable(InterfaceAddress local,
InetSocketAddress remote); InetSocketAddress remote);
/** TcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback,
* Returns true if the plugin is enabled by default. int maxLatency, int maxIdleTime, int connectionTimeout) {
*/
protected abstract boolean isEnabledByDefault();
TcpPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime,
int connectionTimeout) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.backoff = backoff; this.backoff = backoff;
this.callback = callback; this.callback = callback;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
@@ -142,28 +131,21 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
public void start() { public void start() {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
Settings settings = callback.getSettings(); Settings settings = callback.getSettings();
state.setStarted( state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, false));
settings.getBoolean(PREF_PLUGIN_ENABLE, isEnabledByDefault()));
bind(); bind();
} }
protected void bind() { protected void bind() {
bindExecutor.execute(() -> { bindExecutor.execute(() -> {
State s = getState(); if (getState() != INACTIVE) return;
if (s != ACTIVE && s != INACTIVE) return;
bind(true); bind(true);
bind(false); bind(false);
}); });
} }
private void bind(boolean ipv4) { private void bind(boolean ipv4) {
ServerSocket old = state.getServerSocket(ipv4);
ServerSocket ss = null; ServerSocket ss = null;
for (InetSocketAddress addr : getLocalSocketAddresses(ipv4)) { for (InetSocketAddress addr : getLocalSocketAddresses(ipv4)) {
if (old != null && addr.equals(old.getLocalSocketAddress())) {
LOG.info("Server socket already bound");
return;
}
try { try {
ss = new ServerSocket(); ss = new ServerSocket();
ss.bind(addr); ss.bind(addr);
@@ -174,7 +156,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
tryToClose(ss, LOG, WARNING); tryToClose(ss, LOG, WARNING);
} }
} }
if (ss == null || !ss.isBound()) { if (ss == null) {
LOG.info("Could not bind server socket"); LOG.info("Could not bind server socket");
return; return;
} }
@@ -257,7 +239,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
} }
private void connect(TransportProperties p, ConnectionHandler h) { private void connect(TransportProperties p, ConnectionHandler h) {
wakefulIoExecutor.execute(() -> { ioExecutor.execute(() -> {
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
@@ -420,8 +402,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
@IoExecutor @IoExecutor
private void onSettingsUpdated(Settings settings) { private void onSettingsUpdated(Settings settings) {
boolean enabledByUser = boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false);
settings.getBoolean(PREF_PLUGIN_ENABLE, isEnabledByDefault());
List<ServerSocket> toClose = state.setEnabledByUser(enabledByUser); List<ServerSocket> toClose = state.setEnabledByUser(enabledByUser);
State s = getState(); State s = getState();
if (!toClose.isEmpty()) { if (!toClose.isEmpty()) {

View File

@@ -17,7 +17,6 @@ import java.util.concurrent.Executor;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.plugin.WanTcpConstants.DEFAULT_PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.WanTcpConstants.ID; import static org.briarproject.bramble.api.plugin.WanTcpConstants.ID;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -30,16 +29,11 @@ class WanTcpPlugin extends TcpPlugin {
private volatile MappingResult mappingResult; private volatile MappingResult mappingResult;
WanTcpPlugin(Executor ioExecutor, WanTcpPlugin(Executor ioExecutor, Backoff backoff, PortMapper portMapper,
Executor wakefulIoExecutor, PluginCallback callback, int maxLatency, int maxIdleTime,
Backoff backoff,
PortMapper portMapper,
PluginCallback callback,
int maxLatency,
int maxIdleTime,
int connectionTimeout) { int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency, super(ioExecutor, backoff, callback, maxLatency, maxIdleTime,
maxIdleTime, connectionTimeout); connectionTimeout);
this.portMapper = portMapper; this.portMapper = portMapper;
} }
@@ -48,11 +42,6 @@ class WanTcpPlugin extends TcpPlugin {
return ID; return ID;
} }
@Override
protected boolean isEnabledByDefault() {
return DEFAULT_PREF_PLUGIN_ENABLE;
}
@Override @Override
protected List<InetSocketAddress> getLocalSocketAddresses(boolean ipv4) { protected List<InetSocketAddress> getLocalSocketAddresses(boolean ipv4) {
if (!ipv4) return emptyList(); if (!ipv4) return emptyList();

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
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.ShutdownManager; import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -10,12 +9,10 @@ import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.plugin.WanTcpConstants.ID; import static org.briarproject.bramble.api.plugin.WanTcpConstants.ID;
@@ -30,19 +27,14 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
private final ShutdownManager shutdownManager; private final ShutdownManager shutdownManager;
@Inject public WanTcpPluginFactory(Executor ioExecutor, EventBus eventBus,
public WanTcpPluginFactory(@IoExecutor Executor ioExecutor, BackoffFactory backoffFactory, ShutdownManager shutdownManager) {
@WakefulIoExecutor Executor wakefulIoExecutor,
EventBus eventBus,
BackoffFactory backoffFactory,
ShutdownManager shutdownManager) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
this.shutdownManager = shutdownManager; this.shutdownManager = shutdownManager;
@@ -62,10 +54,9 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(PluginCallback callback) { public DuplexPlugin createPlugin(PluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
PortMapper portMapper = new PortMapperImpl(shutdownManager); WanTcpPlugin plugin = new WanTcpPlugin(ioExecutor, backoff,
WanTcpPlugin plugin = new WanTcpPlugin(ioExecutor, wakefulIoExecutor, new PortMapperImpl(shutdownManager), callback, MAX_LATENCY,
backoff, portMapper, callback, MAX_LATENCY, MAX_IDLE_TIME, MAX_IDLE_TIME, CONNECTION_TIMEOUT);
CONNECTION_TIMEOUT);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -75,13 +75,6 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_PRIVATE_KEY_V2;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_PRIVATE_KEY_V3;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_V3_CREATED;
import static org.briarproject.bramble.api.plugin.TorConstants.ID; import static org.briarproject.bramble.api.plugin.TorConstants.ID;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
@@ -95,7 +88,6 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
import static org.briarproject.bramble.api.plugin.TorConstants.V3_MIGRATION_PERIOD_MS;
import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES; import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES;
import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.IoUtils.tryToClose;
@@ -118,8 +110,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final Pattern ONION_V2 = Pattern.compile("[a-z2-7]{16}"); private static final Pattern ONION_V2 = Pattern.compile("[a-z2-7]{16}");
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}"); private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor, connectionStatusExecutor;
private final Executor connectionStatusExecutor;
private final NetworkManager networkManager; private final NetworkManager networkManager;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final SocketFactory torSocketFactory; private final SocketFactory torSocketFactory;
@@ -132,7 +123,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final CircumventionProvider circumventionProvider; private final CircumventionProvider circumventionProvider;
private final ResourceProvider resourceProvider; private final ResourceProvider resourceProvider;
private final int maxLatency, maxIdleTime, socketTimeout; private final int maxLatency, maxIdleTime, socketTimeout;
private final File torDirectory, geoIpFile, configFile; private final File torDirectory, torFile, geoIpFile, obfs4File, configFile;
private final File doneFile, cookieFile; private final File doneFile, cookieFile;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@@ -146,24 +137,15 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
protected abstract long getLastUpdateTime(); protected abstract long getLastUpdateTime();
TorPlugin(Executor ioExecutor, TorPlugin(Executor ioExecutor, NetworkManager networkManager,
Executor wakefulIoExecutor, LocationUtils locationUtils, SocketFactory torSocketFactory,
NetworkManager networkManager, Clock clock, ResourceProvider resourceProvider,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, BatteryManager batteryManager, Backoff backoff,
Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, PluginCallback callback, String architecture, int maxLatency,
String architecture, int maxIdleTime, File torDirectory) {
int maxLatency,
int maxIdleTime,
File torDirectory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.networkManager = networkManager; this.networkManager = networkManager;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.torSocketFactory = torSocketFactory; this.torSocketFactory = torSocketFactory;
@@ -181,7 +163,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
socketTimeout = Integer.MAX_VALUE; socketTimeout = Integer.MAX_VALUE;
else socketTimeout = maxIdleTime * 2; else socketTimeout = maxIdleTime * 2;
this.torDirectory = torDirectory; this.torDirectory = torDirectory;
torFile = new File(torDirectory, "tor");
geoIpFile = new File(torDirectory, "geoip"); geoIpFile = new File(torDirectory, "geoip");
obfs4File = new File(torDirectory, "obfs4proxy");
configFile = new File(torDirectory, "torrc"); configFile = new File(torDirectory, "torrc");
doneFile = new File(torDirectory, "done"); doneFile = new File(torDirectory, "done");
cookieFile = new File(torDirectory, ".tor/control_auth_cookie"); cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
@@ -190,14 +174,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
new PoliteExecutor("TorPlugin", ioExecutor, 1); new PoliteExecutor("TorPlugin", ioExecutor, 1);
} }
protected File getTorExecutableFile() {
return new File(torDirectory, "tor");
}
protected File getObfs4ExecutableFile() {
return new File(torDirectory, "obfs4proxy");
}
@Override @Override
public TransportId getId() { public TransportId getId() {
return TorConstants.ID; return TorConstants.ID;
@@ -230,7 +206,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
LOG.warning("Old auth cookie not deleted"); LOG.warning("Old auth cookie not deleted");
// Start a new Tor process // Start a new Tor process
LOG.info("Starting Tor"); LOG.info("Starting Tor");
File torFile = getTorExecutableFile();
String torPath = torFile.getAbsolutePath(); String torPath = torFile.getAbsolutePath();
String configPath = configFile.getAbsolutePath(); String configPath = configFile.getAbsolutePath();
String pid = String.valueOf(getProcessId()); String pid = String.valueOf(getProcessId());
@@ -315,9 +290,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
// TODO: Remove after a reasonable migration period (added 2020-06-25) // TODO: Remove after a reasonable migration period (added 2020-06-25)
private Settings migrateSettings(Settings settings) { private Settings migrateSettings(Settings settings) {
int network = settings.getInt(PREF_TOR_NETWORK, int network = settings.getInt(PREF_TOR_NETWORK,
DEFAULT_PREF_TOR_NETWORK); PREF_TOR_NETWORK_AUTOMATIC);
if (network == PREF_TOR_NETWORK_NEVER) { if (network == PREF_TOR_NETWORK_NEVER) {
settings.putInt(PREF_TOR_NETWORK, DEFAULT_PREF_TOR_NETWORK); settings.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC);
settings.putBoolean(PREF_PLUGIN_ENABLE, false); settings.putBoolean(PREF_PLUGIN_ENABLE, false);
callback.mergeSettings(settings); callback.mergeSettings(settings);
} }
@@ -329,43 +304,44 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void installAssets() throws PluginException { private void installAssets() throws PluginException {
InputStream in = null;
OutputStream out = null;
try { try {
// The done file may already exist from a previous installation // The done file may already exist from a previous installation
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
doneFile.delete(); doneFile.delete();
installTorExecutable(); // Unzip the Tor binary to the filesystem
installObfs4Executable(); in = getTorInputStream();
extract(getGeoIpInputStream(), geoIpFile); out = new FileOutputStream(torFile);
extract(getConfigInputStream(), configFile); copyAndClose(in, out);
// Make the Tor binary executable
if (!torFile.setExecutable(true, true)) throw new IOException();
// Unzip the GeoIP database to the filesystem
in = getGeoIpInputStream();
out = new FileOutputStream(geoIpFile);
copyAndClose(in, out);
// Unzip the Obfs4 proxy to the filesystem
in = getObfs4InputStream();
out = new FileOutputStream(obfs4File);
copyAndClose(in, out);
// Make the Obfs4 proxy executable
if (!obfs4File.setExecutable(true, true)) throw new IOException();
// Copy the config file to the filesystem
in = getConfigInputStream();
out = new FileOutputStream(configFile);
copyAndClose(in, out);
if (!doneFile.createNewFile()) if (!doneFile.createNewFile())
LOG.warning("Failed to create done file"); LOG.warning("Failed to create done file");
} catch (IOException e) { } catch (IOException e) {
tryToClose(in, LOG, WARNING);
tryToClose(out, LOG, WARNING);
throw new PluginException(e); throw new PluginException(e);
} }
} }
protected void extract(InputStream in, File dest) throws IOException { private InputStream getTorInputStream() throws IOException {
OutputStream out = new FileOutputStream(dest);
copyAndClose(in, out);
}
protected void installTorExecutable() throws IOException {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Installing Tor binary for " + architecture); LOG.info("Installing Tor binary for " + architecture);
File torFile = getTorExecutableFile();
extract(getTorInputStream(), torFile);
if (!torFile.setExecutable(true, true)) throw new IOException();
}
protected void installObfs4Executable() throws IOException {
if (LOG.isLoggable(INFO))
LOG.info("Installing obfs4proxy binary for " + architecture);
File obfs4File = getObfs4ExecutableFile();
extract(getObfs4InputStream(), obfs4File);
if (!obfs4File.setExecutable(true, true)) throw new IOException();
}
private InputStream getTorInputStream() throws IOException {
InputStream in = resourceProvider InputStream in = resourceProvider
.getResourceInputStream("tor_" + architecture, ".zip"); .getResourceInputStream("tor_" + architecture, ".zip");
ZipInputStream zin = new ZipInputStream(in); ZipInputStream zin = new ZipInputStream(in);
@@ -382,6 +358,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private InputStream getObfs4InputStream() throws IOException { private InputStream getObfs4InputStream() throws IOException {
if (LOG.isLoggable(INFO))
LOG.info("Installing obfs4proxy binary for " + architecture);
InputStream in = resourceProvider InputStream in = resourceProvider
.getResourceInputStream("obfs4proxy_" + architecture, ".zip"); .getResourceInputStream("obfs4proxy_" + architecture, ".zip");
ZipInputStream zin = new ZipInputStream(in); ZipInputStream zin = new ZipInputStream(in);
@@ -456,66 +434,15 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private void publishHiddenService(String port) { private void publishHiddenService(String port) {
if (!state.isTorRunning()) return; if (!state.isTorRunning()) return;
// TODO: Remove support for v2 hidden services after a reasonable LOG.info("Creating hidden service");
// migration period (migration started 2020-06-30) String privKey = settings.get(HS_PRIVKEY);
String privKey2 = settings.get(HS_PRIVATE_KEY_V2);
String privKey3 = settings.get(HS_PRIVATE_KEY_V3);
String v3Created = settings.get(HS_V3_CREATED);
// Publish a v2 hidden service if we've already created one, and
// either we've never published a v3 hidden service or we're still
// in the migration period since first publishing it
if (!isNullOrEmpty(privKey2)) {
long now = clock.currentTimeMillis();
long then = v3Created == null ? now : Long.parseLong(v3Created);
if (now - then >= V3_MIGRATION_PERIOD_MS) retireV2HiddenService();
else publishV2HiddenService(port, privKey2);
}
publishV3HiddenService(port, privKey3);
}
private void publishV2HiddenService(String port, String privKey) {
LOG.info("Creating v2 hidden service");
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
Map<String, String> response;
try {
response = controlConnection.addOnion(privKey, portLines);
} catch (IOException e) {
logException(LOG, WARNING, e);
return;
}
if (!response.containsKey(HS_ADDRESS)) {
LOG.warning("Tor did not return a hidden service address");
return;
}
String onion2 = response.get(HS_ADDRESS);
if (LOG.isLoggable(INFO)) {
LOG.info("V2 hidden service " + scrubOnion(onion2));
}
// The hostname has already been published and the private key stored
}
private void retireV2HiddenService() {
LOG.info("Retiring v2 hidden service");
TransportProperties p = new TransportProperties();
p.put(PROP_ONION_V2, "");
callback.mergeLocalProperties(p);
Settings s = new Settings();
s.put(HS_PRIVATE_KEY_V2, "");
callback.mergeSettings(s);
}
private void publishV3HiddenService(String port, @Nullable String privKey) {
LOG.info("Creating v3 hidden service");
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port); Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
Map<String, String> response; Map<String, String> response;
try { try {
// Use the control connection to set up the hidden service // Use the control connection to set up the hidden service
if (privKey == null) { if (privKey == null)
response = controlConnection.addOnion("NEW:ED25519-V3", response = controlConnection.addOnion(portLines);
portLines, null); else response = controlConnection.addOnion(privKey, portLines);
} else {
response = controlConnection.addOnion(privKey, portLines);
}
} catch (IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
return; return;
@@ -528,19 +455,17 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
LOG.warning("Tor did not return a private key"); LOG.warning("Tor did not return a private key");
return; return;
} }
String onion3 = response.get(HS_ADDRESS); // Publish the hidden service's onion hostname in transport properties
if (LOG.isLoggable(INFO)) { String onion2 = response.get(HS_ADDRESS);
LOG.info("V3 hidden service " + scrubOnion(onion3)); if (LOG.isLoggable(INFO))
} LOG.info("Hidden service " + scrubOnion(onion2));
TransportProperties p = new TransportProperties();
p.put(PROP_ONION_V2, onion2);
callback.mergeLocalProperties(p);
if (privKey == null) { if (privKey == null) {
// Publish the hidden service's onion hostname in transport props
TransportProperties p = new TransportProperties();
p.put(PROP_ONION_V3, onion3);
callback.mergeLocalProperties(p);
// Save the hidden service's private key for next time // Save the hidden service's private key for next time
Settings s = new Settings(); Settings s = new Settings();
s.put(HS_PRIVATE_KEY_V3, response.get(HS_PRIVKEY)); s.put(HS_PRIVKEY, response.get(HS_PRIVKEY));
s.put(HS_V3_CREATED, String.valueOf(clock.currentTimeMillis()));
callback.mergeSettings(s); callback.mergeSettings(s);
} }
} }
@@ -573,7 +498,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (enable) { if (enable) {
Collection<String> conf = new ArrayList<>(); Collection<String> conf = new ArrayList<>();
conf.add("UseBridges 1"); conf.add("UseBridges 1");
File obfs4File = getObfs4ExecutableFile();
if (needsMeek) { if (needsMeek) {
conf.add("ClientTransportPlugin meek_lite exec " + conf.add("ClientTransportPlugin meek_lite exec " +
obfs4File.getAbsolutePath()); obfs4File.getAbsolutePath());
@@ -635,7 +559,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void connect(TransportProperties p, ConnectionHandler h) { private void connect(TransportProperties p, ConnectionHandler h) {
wakefulIoExecutor.execute(() -> { ioExecutor.execute(() -> {
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
@@ -647,15 +571,12 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public DuplexTransportConnection createConnection(TransportProperties p) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (getState() != ACTIVE) return null; if (getState() != ACTIVE) return null;
// TODO: Remove support for v2 hidden services after a reasonable String bestOnion = null;
// migration period (migration started 2020-06-30)
String bestOnion = null, version = null;
String onion2 = p.get(PROP_ONION_V2); String onion2 = p.get(PROP_ONION_V2);
String onion3 = p.get(PROP_ONION_V3); String onion3 = p.get(PROP_ONION_V3);
if (!isNullOrEmpty(onion2)) { if (!isNullOrEmpty(onion2)) {
if (ONION_V2.matcher(onion2).matches()) { if (ONION_V2.matcher(onion2).matches()) {
bestOnion = onion2; bestOnion = onion2;
version = "v2";
} else { } else {
// Don't scrub the address so we can find the problem // Don't scrub the address so we can find the problem
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -665,7 +586,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!isNullOrEmpty(onion3)) { if (!isNullOrEmpty(onion3)) {
if (ONION_V3.matcher(onion3).matches()) { if (ONION_V3.matcher(onion3).matches()) {
bestOnion = onion3; bestOnion = onion3;
version = "v3";
} else { } else {
// Don't scrub the address so we can find the problem // Don't scrub the address so we can find the problem
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -675,21 +595,17 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (bestOnion == null) return null; if (bestOnion == null) return null;
Socket s = null; Socket s = null;
try { try {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + version + " " LOG.info("Connecting to " + scrubOnion(bestOnion));
+ scrubOnion(bestOnion));
}
s = torSocketFactory.createSocket(bestOnion + ".onion", 80); s = torSocketFactory.createSocket(bestOnion + ".onion", 80);
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO))
LOG.info("Connected to " + version + " " LOG.info("Connected to " + scrubOnion(bestOnion));
+ scrubOnion(bestOnion));
}
return new TorTransportConnection(this, s); return new TorTransportConnection(this, s);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Could not connect to " + version + " " LOG.info("Could not connect to " + scrubOnion(bestOnion)
+ scrubOnion(bestOnion) + ": " + e.toString()); + ": " + e.toString());
} }
tryToClose(s, LOG, WARNING); tryToClose(s, LOG, WARNING);
return null; return null;
@@ -811,16 +727,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public void unrecognized(String type, String msg) { public void unrecognized(String type, String msg) {
if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) { if (type.equals("HS_DESC") && msg.startsWith("UPLOADED"))
if (LOG.isLoggable(INFO)) { LOG.info("Descriptor uploaded");
String[] words = msg.split(" ");
if (words.length > 1 && ONION_V3.matcher(words[1]).matches()) {
LOG.info("V3 descriptor uploaded");
} else {
LOG.info("V2 descriptor uploaded");
}
}
}
} }
@Override @Override
@@ -866,15 +774,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
String country = locationUtils.getCurrentCountry(); String country = locationUtils.getCurrentCountry();
boolean blocked = boolean blocked =
circumventionProvider.isTorProbablyBlocked(country); circumventionProvider.isTorProbablyBlocked(country);
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, boolean enabledByUser =
DEFAULT_PREF_PLUGIN_ENABLE); settings.getBoolean(PREF_PLUGIN_ENABLE, true);
int network = settings.getInt(PREF_TOR_NETWORK, int network = settings.getInt(PREF_TOR_NETWORK,
DEFAULT_PREF_TOR_NETWORK); PREF_TOR_NETWORK_AUTOMATIC);
boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true);
DEFAULT_PREF_TOR_MOBILE);
boolean onlyWhenCharging = boolean onlyWhenCharging =
settings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING, settings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false);
DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING);
boolean bridgesWork = circumventionProvider.doBridgesWork(country); boolean bridgesWork = circumventionProvider.doBridgesWork(country);
boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC; boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC;

View File

@@ -18,7 +18,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; 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.properties.event.RemoteTransportPropertiesUpdatedEvent;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility; import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
@@ -31,7 +30,6 @@ import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook; import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -43,8 +41,6 @@ import static org.briarproject.bramble.api.properties.TransportPropertyConstants
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_LOCAL; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_TRANSPORT_ID; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_TRANSPORT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_VERSION; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_VERSION;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.REFLECTED_PROPERTY_PREFIX;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -131,10 +127,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
// We've already received a newer update - delete this one // We've already received a newer update - delete this one
db.deleteMessage(txn, m.getId()); db.deleteMessage(txn, m.getId());
db.deleteMessageMetadata(txn, m.getId()); db.deleteMessageMetadata(txn, m.getId());
return false;
} }
} }
txn.attach(new RemoteTransportPropertiesUpdatedEvent(t));
} catch (FormatException e) { } catch (FormatException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }
@@ -157,27 +151,15 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
if (props.isEmpty()) return; if (props.isEmpty()) return;
try { try {
db.transaction(false, txn -> { db.transaction(false, txn -> {
Contact contact = db.getContact(txn, c); Group g = getContactGroup(db.getContact(txn, c));
Group g = getContactGroup(contact);
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary( BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(
txn, g.getId()); txn, g.getId());
BdfDictionary discovered = BdfDictionary discovered =
meta.getOptionalDictionary(GROUP_KEY_DISCOVERED); meta.getOptionalDictionary(GROUP_KEY_DISCOVERED);
BdfDictionary merged; if (discovered == null) discovered = new BdfDictionary();
boolean changed; discovered.putAll(props);
if (discovered == null) { meta.put(GROUP_KEY_DISCOVERED, discovered);
merged = new BdfDictionary(props); clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
changed = true;
} else {
merged = new BdfDictionary(discovered);
merged.putAll(props);
changed = !merged.equals(discovered);
}
if (changed) {
meta.put(GROUP_KEY_DISCOVERED, merged);
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
updateLocalProperties(txn, contact, t);
}
}); });
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
@@ -242,24 +224,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
}); });
} }
private void updateLocalProperties(Transaction txn, Contact c,
TransportId t) throws DbException {
try {
TransportProperties local;
LatestUpdate latest = findLatest(txn, localGroup.getId(), t, true);
if (latest == null) {
local = new TransportProperties();
} else {
BdfList message = clientHelper.getMessageAsList(txn,
latest.messageId);
local = parseProperties(message);
}
storeLocalProperties(txn, c, t, local);
} catch (FormatException e) {
throw new DbException(e);
}
}
private TransportProperties getRemoteProperties(Transaction txn, Contact c, private TransportProperties getRemoteProperties(Transaction txn, Contact c,
TransportId t) throws DbException { TransportId t) throws DbException {
Group g = getContactGroup(c); Group g = getContactGroup(c);
@@ -308,22 +272,14 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
LatestUpdate latest = findLatest(txn, localGroup.getId(), t, LatestUpdate latest = findLatest(txn, localGroup.getId(), t,
true); true);
if (latest == null) { if (latest == null) {
merged = new TransportProperties(p); merged = p;
Iterator<String> it = merged.values().iterator();
while (it.hasNext()) {
if (isNullOrEmpty(it.next())) it.remove();
}
changed = true; changed = true;
} else { } else {
BdfList message = clientHelper.getMessageAsList(txn, BdfList message = clientHelper.getMessageAsList(txn,
latest.messageId); latest.messageId);
TransportProperties old = parseProperties(message); TransportProperties old = parseProperties(message);
merged = new TransportProperties(old); merged = new TransportProperties(old);
for (Entry<String, String> e : p.entrySet()) { merged.putAll(p);
String key = e.getKey(), value = e.getValue();
if (isNullOrEmpty(value)) merged.remove(key);
else merged.put(key, value);
}
changed = !merged.equals(old); changed = !merged.equals(old);
} }
if (changed) { if (changed) {
@@ -332,10 +288,18 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
storeMessage(txn, localGroup.getId(), t, merged, version, storeMessage(txn, localGroup.getId(), t, merged, version,
true, false); true, false);
// Delete the previous update, if any // Delete the previous update, if any
if (latest != null) db.removeMessage(txn, latest.messageId); if (latest != null)
db.removeMessage(txn, latest.messageId);
// Store the merged properties in each contact's group // Store the merged properties in each contact's group
for (Contact c : db.getContacts(txn)) { for (Contact c : db.getContacts(txn)) {
storeLocalProperties(txn, c, t, merged); Group g = getContactGroup(c);
latest = findLatest(txn, g.getId(), t, true);
version = latest == null ? 1 : latest.version + 1;
storeMessage(txn, g.getId(), t, merged, version,
true, true);
// Delete the previous update, if any
if (latest != null)
db.removeMessage(txn, latest.messageId);
} }
} }
}); });
@@ -344,34 +308,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
} }
} }
private void storeLocalProperties(Transaction txn, Contact c,
TransportId t, TransportProperties p)
throws DbException, FormatException {
Group g = getContactGroup(c);
LatestUpdate latest = findLatest(txn, g.getId(), t, true);
long version = latest == null ? 1 : latest.version + 1;
// Reflect any remote properties we've discovered
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn,
g.getId());
BdfDictionary discovered =
meta.getOptionalDictionary(GROUP_KEY_DISCOVERED);
TransportProperties combined;
if (discovered == null) {
combined = p;
} else {
combined = new TransportProperties(p);
TransportProperties d = clientHelper
.parseAndValidateTransportProperties(discovered);
for (Entry<String, String> e : d.entrySet()) {
String key = REFLECTED_PROPERTY_PREFIX + e.getKey();
combined.put(key, e.getValue());
}
}
storeMessage(txn, g.getId(), t, combined, version, true, true);
// Delete the previous update, if any
if (latest != null) db.removeMessage(txn, latest.messageId);
}
private Group getContactGroup(Contact c) { private Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID,
MAJOR_VERSION, c); MAJOR_VERSION, c);

View File

@@ -41,9 +41,7 @@ import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedE
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousPollEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousPollEvent;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
import org.briarproject.bramble.api.system.Wakeful;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -54,6 +52,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
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;
@@ -69,7 +68,6 @@ import static org.briarproject.bramble.api.contact.PendingContactState.ADDING_CO
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED; import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
import static org.briarproject.bramble.api.contact.PendingContactState.OFFLINE; import static org.briarproject.bramble.api.contact.PendingContactState.OFFLINE;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION; import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNull; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNull;
import static org.briarproject.bramble.rendezvous.RendezvousConstants.POLLING_INTERVAL_MS; import static org.briarproject.bramble.rendezvous.RendezvousConstants.POLLING_INTERVAL_MS;
import static org.briarproject.bramble.rendezvous.RendezvousConstants.RENDEZVOUS_TIMEOUT_MS; import static org.briarproject.bramble.rendezvous.RendezvousConstants.RENDEZVOUS_TIMEOUT_MS;
@@ -82,7 +80,7 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
private static final Logger LOG = private static final Logger LOG =
getLogger(RendezvousPollerImpl.class.getName()); getLogger(RendezvousPollerImpl.class.getName());
private final TaskScheduler scheduler; private final ScheduledExecutorService scheduler;
private final DatabaseComponent db; private final DatabaseComponent db;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final TransportCrypto transportCrypto; private final TransportCrypto transportCrypto;
@@ -104,12 +102,10 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
new HashMap<>(); new HashMap<>();
@Nullable @Nullable
private KeyPair handshakeKeyPair = null; private KeyPair handshakeKeyPair = null;
@Nullable
private Cancellable pollTask = null;
@Inject @Inject
RendezvousPollerImpl(@IoExecutor Executor ioExecutor, RendezvousPollerImpl(@IoExecutor Executor ioExecutor,
TaskScheduler scheduler, @Scheduler ScheduledExecutorService scheduler,
DatabaseComponent db, DatabaseComponent db,
IdentityManager identityManager, IdentityManager identityManager,
TransportCrypto transportCrypto, TransportCrypto transportCrypto,
@@ -148,6 +144,8 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
} catch (DbException e) { } catch (DbException e) {
throw new ServiceException(e); throw new ServiceException(e);
} }
scheduler.scheduleAtFixedRate(this::poll, POLLING_INTERVAL_MS,
POLLING_INTERVAL_MS, MILLISECONDS);
} }
@EventExecutor @EventExecutor
@@ -188,12 +186,6 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
} }
if (cs.numEndpoints == 0) broadcastState(p.getId(), OFFLINE); if (cs.numEndpoints == 0) broadcastState(p.getId(), OFFLINE);
else broadcastState(p.getId(), WAITING_FOR_CONNECTION); else broadcastState(p.getId(), WAITING_FOR_CONNECTION);
if (cryptoStates.size() == 1) {
LOG.info("Starting poller");
requireNull(pollTask);
pollTask = scheduler.scheduleWithFixedDelay(this::poll, worker,
POLLING_INTERVAL_MS, POLLING_INTERVAL_MS, MILLISECONDS);
}
} catch (DbException | GeneralSecurityException e) { } catch (DbException | GeneralSecurityException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
} }
@@ -213,12 +205,12 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
return plugin.createRendezvousEndpoint(k, cs.alice, h); return plugin.createRendezvousEndpoint(k, cs.alice, h);
} }
// Worker @Scheduler
@Wakeful
private void poll() { private void poll() {
LOG.info("Polling"); worker.execute(() -> {
removeExpiredPendingContacts(); removeExpiredPendingContacts();
for (PluginState ps : pluginStates.values()) poll(ps); for (PluginState ps : pluginStates.values()) poll(ps);
});
} }
// Worker // Worker
@@ -243,15 +235,9 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
RendezvousEndpoint endpoint = ps.endpoints.remove(p); RendezvousEndpoint endpoint = ps.endpoints.remove(p);
if (endpoint != null) tryToClose(endpoint, LOG, INFO); if (endpoint != null) tryToClose(endpoint, LOG, INFO);
} }
if (cryptoStates.isEmpty()) {
LOG.info("Stopping poller");
requireNonNull(pollTask).cancel();
pollTask = null;
}
} }
// Worker // Worker
@Wakeful
private void poll(PluginState ps) { private void poll(PluginState ps) {
if (ps.endpoints.isEmpty()) return; if (ps.endpoints.isEmpty()) return;
TransportId t = ps.plugin.getId(); TransportId t = ps.plugin.getId();

View File

@@ -1,15 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.system.Clock;
import dagger.Module;
import dagger.Provides;
@Module
public class ClockModule {
@Provides
Clock provideClock() {
return new SystemClock();
}
}

View File

@@ -1,23 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.util.concurrent.Executor;
import dagger.Module;
import dagger.Provides;
/**
* Provides a default implementation of {@link WakefulIoExecutor} for systems
* without wake locks.
*/
@Module
public class DefaultWakefulIoExecutorModule {
@Provides
@WakefulIoExecutor
Executor provideWakefulIoExecutor(@IoExecutor Executor ioExecutor) {
return ioExecutor;
}
}

View File

@@ -1,7 +1,8 @@
package org.briarproject.bramble.system; package org.briarproject.bramble.system;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@@ -14,26 +15,34 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
@Module @Module
public class DefaultTaskSchedulerModule { public class SystemModule {
public static class EagerSingletons { public static class EagerSingletons {
@Inject @Inject
TaskScheduler scheduler; @Scheduler
ScheduledExecutorService scheduledExecutorService;
} }
private final ScheduledExecutorService scheduledExecutorService; private final ScheduledExecutorService scheduler;
public DefaultTaskSchedulerModule() { public SystemModule() {
// Discard tasks that are submitted during shutdown // Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy = RejectedExecutionHandler policy =
new ScheduledThreadPoolExecutor.DiscardPolicy(); new ScheduledThreadPoolExecutor.DiscardPolicy();
scheduledExecutorService = new ScheduledThreadPoolExecutor(1, policy); scheduler = new ScheduledThreadPoolExecutor(1, policy);
}
@Provides
Clock provideClock() {
return new SystemClock();
} }
@Provides @Provides
@Singleton @Singleton
TaskScheduler provideTaskScheduler(LifecycleManager lifecycleManager) { @Scheduler
lifecycleManager.registerForShutdown(scheduledExecutorService); ScheduledExecutorService provideScheduledExecutorService(
return new TaskSchedulerImpl(scheduledExecutorService); LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(scheduler);
return scheduler;
} }
} }

View File

@@ -1,43 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.TaskScheduler;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.ThreadSafe;
/**
* A {@link TaskScheduler} that uses a {@link ScheduledExecutorService}.
*/
@ThreadSafe
@NotNullByDefault
class TaskSchedulerImpl implements TaskScheduler {
private final ScheduledExecutorService scheduledExecutorService;
TaskSchedulerImpl(ScheduledExecutorService scheduledExecutorService) {
this.scheduledExecutorService = scheduledExecutorService;
}
@Override
public Cancellable schedule(Runnable task, Executor executor, long delay,
TimeUnit unit) {
Runnable execute = () -> executor.execute(task);
ScheduledFuture<?> future =
scheduledExecutorService.schedule(execute, delay, unit);
return () -> future.cancel(false);
}
@Override
public Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
long delay, long interval, TimeUnit unit) {
Runnable execute = () -> executor.execute(task);
ScheduledFuture<?> future = scheduledExecutorService.
scheduleWithFixedDelay(execute, delay, interval, unit);
return () -> future.cancel(false);
}
}

View File

@@ -6,9 +6,10 @@ import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.system.Scheduler;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
@@ -21,15 +22,14 @@ class TransportKeyManagerFactoryImpl implements
private final DatabaseComponent db; private final DatabaseComponent db;
private final TransportCrypto transportCrypto; private final TransportCrypto transportCrypto;
private final Executor dbExecutor; private final Executor dbExecutor;
private final TaskScheduler scheduler; private final ScheduledExecutorService scheduler;
private final Clock clock; private final Clock clock;
@Inject @Inject
TransportKeyManagerFactoryImpl(DatabaseComponent db, TransportKeyManagerFactoryImpl(DatabaseComponent db,
TransportCrypto transportCrypto, TransportCrypto transportCrypto,
@DatabaseExecutor Executor dbExecutor, @DatabaseExecutor Executor dbExecutor,
TaskScheduler scheduler, @Scheduler ScheduledExecutorService scheduler, Clock clock) {
Clock clock) {
this.db = db; this.db = db;
this.transportCrypto = transportCrypto; this.transportCrypto = transportCrypto;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;

View File

@@ -6,14 +6,12 @@ import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.system.Wakeful;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
@@ -26,6 +24,7 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
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.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -54,7 +53,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final DatabaseComponent db; private final DatabaseComponent db;
private final TransportCrypto transportCrypto; private final TransportCrypto transportCrypto;
private final Executor dbExecutor; private final Executor dbExecutor;
private final TaskScheduler scheduler; private final ScheduledExecutorService scheduler;
private final Clock clock; private final Clock clock;
private final TransportId transportId; private final TransportId transportId;
private final long timePeriodLength; private final long timePeriodLength;
@@ -73,12 +72,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
pendingContactOutContexts = new HashMap<>(); pendingContactOutContexts = new HashMap<>();
TransportKeyManagerImpl(DatabaseComponent db, TransportKeyManagerImpl(DatabaseComponent db,
TransportCrypto transportCrypto, TransportCrypto transportCrypto, Executor dbExecutor,
Executor dbExecutor, @Scheduler ScheduledExecutorService scheduler, Clock clock,
TaskScheduler scheduler, TransportId transportId, long maxLatency) {
Clock clock,
TransportId transportId,
long maxLatency) {
this.db = db; this.db = db;
this.transportCrypto = transportCrypto; this.transportCrypto = transportCrypto;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
@@ -200,17 +196,17 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private void scheduleKeyUpdate(long now) { private void scheduleKeyUpdate(long now) {
long delay = timePeriodLength - now % timePeriodLength; long delay = timePeriodLength - now % timePeriodLength;
scheduler.schedule(this::updateKeys, dbExecutor, delay, MILLISECONDS); scheduler.schedule((Runnable) this::updateKeys, delay, MILLISECONDS);
} }
@DatabaseExecutor
@Wakeful
private void updateKeys() { private void updateKeys() {
try { dbExecutor.execute(() -> {
db.transaction(false, this::updateKeys); try {
} catch (DbException e) { db.transaction(false, this::updateKeys);
logException(LOG, WARNING, e); } catch (DbException e) {
} logException(LOG, WARNING, e);
}
});
} }
@Override @Override
@@ -443,8 +439,6 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
} }
@DatabaseExecutor
@Wakeful
private void updateKeys(Transaction txn) throws DbException { private void updateKeys(Transaction txn) throws DbException {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
lock.lock(); lock.lock();

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble;
import org.briarproject.bramble.system.DefaultTaskSchedulerModule;
public interface BrambleCoreIntegrationTestEagerSingletons
extends BrambleCoreEagerSingletons {
void inject(DefaultTaskSchedulerModule.EagerSingletons init);
class Helper {
public static void injectEagerSingletons(
BrambleCoreIntegrationTestEagerSingletons c) {
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(c);
c.inject(new DefaultTaskSchedulerModule.EagerSingletons());
}
}
}

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.contact; package org.briarproject.bramble.contact;
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons; import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
@@ -62,13 +62,11 @@ public class ContactExchangeIntegrationTest extends BrambleTestCase {
alice = DaggerContactExchangeIntegrationTestComponent.builder() alice = DaggerContactExchangeIntegrationTestComponent.builder()
.testDatabaseConfigModule( .testDatabaseConfigModule(
new TestDatabaseConfigModule(aliceDir)).build(); new TestDatabaseConfigModule(aliceDir)).build();
BrambleCoreIntegrationTestEagerSingletons.Helper BrambleCoreEagerSingletons.Helper.injectEagerSingletons(alice);
.injectEagerSingletons(alice);
bob = DaggerContactExchangeIntegrationTestComponent.builder() bob = DaggerContactExchangeIntegrationTestComponent.builder()
.testDatabaseConfigModule(new TestDatabaseConfigModule(bobDir)) .testDatabaseConfigModule(new TestDatabaseConfigModule(bobDir))
.build(); .build();
BrambleCoreIntegrationTestEagerSingletons.Helper BrambleCoreEagerSingletons.Helper.injectEagerSingletons(bob);
.injectEagerSingletons(bob);
// Set up the devices and get the identities // Set up the devices and get the identities
aliceIdentity = setUp(alice, "Alice"); aliceIdentity = setUp(alice, "Alice");
bobIdentity = setUp(bob, "Bob"); bobIdentity = setUp(bob, "Bob");

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.contact; package org.briarproject.bramble.contact;
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons; import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.api.connection.ConnectionManager; import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.ContactExchangeManager; import org.briarproject.bramble.api.contact.ContactExchangeManager;
@@ -23,7 +23,7 @@ import dagger.Component;
BrambleCoreModule.class BrambleCoreModule.class
}) })
interface ContactExchangeIntegrationTestComponent interface ContactExchangeIntegrationTestComponent
extends BrambleCoreIntegrationTestEagerSingletons { extends BrambleCoreEagerSingletons {
ConnectionManager getConnectionManager(); ConnectionManager getConnectionManager();

View File

@@ -109,8 +109,8 @@ public class PluginManagerImplTest extends BrambleTestCase {
oneOf(duplexPlugin).stop(); oneOf(duplexPlugin).stop();
}}); }});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, ioExecutor, PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
eventBus, pluginConfig, connectionManager, settingsManager, pluginConfig, connectionManager, settingsManager,
transportPropertyManager); transportPropertyManager);
// Two plugins should be started and stopped // Two plugins should be started and stopped

View File

@@ -19,8 +19,6 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties; 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.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor; import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.RunAction; import org.briarproject.bramble.test.RunAction;
@@ -32,6 +30,8 @@ import org.junit.Test;
import java.security.SecureRandom; import java.security.SecureRandom;
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.ScheduledFuture;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
@@ -45,7 +45,8 @@ import static org.briarproject.bramble.test.TestUtils.getTransportId;
public class PollerImplTest extends BrambleMockTestCase { public class PollerImplTest extends BrambleMockTestCase {
private final TaskScheduler scheduler = context.mock(TaskScheduler.class); private final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
private final ConnectionManager connectionManager = private final ConnectionManager connectionManager =
context.mock(ConnectionManager.class); context.mock(ConnectionManager.class);
private final ConnectionRegistry connectionRegistry = private final ConnectionRegistry connectionRegistry =
@@ -55,11 +56,10 @@ public class PollerImplTest extends BrambleMockTestCase {
private final TransportPropertyManager transportPropertyManager = private final TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class); context.mock(TransportPropertyManager.class);
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final Cancellable cancellable = context.mock(Cancellable.class); private final ScheduledFuture future = context.mock(ScheduledFuture.class);
private final SecureRandom random; private final SecureRandom random;
private final Executor ioExecutor = new ImmediateExecutor(); private final Executor ioExecutor = new ImmediateExecutor();
private final Executor wakefulIoExecutor = new ImmediateExecutor();
private final TransportId transportId = getTransportId(); private final TransportId transportId = getTransportId();
private final ContactId contactId = getContactId(); private final ContactId contactId = getContactId();
private final TransportProperties properties = new TransportProperties(); private final TransportProperties properties = new TransportProperties();
@@ -75,9 +75,9 @@ public class PollerImplTest extends BrambleMockTestCase {
@Before @Before
public void setUp() { public void setUp() {
poller = new PollerImpl(ioExecutor, wakefulIoExecutor, scheduler, poller = new PollerImpl(ioExecutor, scheduler, connectionManager,
connectionManager, connectionRegistry, pluginManager, connectionRegistry, pluginManager, transportPropertyManager,
transportPropertyManager, random, clock); random, clock);
} }
@Test @Test
@@ -235,9 +235,8 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) pollingInterval), with((long) pollingInterval), with(MILLISECONDS));
with(MILLISECONDS)); will(returnValue(future));
will(returnValue(cancellable));
}}); }});
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
@@ -264,9 +263,8 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) pollingInterval), with((long) pollingInterval), with(MILLISECONDS));
with(MILLISECONDS)); will(returnValue(future));
will(returnValue(cancellable));
// Second event // Second event
// Get the plugin // Get the plugin
oneOf(pluginManager).getPlugin(transportId); oneOf(pluginManager).getPlugin(transportId);
@@ -307,9 +305,8 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) pollingInterval), with((long) pollingInterval), with(MILLISECONDS));
with(MILLISECONDS)); will(returnValue(future));
will(returnValue(cancellable));
// Second event // Second event
// Get the plugin // Get the plugin
oneOf(pluginManager).getPlugin(transportId); oneOf(pluginManager).getPlugin(transportId);
@@ -322,10 +319,9 @@ public class PollerImplTest extends BrambleMockTestCase {
will(returnValue(pollingInterval - 2)); will(returnValue(pollingInterval - 2));
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now + 1)); will(returnValue(now + 1));
oneOf(cancellable).cancel(); oneOf(future).cancel(false);
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) pollingInterval - 2), with((long) pollingInterval - 2), with(MILLISECONDS));
with(MILLISECONDS));
}}); }});
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
@@ -350,9 +346,9 @@ public class PollerImplTest extends BrambleMockTestCase {
// Schedule a polling task immediately // Schedule a polling task immediately
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L),
with(ioExecutor), with(0L), with(MILLISECONDS)); with(MILLISECONDS));
will(returnValue(cancellable)); will(returnValue(future));
will(new RunAction()); will(new RunAction());
// Running the polling task schedules the next polling task // Running the polling task schedules the next polling task
oneOf(plugin).getPollingInterval(); oneOf(plugin).getPollingInterval();
@@ -362,9 +358,8 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) (pollingInterval * 0.5)), with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
with(MILLISECONDS)); will(returnValue(future));
will(returnValue(cancellable));
// Get the transport properties and connected contacts // Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId); oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties))); will(returnValue(singletonMap(contactId, properties)));
@@ -394,9 +389,9 @@ public class PollerImplTest extends BrambleMockTestCase {
// Schedule a polling task immediately // Schedule a polling task immediately
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L),
with(ioExecutor), with(0L), with(MILLISECONDS)); with(MILLISECONDS));
will(returnValue(cancellable)); will(returnValue(future));
will(new RunAction()); will(new RunAction());
// Running the polling task schedules the next polling task // Running the polling task schedules the next polling task
oneOf(plugin).getPollingInterval(); oneOf(plugin).getPollingInterval();
@@ -406,9 +401,8 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) (pollingInterval * 0.5)), with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
with(MILLISECONDS)); will(returnValue(future));
will(returnValue(cancellable));
// Get the transport properties and connected contacts // Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId); oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties))); will(returnValue(singletonMap(contactId, properties)));
@@ -436,11 +430,11 @@ public class PollerImplTest extends BrambleMockTestCase {
// Schedule a polling task immediately // Schedule a polling task immediately
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L),
with(ioExecutor), with(0L), with(MILLISECONDS)); with(MILLISECONDS));
will(returnValue(cancellable)); will(returnValue(future));
// The plugin is deactivated before the task runs - cancel the task // The plugin is deactivated before the task runs - cancel the task
oneOf(cancellable).cancel(); oneOf(future).cancel(false);
}}); }});
poller.eventOccurred(new TransportActiveEvent(transportId)); poller.eventOccurred(new TransportActiveEvent(transportId));
@@ -461,9 +455,8 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) pollingInterval), with((long) pollingInterval), with(MILLISECONDS));
with(MILLISECONDS)); will(returnValue(future));
will(returnValue(cancellable));
}}); }});
} }

View File

@@ -22,13 +22,11 @@ import java.net.InetSocketAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.Collection;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import static java.net.NetworkInterface.getNetworkInterfaces; import static java.net.NetworkInterface.getNetworkInterfaces;
import static java.util.Collections.emptyList;
import static java.util.Collections.list; import static java.util.Collections.list;
import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.Executors.newCachedThreadPool;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
@@ -53,8 +51,7 @@ public class LanTcpPluginTest extends BrambleTestCase {
@Before @Before
public void setUp() { public void setUp() {
callback = new Callback(); callback = new Callback();
plugin = new LanTcpPlugin(ioExecutor, ioExecutor, backoff, callback, plugin = new LanTcpPlugin(ioExecutor, backoff, callback, 0, 0, 1000) {
0, 0, 1000) {
@Override @Override
protected boolean canConnectToOwnAddress() { protected boolean canConnectToOwnAddress() {
return true; return true;
@@ -323,11 +320,6 @@ public class LanTcpPluginTest extends BrambleTestCase {
return local; return local;
} }
@Override
public Collection<TransportProperties> getRemoteProperties() {
return emptyList();
}
@Override @Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
} }

View File

@@ -8,15 +8,11 @@ import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry; import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser; import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.CommitAction;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.EventAction;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.event.RemoteTransportPropertiesUpdatedEvent;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
@@ -49,7 +45,6 @@ import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class TransportPropertyManagerImplTest extends BrambleMockTestCase { public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
@@ -64,28 +59,23 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final BdfDictionary fooPropertiesDict, barPropertiesDict; private final BdfDictionary fooPropertiesDict = BdfDictionary.of(
private final BdfDictionary discoveredPropertiesDict, mergedPropertiesDict; new BdfEntry("fooKey1", "fooValue1"),
new BdfEntry("fooKey2", "fooValue2")
);
private final BdfDictionary barPropertiesDict = BdfDictionary.of(
new BdfEntry("barKey1", "barValue1"),
new BdfEntry("barKey2", "barValue2")
);
private final TransportProperties fooProperties, barProperties; private final TransportProperties fooProperties, barProperties;
private final TransportProperties discoveredProperties;
public TransportPropertyManagerImplTest() { public TransportPropertyManagerImplTest() throws Exception {
fooProperties = new TransportProperties(); fooProperties = new TransportProperties();
fooProperties.put("fooKey1", "fooValue1"); for (String key : fooPropertiesDict.keySet())
fooProperties.put("fooKey2", "fooValue2"); fooProperties.put(key, fooPropertiesDict.getString(key));
fooPropertiesDict = new BdfDictionary(fooProperties);
barProperties = new TransportProperties(); barProperties = new TransportProperties();
barProperties.put("barKey1", "barValue1"); for (String key : barPropertiesDict.keySet())
barProperties.put("barKey2", "barValue2"); barProperties.put(key, barPropertiesDict.getString(key));
barPropertiesDict = new BdfDictionary(barProperties);
discoveredProperties = new TransportProperties();
discoveredProperties.put("fooKey3", "fooValue3");
discoveredPropertiesDict = new BdfDictionary(discoveredProperties);
mergedPropertiesDict = new BdfDictionary(fooProperties);
mergedPropertiesDict.put("u:fooKey3", "fooValue3");
} }
private TransportPropertyManagerImpl createInstance() { private TransportPropertyManagerImpl createInstance() {
@@ -231,7 +221,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
TransportPropertyManagerImpl t = createInstance(); TransportPropertyManagerImpl t = createInstance();
assertFalse(t.incomingMessage(txn, message, meta)); assertFalse(t.incomingMessage(txn, message, meta));
assertTrue(hasEvent(txn, RemoteTransportPropertiesUpdatedEvent.class));
} }
@Test @Test
@@ -270,7 +259,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
TransportPropertyManagerImpl t = createInstance(); TransportPropertyManagerImpl t = createInstance();
assertFalse(t.incomingMessage(txn, message, meta)); assertFalse(t.incomingMessage(txn, message, meta));
assertTrue(hasEvent(txn, RemoteTransportPropertiesUpdatedEvent.class));
} }
@Test @Test
@@ -304,12 +292,10 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// The update being delivered (version 3) should be deleted // The update being delivered (version 3) should be deleted
oneOf(db).deleteMessage(txn, message.getId()); oneOf(db).deleteMessage(txn, message.getId());
oneOf(db).deleteMessageMetadata(txn, message.getId()); oneOf(db).deleteMessageMetadata(txn, message.getId());
// No event should be broadcast
}}); }});
TransportPropertyManagerImpl t = createInstance(); TransportPropertyManagerImpl t = createInstance();
assertFalse(t.incomingMessage(txn, message, meta)); assertFalse(t.incomingMessage(txn, message, meta));
assertFalse(hasEvent(txn, RemoteTransportPropertiesUpdatedEvent.class));
} }
@Test @Test
@@ -580,10 +566,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Contact contact = getContact(); Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
// Property with an empty value should be discarded
TransportProperties properties = new TransportProperties(fooProperties);
properties.put("fooKey3", "");
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(db).transaction(with(false), withDbRunnable(txn));
// There are no existing properties to merge with // There are no existing properties to merge with
@@ -602,56 +584,10 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());
will(returnValue(emptyMap())); will(returnValue(emptyMap()));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(new BdfDictionary()));
expectStoreMessage(txn, contactGroup.getId(), "foo", expectStoreMessage(txn, contactGroup.getId(), "foo",
fooPropertiesDict, 1, true, true); fooPropertiesDict, 1, true, true);
}}); }});
TransportPropertyManagerImpl t = createInstance();
t.mergeLocalProperties(new TransportId("foo"), properties);
}
@Test
public void testMergingNewPropertiesCreatesUpdateWithReflectedProperties()
throws Exception {
Transaction txn = new Transaction(null, false);
Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
BdfDictionary contactGroupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_DISCOVERED, discoveredPropertiesDict)
);
context.checking(new DbExpectations() {{
oneOf(db).transaction(with(false), withDbRunnable(txn));
// There are no existing properties to merge with
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
localGroup.getId());
will(returnValue(emptyMap()));
// Store the new properties in the local group, version 1
expectStoreMessage(txn, localGroup.getId(), "foo",
fooPropertiesDict, 1, true, false);
// Store the new properties in each contact's group, version 1
oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(emptyMap()));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(contactGroupMeta));
// Reflect discovered properties
oneOf(clientHelper).parseAndValidateTransportProperties(
discoveredPropertiesDict);
will(returnValue(discoveredProperties));
expectStoreMessage(txn, contactGroup.getId(), "foo",
mergedPropertiesDict, 1, true, true);
}});
TransportPropertyManagerImpl t = createInstance(); TransportPropertyManagerImpl t = createInstance();
t.mergeLocalProperties(new TransportId("foo"), fooProperties); t.mergeLocalProperties(new TransportId("foo"), fooProperties);
} }
@@ -667,79 +603,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry(MSG_KEY_LOCAL, true)
); );
MessageId localGroupUpdateId = new MessageId(getRandomId()); MessageId localGroupUpdateId = new MessageId(getRandomId());
Map<MessageId, BdfDictionary> localGroupMessageMetadata =
singletonMap(localGroupUpdateId, oldMetadata);
MessageId contactGroupUpdateId = new MessageId(getRandomId());
Map<MessageId, BdfDictionary> contactGroupMessageMetadata =
singletonMap(contactGroupUpdateId, oldMetadata);
TransportProperties oldProperties = new TransportProperties();
oldProperties.put("fooKey1", "oldFooValue1");
oldProperties.put("fooKey3", "oldFooValue3");
BdfDictionary oldPropertiesDict = BdfDictionary.of(
new BdfEntry("fooKey1", "oldFooValue1"),
new BdfEntry("fooKey3", "oldFooValue3")
);
BdfList oldUpdate = BdfList.of("foo", 1, oldPropertiesDict);
// Property assigned an empty value should be removed
TransportProperties properties = new TransportProperties(fooProperties);
properties.put("fooKey3", "");
context.checking(new DbExpectations() {{
oneOf(db).transaction(with(false), withDbRunnable(txn));
// Merge the new properties with the existing properties
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
localGroup.getId());
will(returnValue(localGroupMessageMetadata));
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId);
will(returnValue(oldUpdate));
oneOf(clientHelper).parseAndValidateTransportProperties(
oldPropertiesDict);
will(returnValue(oldProperties));
// Store the merged properties in the local group, version 2
expectStoreMessage(txn, localGroup.getId(), "foo",
fooPropertiesDict, 2, true, false);
// Delete the previous update
oneOf(db).removeMessage(txn, localGroupUpdateId);
// Store the merged properties in each contact's group, version 2
oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(contactGroupMessageMetadata));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(new BdfDictionary()));
expectStoreMessage(txn, contactGroup.getId(), "foo",
fooPropertiesDict, 2, true, true);
// Delete the previous update
oneOf(db).removeMessage(txn, contactGroupUpdateId);
}});
TransportPropertyManagerImpl t = createInstance();
t.mergeLocalProperties(new TransportId("foo"), properties);
}
@Test
public void testMergingUpdatedPropertiesCreatesUpdateWithReflectedProperties()
throws Exception {
Transaction txn = new Transaction(null, false);
Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
BdfDictionary contactGroupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_DISCOVERED, discoveredPropertiesDict)
);
BdfDictionary oldMetadata = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"),
new BdfEntry(MSG_KEY_VERSION, 1),
new BdfEntry(MSG_KEY_LOCAL, true)
);
MessageId localGroupUpdateId = new MessageId(getRandomId());
Map<MessageId, BdfDictionary> localGroupMessageMetadata = Map<MessageId, BdfDictionary> localGroupMessageMetadata =
singletonMap(localGroupUpdateId, oldMetadata); singletonMap(localGroupUpdateId, oldMetadata);
MessageId contactGroupUpdateId = new MessageId(getRandomId()); MessageId contactGroupUpdateId = new MessageId(getRandomId());
@@ -777,15 +640,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());
will(returnValue(contactGroupMessageMetadata)); will(returnValue(contactGroupMessageMetadata));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(contactGroupMeta));
// Reflect discovered properties
oneOf(clientHelper).parseAndValidateTransportProperties(
discoveredPropertiesDict);
will(returnValue(discoveredProperties));
expectStoreMessage(txn, contactGroup.getId(), "foo", expectStoreMessage(txn, contactGroup.getId(), "foo",
mergedPropertiesDict, 2, true, true); fooPropertiesDict, 2, true, true);
// Delete the previous update // Delete the previous update
oneOf(db).removeMessage(txn, contactGroupUpdateId); oneOf(db).removeMessage(txn, contactGroupUpdateId);
}}); }});
@@ -851,15 +707,4 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
false); false);
}}); }});
} }
private boolean hasEvent(Transaction txn,
Class<? extends Event> eventClass) {
for (CommitAction action : txn.getActions()) {
if (action instanceof EventAction) {
Event event = ((EventAction) action).getEvent();
if (eventClass.isInstance(event)) return true;
}
}
return false;
}
} }

View File

@@ -26,8 +26,6 @@ import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedE
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousPollEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousPollEvent;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.CaptureArgumentAction; import org.briarproject.bramble.test.CaptureArgumentAction;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
@@ -39,6 +37,7 @@ import org.junit.Test;
import java.util.Random; import java.util.Random;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
@@ -61,7 +60,8 @@ import static org.briarproject.bramble.test.TestUtils.getTransportProperties;
public class RendezvousPollerImplTest extends BrambleMockTestCase { public class RendezvousPollerImplTest extends BrambleMockTestCase {
private final TaskScheduler scheduler = context.mock(TaskScheduler.class); private final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
private final DatabaseComponent db = context.mock(DatabaseComponent.class); private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final IdentityManager identityManager = private final IdentityManager identityManager =
context.mock(IdentityManager.class); context.mock(IdentityManager.class);
@@ -80,7 +80,6 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
context.mock(KeyMaterialSource.class); context.mock(KeyMaterialSource.class);
private final RendezvousEndpoint rendezvousEndpoint = private final RendezvousEndpoint rendezvousEndpoint =
context.mock(RendezvousEndpoint.class); context.mock(RendezvousEndpoint.class);
private final Cancellable cancellable = context.mock(Cancellable.class);
private final Executor ioExecutor = new ImmediateExecutor(); private final Executor ioExecutor = new ImmediateExecutor();
private final PendingContact pendingContact = getPendingContact(); private final PendingContact pendingContact = getPendingContact();
@@ -109,7 +108,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
long beforeExpiry = pendingContact.getTimestamp() long beforeExpiry = pendingContact.getTimestamp()
+ RENDEZVOUS_TIMEOUT_MS - 1000; + RENDEZVOUS_TIMEOUT_MS - 1000;
long afterExpiry = beforeExpiry + POLLING_INTERVAL_MS; long afterExpiry = beforeExpiry + POLLING_INTERVAL_MS;
AtomicReference<Runnable> capturePollTask; AtomicReference<Runnable> capturePollTask = new AtomicReference<>();
// Start the service // Start the service
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
@@ -123,17 +122,21 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(new PredicateMatcher<>( oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
PendingContactStateChangedEvent.class, e -> PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == OFFLINE))); e.getPendingContactState() == OFFLINE)));
// Capture the poll task
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
with(MILLISECONDS));
will(new CaptureArgumentAction<>(capturePollTask, Runnable.class,
0));
}}); }});
expectDeriveRendezvousKey(); expectDeriveRendezvousKey();
capturePollTask = expectSchedulePolling();
rendezvousPoller.startService(); rendezvousPoller.startService();
context.assertIsSatisfied(); context.assertIsSatisfied();
// Run the poll task - pending contact expires, polling is cancelled // Run the poll task - pending contact expires
expectPendingContactExpires(afterExpiry); expectPendingContactExpires(afterExpiry);
expectCancelPolling();
capturePollTask.get().run(); capturePollTask.get().run();
} }
@@ -155,6 +158,10 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(new PredicateMatcher<>( oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
PendingContactStateChangedEvent.class, e -> PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == FAILED))); e.getPendingContactState() == FAILED)));
// Schedule the poll task
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
with(MILLISECONDS));
}}); }});
rendezvousPoller.startService(); rendezvousPoller.startService();
@@ -177,8 +184,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
rendezvousPoller.eventOccurred(new TransportActiveEvent(transportId)); rendezvousPoller.eventOccurred(new TransportActiveEvent(transportId));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Add the pending contact - endpoint should be created and polled, // Add the pending contact - endpoint should be created and polled
// polling should be scheduled
expectAddPendingContact(beforeExpiry, WAITING_FOR_CONNECTION); expectAddPendingContact(beforeExpiry, WAITING_FOR_CONNECTION);
expectDeriveRendezvousKey(); expectDeriveRendezvousKey();
expectCreateEndpoint(); expectCreateEndpoint();
@@ -195,16 +201,12 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
any(ConnectionHandler.class))))); any(ConnectionHandler.class)))));
}}); }});
expectSchedulePolling();
rendezvousPoller.eventOccurred( rendezvousPoller.eventOccurred(
new PendingContactAddedEvent(pendingContact)); new PendingContactAddedEvent(pendingContact));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Remove the pending contact - endpoint should be closed, // Remove the pending contact - endpoint should be closed
// polling should be cancelled
expectCloseEndpoint(); expectCloseEndpoint();
expectCancelPolling();
rendezvousPoller.eventOccurred( rendezvousPoller.eventOccurred(
new PendingContactRemovedEvent(pendingContact.getId())); new PendingContactRemovedEvent(pendingContact.getId()));
@@ -220,10 +222,10 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
long beforeExpiry = pendingContact.getTimestamp() long beforeExpiry = pendingContact.getTimestamp()
+ RENDEZVOUS_TIMEOUT_MS - 1000; + RENDEZVOUS_TIMEOUT_MS - 1000;
long afterExpiry = beforeExpiry + POLLING_INTERVAL_MS; long afterExpiry = beforeExpiry + POLLING_INTERVAL_MS;
AtomicReference<Runnable> capturePollTask;
// Start the service // Start the service, capturing the poll task
expectStartupWithNoPendingContacts(); AtomicReference<Runnable> capturePollTask =
expectStartupWithNoPendingContacts();
rendezvousPoller.startService(); rendezvousPoller.startService();
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -234,8 +236,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
rendezvousPoller.eventOccurred(new TransportActiveEvent(transportId)); rendezvousPoller.eventOccurred(new TransportActiveEvent(transportId));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Add the pending contact - endpoint should be created and polled, // Add the pending contact - endpoint should be created and polled
// polling should be scheduled
expectAddPendingContact(beforeExpiry, WAITING_FOR_CONNECTION); expectAddPendingContact(beforeExpiry, WAITING_FOR_CONNECTION);
expectDeriveRendezvousKey(); expectDeriveRendezvousKey();
expectCreateEndpoint(); expectCreateEndpoint();
@@ -252,17 +253,13 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
any(ConnectionHandler.class))))); any(ConnectionHandler.class)))));
}}); }});
capturePollTask = expectSchedulePolling();
rendezvousPoller.eventOccurred( rendezvousPoller.eventOccurred(
new PendingContactAddedEvent(pendingContact)); new PendingContactAddedEvent(pendingContact));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Run the poll task - pending contact expires, endpoint is closed, // Run the poll task - pending contact expires, endpoint is closed
// polling is cancelled
expectPendingContactExpires(afterExpiry); expectPendingContactExpires(afterExpiry);
expectCloseEndpoint(); expectCloseEndpoint();
expectCancelPolling();
capturePollTask.get().run(); capturePollTask.get().run();
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -290,7 +287,6 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
// Add the pending contact - no endpoints should be created yet // Add the pending contact - no endpoints should be created yet
expectAddPendingContact(beforeExpiry, OFFLINE); expectAddPendingContact(beforeExpiry, OFFLINE);
expectDeriveRendezvousKey(); expectDeriveRendezvousKey();
expectSchedulePolling();
rendezvousPoller.eventOccurred( rendezvousPoller.eventOccurred(
new PendingContactAddedEvent(pendingContact)); new PendingContactAddedEvent(pendingContact));
@@ -312,8 +308,6 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
context.assertIsSatisfied(); context.assertIsSatisfied();
// Remove the pending contact - endpoint is already closed // Remove the pending contact - endpoint is already closed
expectCancelPolling();
rendezvousPoller.eventOccurred( rendezvousPoller.eventOccurred(
new PendingContactRemovedEvent(pendingContact.getId())); new PendingContactRemovedEvent(pendingContact.getId()));
} }
@@ -355,9 +349,8 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
rendezvousPoller.startService(); rendezvousPoller.startService();
context.assertIsSatisfied(); context.assertIsSatisfied();
// Run the poll task - pending contact expires, polling is cancelled // Run the poll task - pending contact expires
expectPendingContactExpires(afterExpiry); expectPendingContactExpires(afterExpiry);
expectCancelPolling();
capturePollTask.get().run(); capturePollTask.get().run();
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -393,9 +386,8 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
new RendezvousConnectionOpenedEvent(pendingContact.getId())); new RendezvousConnectionOpenedEvent(pendingContact.getId()));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Run the poll task - pending contact expires, polling is cancelled // Run the poll task - pending contact expires
expectPendingContactExpires(afterExpiry); expectPendingContactExpires(afterExpiry);
expectCancelPolling();
capturePollTask.get().run(); capturePollTask.get().run();
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -426,9 +418,8 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
new RendezvousConnectionOpenedEvent(pendingContact.getId())); new RendezvousConnectionOpenedEvent(pendingContact.getId()));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Run the poll task - pending contact expires, polling is cancelled // Run the poll task - pending contact expires
expectPendingContactExpires(afterExpiry); expectPendingContactExpires(afterExpiry);
expectCancelPolling();
capturePollTask.get().run(); capturePollTask.get().run();
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -457,8 +448,6 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
context.assertIsSatisfied(); context.assertIsSatisfied();
// Pending contact is removed - no event should be broadcast // Pending contact is removed - no event should be broadcast
expectCancelPolling();
rendezvousPoller.eventOccurred( rendezvousPoller.eventOccurred(
new PendingContactRemovedEvent(pendingContact.getId())); new PendingContactRemovedEvent(pendingContact.getId()));
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -468,35 +457,25 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
pendingContact.getId(), false)); pendingContact.getId(), false));
} }
private AtomicReference<Runnable> expectSchedulePolling() { private AtomicReference<Runnable> expectStartupWithNoPendingContacts()
AtomicReference<Runnable> capturePollTask = new AtomicReference<>(); throws Exception {
context.checking(new Expectations() {{
oneOf(scheduler).scheduleWithFixedDelay(with(any(Runnable.class)),
with(any(Executor.class)), with(POLLING_INTERVAL_MS),
with(POLLING_INTERVAL_MS), with(MILLISECONDS));
will(doAll(new CaptureArgumentAction<>(capturePollTask,
Runnable.class, 0), returnValue(cancellable)));
}});
return capturePollTask;
}
private void expectCancelPolling() {
context.checking(new Expectations() {{
oneOf(cancellable).cancel();
}});
}
private void expectStartupWithNoPendingContacts() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
AtomicReference<Runnable> capturePollTask = new AtomicReference<>();
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
// Load the pending contacts // Load the pending contacts
oneOf(db).transaction(with(true), withDbRunnable(txn)); oneOf(db).transaction(with(true), withDbRunnable(txn));
oneOf(db).getPendingContacts(txn); oneOf(db).getPendingContacts(txn);
will(returnValue(emptyList())); will(returnValue(emptyList()));
// Capture the poll task
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
with(MILLISECONDS));
will(new CaptureArgumentAction<>(capturePollTask, Runnable.class,
0));
}}); }});
return capturePollTask;
} }
private void expectAddPendingContact(long now, private void expectAddPendingContact(long now,
@@ -552,6 +531,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
private AtomicReference<Runnable> expectStartupWithPendingContact(long now) private AtomicReference<Runnable> expectStartupWithPendingContact(long now)
throws Exception { throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
AtomicReference<Runnable> capturePollTask = new AtomicReference<>();
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
// Load the pending contacts // Load the pending contacts
@@ -564,10 +544,17 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(new PredicateMatcher<>( oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
PendingContactStateChangedEvent.class, e -> PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == OFFLINE))); e.getPendingContactState() == OFFLINE)));
// Capture the poll task
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
with(MILLISECONDS));
will(new CaptureArgumentAction<>(capturePollTask, Runnable.class,
0));
}}); }});
expectDeriveRendezvousKey(); expectDeriveRendezvousKey();
return expectSchedulePolling();
return capturePollTask;
} }
private void expectPendingContactExpires(long now) { private void expectPendingContactExpires(long now) {

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.sync; package org.briarproject.bramble.sync;
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons; import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
@@ -73,8 +73,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
SyncIntegrationTestComponent component = SyncIntegrationTestComponent component =
DaggerSyncIntegrationTestComponent.builder().build(); DaggerSyncIntegrationTestComponent.builder().build();
BrambleCoreIntegrationTestEagerSingletons.Helper BrambleCoreEagerSingletons.Helper.injectEagerSingletons(component);
.injectEagerSingletons(component);
component.inject(this); component.inject(this);
contactId = getContactId(); contactId = getContactId();

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.sync; package org.briarproject.bramble.sync;
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons; import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule; import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
@@ -13,8 +13,7 @@ import dagger.Component;
BrambleCoreIntegrationTestModule.class, BrambleCoreIntegrationTestModule.class,
BrambleCoreModule.class BrambleCoreModule.class
}) })
interface SyncIntegrationTestComponent extends interface SyncIntegrationTestComponent extends BrambleCoreEagerSingletons {
BrambleCoreIntegrationTestEagerSingletons {
void inject(SyncIntegrationTest testCase); void inject(SyncIntegrationTest testCase);
} }

View File

@@ -3,8 +3,6 @@ package org.briarproject.bramble.test;
import org.briarproject.bramble.api.FeatureFlags; import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.bramble.battery.DefaultBatteryManagerModule; import org.briarproject.bramble.battery.DefaultBatteryManagerModule;
import org.briarproject.bramble.event.DefaultEventExecutorModule; import org.briarproject.bramble.event.DefaultEventExecutorModule;
import org.briarproject.bramble.system.DefaultTaskSchedulerModule;
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@@ -12,8 +10,6 @@ import dagger.Provides;
@Module(includes = { @Module(includes = {
DefaultBatteryManagerModule.class, DefaultBatteryManagerModule.class,
DefaultEventExecutorModule.class, DefaultEventExecutorModule.class,
DefaultTaskSchedulerModule.class,
DefaultWakefulIoExecutorModule.class,
TestDatabaseConfigModule.class, TestDatabaseConfigModule.class,
TestPluginConfigModule.class, TestPluginConfigModule.class,
TestSecureRandomModule.class TestSecureRandomModule.class

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
@@ -30,6 +29,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
@@ -55,7 +55,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
private final TransportCrypto transportCrypto = private final TransportCrypto transportCrypto =
context.mock(TransportCrypto.class); context.mock(TransportCrypto.class);
private final Executor dbExecutor = context.mock(Executor.class); private final Executor dbExecutor = context.mock(Executor.class);
private final TaskScheduler scheduler = context.mock(TaskScheduler.class); private final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final TransportId transportId = getTransportId(); private final TransportId transportId = getTransportId();
@@ -117,8 +118,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
new TransportKeySet(keySetId, contactId, null, updated))); new TransportKeySet(keySetId, contactId, null, updated)));
// Schedule a key update at the start of the next time period // Schedule a key update at the start of the next time period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(dbExecutor), with(timePeriodLength - 1), with(timePeriodLength - 1), with(MILLISECONDS));
with(MILLISECONDS));
}}); }});
transportKeyManager.start(txn); transportKeyManager.start(txn);
@@ -421,8 +421,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
} }
// Schedule a key update at the start of the next time period // Schedule a key update at the start of the next time period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(dbExecutor), with(timePeriodLength), with(timePeriodLength), with(MILLISECONDS));
with(MILLISECONDS)); will(new RunAction());
oneOf(dbExecutor).execute(with(any(Runnable.class)));
will(new RunAction()); will(new RunAction());
// Start a transaction for updating keys // Start a transaction for updating keys
oneOf(db).transaction(with(false), withDbRunnable(txn1)); oneOf(db).transaction(with(false), withDbRunnable(txn1));
@@ -445,8 +446,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
new TransportKeySet(keySetId, contactId, null, updated))); new TransportKeySet(keySetId, contactId, null, updated)));
// Schedule a key update at the start of the next time period // Schedule a key update at the start of the next time period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(dbExecutor), with(timePeriodLength), with(timePeriodLength), with(MILLISECONDS));
with(MILLISECONDS));
}}); }});
transportKeyManager.start(txn); transportKeyManager.start(txn);

View File

@@ -1,20 +1,28 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.BluetoothConstants; import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory;
import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory; import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory;
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.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@@ -28,15 +36,27 @@ import static java.util.Collections.singletonMap;
public class DesktopPluginModule extends PluginModule { public class DesktopPluginModule extends PluginModule {
@Provides @Provides
PluginConfig getPluginConfig(JavaBluetoothPluginFactory bluetooth, PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor,
ModemPluginFactory modem, LanTcpPluginFactory lan, SecureRandom random, BackoffFactory backoffFactory,
WanTcpPluginFactory wan) { ReliabilityLayerFactory reliabilityFactory,
ShutdownManager shutdownManager, EventBus eventBus,
TimeoutMonitor timeoutMonitor) {
DuplexPluginFactory bluetooth = new JavaBluetoothPluginFactory(
ioExecutor, random, eventBus, timeoutMonitor, backoffFactory);
DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
reliabilityFactory);
DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor, eventBus,
backoffFactory);
DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor, eventBus,
backoffFactory, shutdownManager);
Collection<DuplexPluginFactory> duplex =
asList(bluetooth, modem, lan, wan);
@NotNullByDefault @NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() { PluginConfig pluginConfig = new PluginConfig() {
@Override @Override
public Collection<DuplexPluginFactory> getDuplexFactories() { public Collection<DuplexPluginFactory> getDuplexFactories() {
return asList(bluetooth, modem, lan, wan); return duplex;
} }
@Override @Override

View File

@@ -1,34 +0,0 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
import javax.annotation.concurrent.Immutable;
import javax.microedition.io.StreamConnection;
@Immutable
@NotNullByDefault
class JavaBluetoothConnectionFactory
implements BluetoothConnectionFactory<StreamConnection> {
private final BluetoothConnectionLimiter connectionLimiter;
private final TimeoutMonitor timeoutMonitor;
JavaBluetoothConnectionFactory(
BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor) {
this.connectionLimiter = connectionLimiter;
this.timeoutMonitor = timeoutMonitor;
}
@Override
public DuplexTransportConnection wrapSocket(DuplexPlugin plugin,
StreamConnection socket) throws IOException {
return new JavaBluetoothTransportConnection(plugin, connectionLimiter,
timeoutMonitor, socket);
}
}

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -25,8 +26,7 @@ import static org.briarproject.bramble.util.StringUtils.isValidMac;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class JavaBluetoothPlugin class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
extends BluetoothPlugin<StreamConnection, StreamConnectionNotifier> {
private static final Logger LOG = private static final Logger LOG =
getLogger(JavaBluetoothPlugin.class.getName()); getLogger(JavaBluetoothPlugin.class.getName());
@@ -35,17 +35,11 @@ class JavaBluetoothPlugin
private volatile LocalDevice localDevice = null; private volatile LocalDevice localDevice = null;
JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager, JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager,
BluetoothConnectionFactory<StreamConnection> connectionFactory, TimeoutMonitor timeoutMonitor, Executor ioExecutor,
Executor ioExecutor, SecureRandom secureRandom, Backoff backoff,
Executor wakefulIoExecutor, PluginCallback callback, int maxLatency, int maxIdleTime) {
SecureRandom secureRandom, super(connectionManager, timeoutMonitor, ioExecutor, secureRandom,
Backoff backoff, backoff, callback, maxLatency, maxIdleTime);
PluginCallback callback,
int maxLatency,
int maxIdleTime) {
super(connectionManager, connectionFactory, ioExecutor,
wakefulIoExecutor, secureRandom, backoff, callback,
maxLatency, maxIdleTime);
} }
@Override @Override
@@ -62,6 +56,22 @@ class JavaBluetoothPlugin
return localDevice != null && LocalDevice.isPowerOn(); return localDevice != null && LocalDevice.isPowerOn();
} }
@Override
void enableAdapter() {
// Nothing we can do on this platform
LOG.info("Could not enable Bluetooth");
}
@Override
void disableAdapterIfEnabledByUs() {
// We didn't enable it so we don't need to disable it
}
@Override
void setEnabledByUs() {
// Irrelevant on this platform
}
@Nullable @Nullable
@Override @Override
String getBluetoothAddress() { String getBluetoothAddress() {
@@ -86,7 +96,7 @@ class JavaBluetoothPlugin
@Override @Override
DuplexTransportConnection acceptConnection(StreamConnectionNotifier ss) DuplexTransportConnection acceptConnection(StreamConnectionNotifier ss)
throws IOException { throws IOException {
return connectionFactory.wrapSocket(this, ss.acceptAndOpen()); return wrapSocket(ss.acceptAndOpen());
} }
@Override @Override
@@ -98,8 +108,7 @@ class JavaBluetoothPlugin
DuplexTransportConnection connectTo(String address, String uuid) DuplexTransportConnection connectTo(String address, String uuid)
throws IOException { throws IOException {
String url = makeUrl(address, uuid); String url = makeUrl(address, uuid);
StreamConnection s = (StreamConnection) Connector.open(url); return wrapSocket((StreamConnection) Connector.open(url));
return connectionFactory.wrapSocket(this, s);
} }
@Override @Override
@@ -111,4 +120,10 @@ class JavaBluetoothPlugin
private String makeUrl(String address, String uuid) { private String makeUrl(String address, String uuid) {
return "btspp://" + address + ":" + uuid + ";name=RFCOMM"; return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
} }
private DuplexTransportConnection wrapSocket(StreamConnection s)
throws IOException {
return new JavaBluetoothTransportConnection(this, connectionLimiter,
timeoutMonitor, s);
}
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor; import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -10,14 +9,11 @@ import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.microedition.io.StreamConnection;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
@@ -31,21 +27,16 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final EventBus eventBus; private final EventBus eventBus;
private final TimeoutMonitor timeoutMonitor; private final TimeoutMonitor timeoutMonitor;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
@Inject public JavaBluetoothPluginFactory(Executor ioExecutor,
public JavaBluetoothPluginFactory(@IoExecutor Executor ioExecutor, SecureRandom secureRandom, EventBus eventBus,
@WakefulIoExecutor Executor wakefulIoExecutor, TimeoutMonitor timeoutMonitor, BackoffFactory backoffFactory) {
SecureRandom secureRandom,
EventBus eventBus,
TimeoutMonitor timeoutMonitor,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.eventBus = eventBus; this.eventBus = eventBus;
this.timeoutMonitor = timeoutMonitor; this.timeoutMonitor = timeoutMonitor;
@@ -66,14 +57,11 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(PluginCallback callback) { public DuplexPlugin createPlugin(PluginCallback callback) {
BluetoothConnectionLimiter connectionLimiter = BluetoothConnectionLimiter connectionLimiter =
new BluetoothConnectionLimiterImpl(eventBus); new BluetoothConnectionLimiterImpl(eventBus);
BluetoothConnectionFactory<StreamConnection> connectionFactory =
new JavaBluetoothConnectionFactory(connectionLimiter,
timeoutMonitor);
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter, JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
connectionFactory, ioExecutor, wakefulIoExecutor, secureRandom, timeoutMonitor, ioExecutor, secureRandom, backoff, callback,
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME); MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -16,20 +16,18 @@ class JavaBluetoothTransportConnection
extends AbstractDuplexTransportConnection { extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionLimiter; private final BluetoothConnectionLimiter connectionLimiter;
private final StreamConnection socket; private final StreamConnection stream;
private final InputStream in; private final InputStream in;
private final OutputStream out;
JavaBluetoothTransportConnection(Plugin plugin, JavaBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionLimiter, BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor, TimeoutMonitor timeoutMonitor,
StreamConnection socket) throws IOException { StreamConnection stream) throws IOException {
super(plugin); super(plugin);
this.connectionLimiter = connectionLimiter; this.connectionLimiter = connectionLimiter;
this.socket = socket; this.stream = stream;
in = timeoutMonitor.createTimeoutInputStream( in = timeoutMonitor.createTimeoutInputStream(
socket.openInputStream(), plugin.getMaxIdleTime() * 2); stream.openInputStream(), plugin.getMaxIdleTime() * 2);
out = socket.openOutputStream();
} }
@Override @Override
@@ -38,15 +36,14 @@ class JavaBluetoothTransportConnection
} }
@Override @Override
protected OutputStream getOutputStream() { protected OutputStream getOutputStream() throws IOException {
return out; return stream.openOutputStream();
} }
@Override @Override
protected void closeConnection(boolean exception) throws IOException { protected void closeConnection(boolean exception) throws IOException {
try { try {
socket.close(); stream.close();
in.close();
} finally { } finally {
connectionLimiter.connectionClosed(this); connectionLimiter.connectionClosed(this);
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.modem; package org.briarproject.bramble.plugin.modem;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -12,7 +11,6 @@ import org.briarproject.bramble.util.StringUtils;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -23,8 +21,7 @@ public class ModemPluginFactory implements DuplexPluginFactory {
private final ModemFactory modemFactory; private final ModemFactory modemFactory;
private final SerialPortList serialPortList; private final SerialPortList serialPortList;
@Inject public ModemPluginFactory(Executor ioExecutor,
public ModemPluginFactory(@IoExecutor Executor ioExecutor,
ReliabilityLayerFactory reliabilityFactory) { ReliabilityLayerFactory reliabilityFactory) {
modemFactory = new ModemFactoryImpl(ioExecutor, reliabilityFactory); modemFactory = new ModemFactoryImpl(ioExecutor, reliabilityFactory);
serialPortList = new SerialPortListImpl(); serialPortList = new SerialPortListImpl();

View File

@@ -20,26 +20,17 @@ import javax.net.SocketFactory;
@NotNullByDefault @NotNullByDefault
abstract class JavaTorPlugin extends TorPlugin { abstract class JavaTorPlugin extends TorPlugin {
JavaTorPlugin(Executor ioExecutor, JavaTorPlugin(Executor ioExecutor, NetworkManager networkManager,
Executor wakefulIoExecutor, LocationUtils locationUtils, SocketFactory torSocketFactory,
NetworkManager networkManager, Clock clock, ResourceProvider resourceProvider,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, BatteryManager batteryManager, Backoff backoff,
Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, PluginCallback callback, String architecture, int maxLatency,
String architecture, int maxIdleTime, File torDirectory) {
int maxLatency, super(ioExecutor, networkManager, locationUtils, torSocketFactory,
int maxIdleTime, clock, resourceProvider, circumventionProvider, batteryManager,
File torDirectory) { backoff, torRendezvousCrypto, callback, architecture,
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, backoff,
torRendezvousCrypto, callback, architecture,
maxLatency, maxIdleTime, torDirectory); maxLatency, maxIdleTime, torDirectory);
} }

View File

@@ -20,26 +20,17 @@ import javax.net.SocketFactory;
@NotNullByDefault @NotNullByDefault
class UnixTorPlugin extends JavaTorPlugin { class UnixTorPlugin extends JavaTorPlugin {
UnixTorPlugin(Executor ioExecutor, UnixTorPlugin(Executor ioExecutor, NetworkManager networkManager,
Executor wakefulIoExecutor, LocationUtils locationUtils, SocketFactory torSocketFactory,
NetworkManager networkManager, Clock clock, ResourceProvider resourceProvider,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, BatteryManager batteryManager, Backoff backoff,
Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, PluginCallback callback, String architecture, int maxLatency,
String architecture, int maxIdleTime, File torDirectory) {
int maxLatency, super(ioExecutor, networkManager, locationUtils, torSocketFactory,
int maxIdleTime, clock, resourceProvider, circumventionProvider, batteryManager,
File torDirectory) { backoff, torRendezvousCrypto, callback, architecture,
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, backoff,
torRendezvousCrypto, callback, architecture,
maxLatency, maxIdleTime, torDirectory); maxLatency, maxIdleTime, torDirectory);
} }

View File

@@ -2,14 +2,12 @@ package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
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.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.TorDirectory;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
@@ -22,7 +20,6 @@ import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
@@ -41,7 +38,7 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final NetworkManager networkManager; private final NetworkManager networkManager;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final EventBus eventBus; private final EventBus eventBus;
@@ -53,21 +50,13 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
private final Clock clock; private final Clock clock;
private final File torDirectory; private final File torDirectory;
@Inject public UnixTorPluginFactory(Executor ioExecutor,
public UnixTorPluginFactory(@IoExecutor Executor ioExecutor, NetworkManager networkManager, LocationUtils locationUtils,
@IoExecutor Executor wakefulIoExecutor, EventBus eventBus, SocketFactory torSocketFactory,
NetworkManager networkManager, BackoffFactory backoffFactory, ResourceProvider resourceProvider,
LocationUtils locationUtils,
EventBus eventBus,
SocketFactory torSocketFactory,
BackoffFactory backoffFactory,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, BatteryManager batteryManager, Clock clock, File torDirectory) {
Clock clock,
@TorDirectory File torDirectory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.networkManager = networkManager; this.networkManager = networkManager;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.eventBus = eventBus; this.eventBus = eventBus;
@@ -108,11 +97,11 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl(); TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, wakefulIoExecutor, UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, networkManager,
networkManager, locationUtils, torSocketFactory, clock, locationUtils, torSocketFactory, clock, resourceProvider,
resourceProvider, circumventionProvider, batteryManager, circumventionProvider, batteryManager, backoff,
backoff, torRendezvousCrypto, callback, architecture, torRendezvousCrypto, callback, architecture, MAX_LATENCY,
MAX_LATENCY, MAX_IDLE_TIME, torDirectory); MAX_IDLE_TIME, torDirectory);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.plugin.tor; package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons; import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
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.IoExecutor;
@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import org.briarproject.bramble.test.BrambleJavaIntegrationTestComponent; import org.briarproject.bramble.test.BrambleJavaIntegrationTestComponent;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.DaggerBrambleJavaIntegrationTestComponent; import org.briarproject.bramble.test.DaggerBrambleJavaIntegrationTestComponent;
@@ -48,8 +47,7 @@ public class BridgeTest extends BrambleTestCase {
public static Iterable<String> data() { public static Iterable<String> data() {
BrambleJavaIntegrationTestComponent component = BrambleJavaIntegrationTestComponent component =
DaggerBrambleJavaIntegrationTestComponent.builder().build(); DaggerBrambleJavaIntegrationTestComponent.builder().build();
BrambleCoreIntegrationTestEagerSingletons.Helper BrambleCoreEagerSingletons.Helper.injectEagerSingletons(component);
.injectEagerSingletons(component);
return component.getCircumventionProvider().getBridges(false); return component.getCircumventionProvider().getBridges(false);
} }
@@ -61,9 +59,6 @@ public class BridgeTest extends BrambleTestCase {
@IoExecutor @IoExecutor
Executor ioExecutor; Executor ioExecutor;
@Inject @Inject
@WakefulIoExecutor
Executor wakefulIoExecutor;
@Inject
NetworkManager networkManager; NetworkManager networkManager;
@Inject @Inject
ResourceProvider resourceProvider; ResourceProvider resourceProvider;
@@ -97,8 +92,7 @@ public class BridgeTest extends BrambleTestCase {
BrambleJavaIntegrationTestComponent component = BrambleJavaIntegrationTestComponent component =
DaggerBrambleJavaIntegrationTestComponent.builder().build(); DaggerBrambleJavaIntegrationTestComponent.builder().build();
BrambleCoreIntegrationTestEagerSingletons.Helper BrambleCoreEagerSingletons.Helper.injectEagerSingletons(component);
.injectEagerSingletons(component);
component.inject(this); component.inject(this);
LocationUtils locationUtils = () -> "US"; LocationUtils locationUtils = () -> "US";
@@ -125,10 +119,10 @@ public class BridgeTest extends BrambleTestCase {
return singletonList(bridge); return singletonList(bridge);
} }
}; };
factory = new UnixTorPluginFactory(ioExecutor, wakefulIoExecutor, factory = new UnixTorPluginFactory(ioExecutor, networkManager,
networkManager, locationUtils, eventBus, torSocketFactory, locationUtils, eventBus, torSocketFactory, backoffFactory,
backoffFactory, resourceProvider, bridgeProvider, resourceProvider, bridgeProvider, batteryManager, clock,
batteryManager, clock, torDir); torDir);
} }
@After @After

View File

@@ -9,10 +9,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
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.Collection;
import static java.util.Collections.emptyList;
@NotNullByDefault @NotNullByDefault
public class TestPluginCallback implements PluginCallback { public class TestPluginCallback implements PluginCallback {
@@ -26,11 +22,6 @@ public class TestPluginCallback implements PluginCallback {
return new TransportProperties(); return new TransportProperties();
} }
@Override
public Collection<TransportProperties> getRemoteProperties() {
return emptyList();
}
@Override @Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
} }

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.test; package org.briarproject.bramble.test;
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons; import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.BrambleJavaModule; import org.briarproject.bramble.BrambleJavaModule;
import org.briarproject.bramble.plugin.tor.BridgeTest; import org.briarproject.bramble.plugin.tor.BridgeTest;
@@ -17,7 +17,7 @@ import dagger.Component;
BrambleJavaModule.class BrambleJavaModule.class
}) })
public interface BrambleJavaIntegrationTestComponent public interface BrambleJavaIntegrationTestComponent
extends BrambleCoreIntegrationTestEagerSingletons { extends BrambleCoreEagerSingletons {
void inject(BridgeTest init); void inject(BridgeTest init);

View File

@@ -16,14 +16,14 @@ def getStdout = { command, defaultValue ->
} }
android { android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion 29
buildToolsVersion rootProject.ext.buildToolsVersion buildToolsVersion '29.0.2'
defaultConfig { defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion 28
versionCode rootProject.ext.versionCode versionCode 10207
versionName rootProject.ext.versionName versionName "1.2.7"
applicationId "org.briarproject.briar.android" applicationId "org.briarproject.briar.android"
buildConfigField "String", "GitHash", buildConfigField "String", "GitHash",
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\"" "\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""

View File

@@ -13,9 +13,8 @@ import java.util.Random;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import static androidx.test.InstrumentationRegistry.getContext; import static androidx.test.InstrumentationRegistry.getContext;
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
import static org.briarproject.briar.android.attachment.AttachmentItem.State.ERROR;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@@ -28,7 +27,7 @@ public class AttachmentRetrieverIntegrationTest {
private final ImageHelper imageHelper = new ImageHelperImpl(); private final ImageHelper imageHelper = new ImageHelperImpl();
private final AttachmentRetriever retriever = private final AttachmentRetriever retriever =
new AttachmentRetrieverImpl(null, null, dimensions, imageHelper, new AttachmentRetrieverImpl(null, dimensions, imageHelper,
new ImageSizeCalculator(imageHelper)); new ImageSizeCalculator(imageHelper));
@Test @Test
@@ -36,7 +35,7 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("kitten_small.jpg"); InputStream is = getAssetInputStream("kitten_small.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(msgId, item.getMessageId()); assertEquals(msgId, item.getMessageId());
assertEquals(160, item.getWidth()); assertEquals(160, item.getWidth());
assertEquals(240, item.getHeight()); assertEquals(240, item.getHeight());
@@ -44,7 +43,7 @@ public class AttachmentRetrieverIntegrationTest {
assertEquals(240, item.getThumbnailHeight()); assertEquals(240, item.getThumbnailHeight());
assertEquals("image/jpeg", item.getMimeType()); assertEquals("image/jpeg", item.getMimeType());
assertJpgOrJpeg(item.getExtension()); assertJpgOrJpeg(item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -52,7 +51,7 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("kitten_original.jpg"); InputStream is = getAssetInputStream("kitten_original.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(msgId, item.getMessageId()); assertEquals(msgId, item.getMessageId());
assertEquals(1728, item.getWidth()); assertEquals(1728, item.getWidth());
assertEquals(2592, item.getHeight()); assertEquals(2592, item.getHeight());
@@ -60,7 +59,7 @@ public class AttachmentRetrieverIntegrationTest {
assertEquals(dimensions.maxHeight, item.getThumbnailHeight()); assertEquals(dimensions.maxHeight, item.getThumbnailHeight());
assertEquals("image/jpeg", item.getMimeType()); assertEquals("image/jpeg", item.getMimeType());
assertJpgOrJpeg(item.getExtension()); assertJpgOrJpeg(item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -68,7 +67,7 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/png"); AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
InputStream is = getAssetInputStream("kitten.png"); InputStream is = getAssetInputStream("kitten.png");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(msgId, item.getMessageId()); assertEquals(msgId, item.getMessageId());
assertEquals(737, item.getWidth()); assertEquals(737, item.getWidth());
assertEquals(510, item.getHeight()); assertEquals(510, item.getHeight());
@@ -76,7 +75,7 @@ public class AttachmentRetrieverIntegrationTest {
assertEquals(138, item.getThumbnailHeight()); assertEquals(138, item.getThumbnailHeight());
assertEquals("image/png", item.getMimeType()); assertEquals("image/png", item.getMimeType());
assertEquals("png", item.getExtension()); assertEquals("png", item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -84,14 +83,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("uber.gif"); InputStream is = getAssetInputStream("uber.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(1, item.getWidth()); assertEquals(1, item.getWidth());
assertEquals(1, item.getHeight()); assertEquals(1, item.getHeight());
assertEquals(dimensions.minHeight, item.getThumbnailWidth()); assertEquals(dimensions.minHeight, item.getThumbnailWidth());
assertEquals(dimensions.minHeight, item.getThumbnailHeight()); assertEquals(dimensions.minHeight, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -99,14 +98,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("lottapixel.jpg"); InputStream is = getAssetInputStream("lottapixel.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(64250, item.getWidth()); assertEquals(64250, item.getWidth());
assertEquals(64250, item.getHeight()); assertEquals(64250, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
assertEquals("image/jpeg", item.getMimeType()); assertEquals("image/jpeg", item.getMimeType());
assertJpgOrJpeg(item.getExtension()); assertJpgOrJpeg(item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -114,14 +113,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/png"); AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
InputStream is = getAssetInputStream("image_io_crash.png"); InputStream is = getAssetInputStream("image_io_crash.png");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(1184, item.getWidth()); assertEquals(1184, item.getWidth());
assertEquals(448, item.getHeight()); assertEquals(448, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.minHeight, item.getThumbnailHeight()); assertEquals(dimensions.minHeight, item.getThumbnailHeight());
assertEquals("image/png", item.getMimeType()); assertEquals("image/png", item.getMimeType());
assertEquals("png", item.getExtension()); assertEquals("png", item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -129,14 +128,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("gimp_crash.gif"); InputStream is = getAssetInputStream("gimp_crash.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(1, item.getWidth()); assertEquals(1, item.getWidth());
assertEquals(1, item.getHeight()); assertEquals(1, item.getHeight());
assertEquals(dimensions.minHeight, item.getThumbnailWidth()); assertEquals(dimensions.minHeight, item.getThumbnailWidth());
assertEquals(dimensions.minHeight, item.getThumbnailHeight()); assertEquals(dimensions.minHeight, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -144,14 +143,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("opti_png_afl.gif"); InputStream is = getAssetInputStream("opti_png_afl.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(32, item.getWidth()); assertEquals(32, item.getWidth());
assertEquals(32, item.getHeight()); assertEquals(32, item.getHeight());
assertEquals(dimensions.minHeight, item.getThumbnailWidth()); assertEquals(dimensions.minHeight, item.getThumbnailWidth());
assertEquals(dimensions.minHeight, item.getThumbnailHeight()); assertEquals(dimensions.minHeight, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -159,8 +158,8 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("libraw_error.jpg"); InputStream is = getAssetInputStream("libraw_error.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(ERROR, item.getState()); assertTrue(item.hasError());
} }
@Test @Test
@@ -168,14 +167,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("animated.gif"); InputStream is = getAssetInputStream("animated.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(65535, item.getWidth()); assertEquals(65535, item.getWidth());
assertEquals(65535, item.getHeight()); assertEquals(65535, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -183,14 +182,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("animated2.gif"); InputStream is = getAssetInputStream("animated2.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(10000, item.getWidth()); assertEquals(10000, item.getWidth());
assertEquals(10000, item.getHeight()); assertEquals(10000, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -198,14 +197,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("error_large.gif"); InputStream is = getAssetInputStream("error_large.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(16384, item.getWidth()); assertEquals(16384, item.getWidth());
assertEquals(16384, item.getHeight()); assertEquals(16384, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -213,14 +212,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("error_high.jpg"); InputStream is = getAssetInputStream("error_high.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(1, item.getWidth()); assertEquals(1, item.getWidth());
assertEquals(10000, item.getHeight()); assertEquals(10000, item.getHeight());
assertEquals(dimensions.minWidth, item.getThumbnailWidth()); assertEquals(dimensions.minWidth, item.getThumbnailWidth());
assertEquals(dimensions.maxHeight, item.getThumbnailHeight()); assertEquals(dimensions.maxHeight, item.getThumbnailHeight());
assertEquals("image/jpeg", item.getMimeType()); assertEquals("image/jpeg", item.getMimeType());
assertJpgOrJpeg(item.getExtension()); assertJpgOrJpeg(item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
@Test @Test
@@ -228,14 +227,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("error_wide.jpg"); InputStream is = getAssetInputStream("error_wide.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.createAttachmentItem(a, true); AttachmentItem item = retriever.getAttachmentItem(a, true);
assertEquals(1920, item.getWidth()); assertEquals(1920, item.getWidth());
assertEquals(1, item.getHeight()); assertEquals(1, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.minHeight, item.getThumbnailHeight()); assertEquals(dimensions.minHeight, item.getThumbnailHeight());
assertEquals("image/jpeg", item.getMimeType()); assertEquals("image/jpeg", item.getMimeType());
assertJpgOrJpeg(item.getExtension()); assertJpgOrJpeg(item.getExtension());
assertEquals(AVAILABLE, item.getState()); assertFalse(item.hasError());
} }
private InputStream getAssetInputStream(String name) throws Exception { private InputStream getAssetInputStream(String name) throws Exception {

Some files were not shown because too many files have changed in this diff Show More