mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
1 Commits
limit-in-m
...
recently-o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1cda4fb1e |
3
.idea/codeStyles/Project.xml
generated
3
.idea/codeStyles/Project.xml
generated
@@ -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">
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -11,8 +11,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 10209
|
versionCode 10207
|
||||||
versionName "1.2.9"
|
versionName "1.2.7"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
package org.briarproject.bramble;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.AlarmListener;
|
|
||||||
|
|
||||||
public interface BrambleAppComponent {
|
|
||||||
|
|
||||||
AlarmListener alarmListener();
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package org.briarproject.bramble;
|
|
||||||
|
|
||||||
public interface BrambleApplication {
|
|
||||||
|
|
||||||
BrambleAppComponent getBrambleAppComponent();
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -9,17 +9,16 @@ import android.net.ConnectivityManager;
|
|||||||
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;
|
||||||
@@ -51,22 +50,20 @@ 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 Context appContext;
|
private final Context appContext;
|
||||||
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.appContext = app.getApplicationContext();
|
this.appContext = app.getApplicationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,12 +104,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 {
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
@@ -59,8 +58,7 @@ 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());
|
||||||
@@ -68,31 +66,23 @@ class AndroidBluetoothPlugin
|
|||||||
private static final int MAX_DISCOVERY_MS = 10_000;
|
private static final int MAX_DISCOVERY_MS = 10_000;
|
||||||
|
|
||||||
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,
|
Executor ioExecutor, AndroidExecutor androidExecutor,
|
||||||
Executor ioExecutor,
|
Context appContext, SecureRandom secureRandom, Clock clock,
|
||||||
Executor wakefulIoExecutor,
|
Backoff backoff, PluginCallback callback, int maxLatency) {
|
||||||
SecureRandom secureRandom,
|
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
|
||||||
AndroidExecutor androidExecutor,
|
maxLatency);
|
||||||
Application app,
|
|
||||||
Clock clock,
|
|
||||||
Backoff backoff,
|
|
||||||
PluginCallback callback,
|
|
||||||
int maxLatency,
|
|
||||||
int maxIdleTime) {
|
|
||||||
super(connectionLimiter, connectionFactory, ioExecutor,
|
|
||||||
wakefulIoExecutor, secureRandom, backoff, callback,
|
|
||||||
maxLatency, maxIdleTime);
|
|
||||||
this.androidExecutor = androidExecutor;
|
this.androidExecutor = androidExecutor;
|
||||||
this.app = app;
|
this.appContext = appContext;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,13 +94,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,10 +122,36 @@ 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
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
String getBluetoothAddress() {
|
String getBluetoothAddress() {
|
||||||
String address = AndroidUtils.getBluetoothAddress(app, adapter);
|
String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
|
||||||
return address.isEmpty() ? null : address;
|
return address.isEmpty() ? null : address;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +169,12 @@ 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) {
|
||||||
|
return new AndroidBluetoothTransportConnection(this,
|
||||||
|
connectionLimiter, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -170,7 +191,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;
|
||||||
@@ -205,7 +226,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();
|
||||||
@@ -242,7 +263,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);
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
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.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 +11,12 @@ 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 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;
|
||||||
|
|
||||||
@@ -31,41 +25,28 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
|||||||
public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
||||||
|
|
||||||
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
||||||
private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
|
|
||||||
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
|
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
|
||||||
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 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 BackoffFactory backoffFactory;
|
private final BackoffFactory backoffFactory;
|
||||||
|
|
||||||
@Inject
|
public AndroidBluetoothPluginFactory(Executor ioExecutor,
|
||||||
public AndroidBluetoothPluginFactory(@IoExecutor Executor ioExecutor,
|
AndroidExecutor androidExecutor, Context appContext,
|
||||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
SecureRandom secureRandom, EventBus eventBus, Clock clock,
|
||||||
AndroidExecutor androidExecutor,
|
|
||||||
AndroidWakeLockManager wakeLockManager,
|
|
||||||
Application app,
|
|
||||||
SecureRandom secureRandom,
|
|
||||||
EventBus eventBus,
|
|
||||||
Clock clock,
|
|
||||||
TimeoutMonitor timeoutMonitor,
|
|
||||||
BackoffFactory backoffFactory) {
|
BackoffFactory backoffFactory) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.wakefulIoExecutor = wakefulIoExecutor;
|
|
||||||
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;
|
||||||
this.timeoutMonitor = timeoutMonitor;
|
|
||||||
this.backoffFactory = backoffFactory;
|
this.backoffFactory = backoffFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,16 +63,12 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
@Override
|
@Override
|
||||||
public DuplexPlugin createPlugin(PluginCallback callback) {
|
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||||
BluetoothConnectionLimiter connectionLimiter =
|
BluetoothConnectionLimiter connectionLimiter =
|
||||||
new BluetoothConnectionLimiterImpl(eventBus);
|
new BluetoothConnectionLimiterImpl();
|
||||||
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, ioExecutor, androidExecutor, appContext,
|
||||||
wakefulIoExecutor, secureRandom, androidExecutor, app,
|
secureRandom, clock, backoff, callback, MAX_LATENCY);
|
||||||
clock, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
|
|
||||||
eventBus.addListener(plugin);
|
eventBus.addListener(plugin);
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,9 @@ package org.briarproject.bramble.plugin.bluetooth;
|
|||||||
|
|
||||||
import android.bluetooth.BluetoothSocket;
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
|
||||||
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.api.system.AndroidWakeLockManager;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -20,30 +17,22 @@ import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress
|
|||||||
class AndroidBluetoothTransportConnection
|
class AndroidBluetoothTransportConnection
|
||||||
extends AbstractDuplexTransportConnection {
|
extends AbstractDuplexTransportConnection {
|
||||||
|
|
||||||
private final BluetoothConnectionLimiter connectionLimiter;
|
private final BluetoothConnectionLimiter connectionManager;
|
||||||
private final BluetoothSocket socket;
|
private final BluetoothSocket socket;
|
||||||
private final InputStream in;
|
|
||||||
private final AndroidWakeLock wakeLock;
|
|
||||||
|
|
||||||
AndroidBluetoothTransportConnection(Plugin plugin,
|
AndroidBluetoothTransportConnection(Plugin plugin,
|
||||||
BluetoothConnectionLimiter connectionLimiter,
|
BluetoothConnectionLimiter connectionManager,
|
||||||
AndroidWakeLockManager wakeLockManager,
|
BluetoothSocket socket) {
|
||||||
TimeoutMonitor timeoutMonitor,
|
|
||||||
BluetoothSocket socket) throws IOException {
|
|
||||||
super(plugin);
|
super(plugin);
|
||||||
this.connectionLimiter = connectionLimiter;
|
this.connectionManager = connectionManager;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
in = timeoutMonitor.createTimeoutInputStream(
|
|
||||||
socket.getInputStream(), plugin.getMaxIdleTime() * 2);
|
|
||||||
wakeLock = wakeLockManager.createWakeLock("BluetoothConnection");
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InputStream getInputStream() {
|
protected InputStream getInputStream() throws IOException {
|
||||||
return in;
|
return socket.getInputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -55,10 +44,8 @@ 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();
|
connectionManager.connectionClosed(this);
|
||||||
connectionLimiter.connectionClosed(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,23 @@
|
|||||||
package org.briarproject.bramble.plugin.tcp;
|
package org.briarproject.bramble.plugin.tcp;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.content.Context;
|
||||||
import android.app.Application;
|
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.LinkAddress;
|
|
||||||
import android.net.LinkProperties;
|
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkInfo;
|
||||||
import android.net.wifi.WifiInfo;
|
import android.net.wifi.WifiInfo;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
|
|
||||||
import org.briarproject.bramble.PoliteExecutor;
|
import org.briarproject.bramble.PoliteExecutor;
|
||||||
import org.briarproject.bramble.api.Pair;
|
|
||||||
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.network.event.NetworkStatusEvent;
|
import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
|
||||||
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.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InterfaceAddress;
|
|
||||||
import java.net.NetworkInterface;
|
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketException;
|
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -35,22 +28,14 @@ import javax.net.SocketFactory;
|
|||||||
|
|
||||||
import static android.content.Context.CONNECTIVITY_SERVICE;
|
import static android.content.Context.CONNECTIVITY_SERVICE;
|
||||||
import static android.content.Context.WIFI_SERVICE;
|
import static android.content.Context.WIFI_SERVICE;
|
||||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
import static android.net.ConnectivityManager.TYPE_WIFI;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
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.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.INACTIVE;
|
|
||||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class AndroidLanTcpPlugin extends LanTcpPlugin {
|
class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(AndroidLanTcpPlugin.class.getName());
|
getLogger(AndroidLanTcpPlugin.class.getName());
|
||||||
@@ -62,22 +47,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,137 +68,37 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
public void start() {
|
public void start() {
|
||||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||||
initialisePortProperty();
|
initialisePortProperty();
|
||||||
Settings settings = callback.getSettings();
|
running = true;
|
||||||
state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE,
|
|
||||||
DEFAULT_PREF_PLUGIN_ENABLE));
|
|
||||||
updateConnectionStatus();
|
updateConnectionStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
tryToClose(socket);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Socket createSocket() throws IOException {
|
protected Socket createSocket() throws IOException {
|
||||||
return socketFactory.createSocket();
|
return socketFactory.createSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<InetAddress> getUsableLocalInetAddresses(boolean ipv4) {
|
protected List<InetAddress> getUsableLocalInetAddresses() {
|
||||||
InetAddress addr = getWifiAddress(ipv4);
|
// If the device doesn't have wifi, don't open any sockets
|
||||||
return addr == null ? emptyList() : singletonList(addr);
|
if (wifiManager == null) return emptyList();
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private InetAddress getWifiAddress(boolean ipv4) {
|
|
||||||
Pair<InetAddress, Boolean> wifi = getWifiIpv4Address();
|
|
||||||
if (ipv4) return wifi == null ? null : wifi.getFirst();
|
|
||||||
// If there's no wifi IPv4 address, we might be a client on an
|
|
||||||
// IPv6-only wifi network. We can only detect this on API 21+
|
|
||||||
if (wifi == null) {
|
|
||||||
return SDK_INT >= 21 ? getWifiClientIpv6Address() : null;
|
|
||||||
}
|
|
||||||
// Use the wifi IPv4 address to determine which interface's IPv6
|
|
||||||
// address we should return (if the interface has a suitable address)
|
|
||||||
return getIpv6AddressForInterface(wifi.getFirst());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@link Pair} where the first element is the IPv4 address of
|
|
||||||
* the wifi interface and the second element is true if this device is
|
|
||||||
* providing an access point, or false if this device is a client. Returns
|
|
||||||
* null if this device isn't connected to wifi as an access point or client.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
private Pair<InetAddress, Boolean> getWifiIpv4Address() {
|
|
||||||
if (wifiManager == null) return null;
|
|
||||||
// If we're connected to a wifi network, return its address
|
// If we're connected to a wifi network, return its address
|
||||||
WifiInfo info = wifiManager.getConnectionInfo();
|
WifiInfo info = wifiManager.getConnectionInfo();
|
||||||
if (info != null && info.getIpAddress() != 0) {
|
if (info != null && info.getIpAddress() != 0) {
|
||||||
return new Pair<>(intToInetAddress(info.getIpAddress()), false);
|
return singletonList(intToInetAddress(info.getIpAddress()));
|
||||||
}
|
}
|
||||||
List<InterfaceAddress> ifAddrs = getLocalInterfaceAddresses();
|
// If we're running an access point, return its address
|
||||||
// If we're providing a normal access point, return its address
|
for (InetAddress addr : getLocalInetAddresses()) {
|
||||||
for (InterfaceAddress ifAddr : ifAddrs) {
|
if (addr.equals(WIFI_AP_ADDRESS)) return singletonList(addr);
|
||||||
if (isAndroidWifiApAddress(ifAddr)) {
|
if (addr.equals(WIFI_DIRECT_AP_ADDRESS)) return singletonList(addr);
|
||||||
return new Pair<>(ifAddr.getAddress(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we're providing a wifi direct access point, return its address
|
|
||||||
for (InterfaceAddress ifAddr : ifAddrs) {
|
|
||||||
if (isAndroidWifiDirectApAddress(ifAddr)) {
|
|
||||||
return new Pair<>(ifAddr.getAddress(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Not connected to wifi
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given address belongs to a network provided by an
|
|
||||||
* Android access point (including the access point's own address).
|
|
||||||
* <p>
|
|
||||||
* The access point's address is usually 192.168.43.1, but at least one
|
|
||||||
* device (Honor 8A) may use other addresses in the range 192.168.43.0/24.
|
|
||||||
*/
|
|
||||||
private boolean isAndroidWifiApAddress(InterfaceAddress ifAddr) {
|
|
||||||
if (ifAddr.getNetworkPrefixLength() != 24) return false;
|
|
||||||
byte[] ip = ifAddr.getAddress().getAddress();
|
|
||||||
return ip.length == 4
|
|
||||||
&& ip[0] == (byte) 192
|
|
||||||
&& ip[1] == (byte) 168
|
|
||||||
&& ip[2] == (byte) 43;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given address belongs to a network provided by an
|
|
||||||
* Android wifi direct legacy mode access point (including the access
|
|
||||||
* point's own address).
|
|
||||||
*/
|
|
||||||
private boolean isAndroidWifiDirectApAddress(InterfaceAddress ifAddr) {
|
|
||||||
if (ifAddr.getNetworkPrefixLength() != 24) return false;
|
|
||||||
byte[] ip = ifAddr.getAddress().getAddress();
|
|
||||||
return ip.length == 4
|
|
||||||
&& ip[0] == (byte) 192
|
|
||||||
&& ip[1] == (byte) 168
|
|
||||||
&& ip[2] == (byte) 49;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a link-local IPv6 address for the wifi client interface, or null
|
|
||||||
* if there's no such interface or it doesn't have a suitable address.
|
|
||||||
*/
|
|
||||||
@TargetApi(21)
|
|
||||||
@Nullable
|
|
||||||
private InetAddress getWifiClientIpv6Address() {
|
|
||||||
for (Network net : connectivityManager.getAllNetworks()) {
|
|
||||||
NetworkCapabilities caps =
|
|
||||||
connectivityManager.getNetworkCapabilities(net);
|
|
||||||
if (caps == null || !caps.hasTransport(TRANSPORT_WIFI)) continue;
|
|
||||||
LinkProperties props = connectivityManager.getLinkProperties(net);
|
|
||||||
if (props == null) continue;
|
|
||||||
for (LinkAddress linkAddress : props.getLinkAddresses()) {
|
|
||||||
InetAddress addr = linkAddress.getAddress();
|
|
||||||
if (isIpv6LinkLocalAddress(addr)) return addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a link-local IPv6 address for the interface with the given IPv4
|
|
||||||
* address, or null if the interface doesn't have a suitable address.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
private InetAddress getIpv6AddressForInterface(InetAddress ipv4) {
|
|
||||||
try {
|
|
||||||
NetworkInterface iface = NetworkInterface.getByInetAddress(ipv4);
|
|
||||||
if (iface == null) return null;
|
|
||||||
for (InetAddress addr : list(iface.getInetAddresses())) {
|
|
||||||
if (isIpv6LinkLocalAddress(addr)) return addr;
|
|
||||||
}
|
|
||||||
// No suitable address
|
|
||||||
return null;
|
|
||||||
} catch (SocketException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
// No suitable addresses
|
||||||
|
return emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private InetAddress intToInetAddress(int ip) {
|
private InetAddress intToInetAddress(int ip) {
|
||||||
@@ -237,11 +120,9 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
private SocketFactory getSocketFactory() {
|
private SocketFactory getSocketFactory() {
|
||||||
if (SDK_INT < 21) return SocketFactory.getDefault();
|
if (SDK_INT < 21) return SocketFactory.getDefault();
|
||||||
for (Network net : connectivityManager.getAllNetworks()) {
|
for (Network net : connectivityManager.getAllNetworks()) {
|
||||||
NetworkCapabilities caps =
|
NetworkInfo info = connectivityManager.getNetworkInfo(net);
|
||||||
connectivityManager.getNetworkCapabilities(net);
|
if (info != null && info.getType() == TYPE_WIFI)
|
||||||
if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) {
|
|
||||||
return net.getSocketFactory();
|
return net.getSocketFactory();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LOG.warning("Could not find suitable socket factory");
|
LOG.warning("Could not find suitable socket factory");
|
||||||
return SocketFactory.getDefault();
|
return SocketFactory.getDefault();
|
||||||
@@ -249,59 +130,31 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
super.eventOccurred(e);
|
|
||||||
if (e instanceof NetworkStatusEvent) updateConnectionStatus();
|
if (e instanceof NetworkStatusEvent) updateConnectionStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateConnectionStatus() {
|
private void updateConnectionStatus() {
|
||||||
connectionStatusExecutor.execute(() -> {
|
connectionStatusExecutor.execute(() -> {
|
||||||
State s = getState();
|
if (!running) return;
|
||||||
if (s != ACTIVE && s != INACTIVE) return;
|
List<InetAddress> addrs = getUsableLocalInetAddresses();
|
||||||
Pair<InetAddress, Boolean> wifi = getPreferredWifiAddress();
|
if (addrs.contains(WIFI_AP_ADDRESS)
|
||||||
if (wifi == null) {
|
|| addrs.contains(WIFI_DIRECT_AP_ADDRESS)) {
|
||||||
LOG.info("Not connected to wifi");
|
|
||||||
socketFactory = SocketFactory.getDefault();
|
|
||||||
// Server sockets may not have been closed automatically when
|
|
||||||
// interface was taken down. If any sockets are open, closing
|
|
||||||
// them here will cause the sockets to be cleared and the state
|
|
||||||
// to be updated in acceptContactConnections()
|
|
||||||
if (s == ACTIVE) {
|
|
||||||
LOG.info("Closing server sockets");
|
|
||||||
tryToClose(state.getServerSocket(true), LOG, WARNING);
|
|
||||||
tryToClose(state.getServerSocket(false), LOG, WARNING);
|
|
||||||
}
|
|
||||||
} else if (wifi.getSecond()) {
|
|
||||||
LOG.info("Providing wifi hotspot");
|
LOG.info("Providing wifi hotspot");
|
||||||
// There's no corresponding Network object and thus no way
|
// There's no corresponding Network object and thus no way
|
||||||
// to get a suitable socket factory, so we won't be able to
|
// to get a suitable socket factory, so we won't be able to
|
||||||
// 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 (socket == null || socket.isClosed()) bind();
|
||||||
|
} else if (addrs.isEmpty()) {
|
||||||
|
LOG.info("Not connected to wifi");
|
||||||
|
socketFactory = SocketFactory.getDefault();
|
||||||
|
tryToClose(socket);
|
||||||
} else {
|
} else {
|
||||||
LOG.info("Connected to wifi");
|
LOG.info("Connected to wifi");
|
||||||
socketFactory = getSocketFactory();
|
socketFactory = getSocketFactory();
|
||||||
bind();
|
if (socket == null || socket.isClosed()) bind();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@link Pair} where the first element is an IP address (IPv4 if
|
|
||||||
* available, otherwise IPv6) of the wifi interface and the second element
|
|
||||||
* is true if this device is providing an access point, or false if this
|
|
||||||
* device is a client. Returns null if this device isn't connected to wifi
|
|
||||||
* as an access point or client.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
private Pair<InetAddress, Boolean> getPreferredWifiAddress() {
|
|
||||||
Pair<InetAddress, Boolean> wifi = getWifiIpv4Address();
|
|
||||||
// If there's no wifi IPv4 address, we might be a client on an
|
|
||||||
// IPv6-only wifi network. We can only detect this on API 21+
|
|
||||||
if (wifi == null && SDK_INT >= 21) {
|
|
||||||
InetAddress ipv6 = getWifiClientIpv6Address();
|
|
||||||
if (ipv6 != null) return new Pair<>(ipv6, false);
|
|
||||||
}
|
|
||||||
return wifi;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,50 +12,48 @@ 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.RenewableWakeLock;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static android.content.Context.MODE_PRIVATE;
|
||||||
|
import static android.content.Context.POWER_SERVICE;
|
||||||
|
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
|
||||||
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class AndroidTorPlugin extends TorPlugin {
|
class AndroidTorPlugin extends TorPlugin {
|
||||||
|
|
||||||
private final Application app;
|
private final Context appContext;
|
||||||
private final AndroidWakeLock wakeLock;
|
private final RenewableWakeLock wakeLock;
|
||||||
|
|
||||||
AndroidTorPlugin(Executor ioExecutor,
|
AndroidTorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
|
||||||
Executor wakefulIoExecutor,
|
Context appContext, NetworkManager networkManager,
|
||||||
Application app,
|
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,
|
||||||
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(), 1, MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -65,8 +64,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);
|
||||||
@@ -75,6 +74,7 @@ class AndroidTorPlugin extends TorPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void enableNetwork(boolean enable) throws IOException {
|
protected void enableNetwork(boolean enable) throws IOException {
|
||||||
|
if (!running) return;
|
||||||
if (enable) wakeLock.acquire();
|
if (enable) wakeLock.acquire();
|
||||||
super.enableNetwork(enable);
|
super.enableNetwork(enable);
|
||||||
if (!enable) wakeLock.release();
|
if (!enable) wakeLock.release();
|
||||||
@@ -85,4 +85,17 @@ class AndroidTorPlugin extends TorPlugin {
|
|||||||
super.stop();
|
super.stop();
|
||||||
wakeLock.release();
|
wakeLock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getWakeLockTag() {
|
||||||
|
PackageManager pm = appContext.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 getClass().getSimpleName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -61,12 +61,12 @@ class AndroidLocationUtils implements LocationUtils {
|
|||||||
private String getCountryFromPhoneNetwork() {
|
private String getCountryFromPhoneNetwork() {
|
||||||
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
|
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
|
||||||
TelephonyManager tm = (TelephonyManager) o;
|
TelephonyManager tm = (TelephonyManager) o;
|
||||||
return tm == null ? "" : tm.getNetworkCountryIso();
|
return tm.getNetworkCountryIso();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getCountryFromSimCard() {
|
private String getCountryFromSimCard() {
|
||||||
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
|
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
|
||||||
TelephonyManager tm = (TelephonyManager) o;
|
TelephonyManager tm = (TelephonyManager) o;
|
||||||
return tm == null ? "" : tm.getSimCountryIso();
|
return tm.getSimCountryIso();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps track of which contacts are currently connected by which transports.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface ConnectionRegistry {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers an incoming connection from the given contact over the given
|
|
||||||
* transport. The connection's {@link Priority priority} can be set later
|
|
||||||
* via {@link #setPriority(ContactId, TransportId, InterruptibleConnection,
|
|
||||||
* Priority)} if a priority record is received from the contact.
|
|
||||||
* <p>
|
|
||||||
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
|
|
||||||
* {@link ContactConnectedEvent} if this is the only connection with the
|
|
||||||
* contact.
|
|
||||||
*/
|
|
||||||
void registerIncomingConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers an outgoing connection to the given contact over the given
|
|
||||||
* transport.
|
|
||||||
* <p>
|
|
||||||
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
|
|
||||||
* {@link ContactConnectedEvent} if this is the only connection with the
|
|
||||||
* contact.
|
|
||||||
* <p>
|
|
||||||
* If the registry has any "better" connections with the given contact, the
|
|
||||||
* given connection will be interrupted. If the registry has any "worse"
|
|
||||||
* connections with the given contact, those connections will be
|
|
||||||
* interrupted.
|
|
||||||
* <p>
|
|
||||||
* Connection A is considered "better" than connection B if both
|
|
||||||
* connections have had their priorities set, and either A's transport is
|
|
||||||
* {@link PluginConfig#getTransportPreferences() preferred} to B's, or
|
|
||||||
* they use the same transport and A has higher {@link Priority priority}
|
|
||||||
* than B.
|
|
||||||
* <p>
|
|
||||||
* For backward compatibility, connections without priorities are not
|
|
||||||
* considered better or worse than other connections.
|
|
||||||
*/
|
|
||||||
void registerOutgoingConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, Priority priority);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters a connection with the given contact over the given transport.
|
|
||||||
* <p>
|
|
||||||
* Broadcasts {@link ConnectionClosedEvent}. Also broadcasts
|
|
||||||
* {@link ContactDisconnectedEvent} if this is the only connection with
|
|
||||||
* the contact.
|
|
||||||
*/
|
|
||||||
void unregisterConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, boolean incoming, boolean exception);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the {@link Priority priority} of a connection that was previously
|
|
||||||
* registered via {@link #registerIncomingConnection(ContactId, TransportId,
|
|
||||||
* InterruptibleConnection)}.
|
|
||||||
* <p>
|
|
||||||
* If the registry has any "better" connections with the given contact, the
|
|
||||||
* given connection will be interrupted. If the registry has any "worse"
|
|
||||||
* connections with the given contact, those connections will be
|
|
||||||
* interrupted.
|
|
||||||
* <p>
|
|
||||||
* Connection A is considered "better" than connection B if both
|
|
||||||
* connections have had their priorities set, and either A's transport is
|
|
||||||
* {@link PluginConfig#getTransportPreferences() preferred} to B's, or
|
|
||||||
* they use the same transport and A has higher {@link Priority priority}
|
|
||||||
* than B.
|
|
||||||
* <p>
|
|
||||||
* For backward compatibility, connections without priorities are not
|
|
||||||
* considered better or worse than other connections.
|
|
||||||
*/
|
|
||||||
void setPriority(ContactId c, TransportId t, InterruptibleConnection conn,
|
|
||||||
Priority priority);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns any contacts that are connected via the given transport.
|
|
||||||
*/
|
|
||||||
Collection<ContactId> getConnectedContacts(TransportId t);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns any contacts that are connected via the given transport or any
|
|
||||||
* {@link PluginConfig#getTransportPreferences() better} transport.
|
|
||||||
*/
|
|
||||||
Collection<ContactId> getConnectedOrBetterContacts(TransportId t);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given contact is connected via the given transport.
|
|
||||||
*/
|
|
||||||
boolean isConnected(ContactId c, TransportId t);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given contact is connected via any transport.
|
|
||||||
*/
|
|
||||||
boolean isConnected(ContactId c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a connection with the given pending contact. Broadcasts
|
|
||||||
* {@link RendezvousConnectionOpenedEvent} if this is the only connection
|
|
||||||
* with the pending contact.
|
|
||||||
*
|
|
||||||
* @return True if this is the only connection with the pending contact,
|
|
||||||
* false if it is redundant and should be closed
|
|
||||||
*/
|
|
||||||
boolean registerConnection(PendingContactId p);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters a connection with the given pending contact. Broadcasts
|
|
||||||
* {@link RendezvousConnectionClosedEvent}.
|
|
||||||
*/
|
|
||||||
void unregisterConnection(PendingContactId p, boolean success);
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A duplex sync connection that can be closed by interrupting its outgoing
|
|
||||||
* sync session.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface InterruptibleConnection {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interrupts the connection's outgoing sync session. If the underlying
|
|
||||||
* transport connection is alive and the remote peer is cooperative, this
|
|
||||||
* should result in both sync sessions ending and the connection being
|
|
||||||
* cleanly closed.
|
|
||||||
*/
|
|
||||||
void interruptOutgoingSession();
|
|
||||||
}
|
|
||||||
@@ -18,8 +18,6 @@ public interface EventBus {
|
|||||||
/**
|
/**
|
||||||
* Asynchronously notifies all listeners of an event. Listeners are
|
* Asynchronously notifies all listeners of an event. Listeners are
|
||||||
* notified on the {@link EventExecutor}.
|
* notified on the {@link EventExecutor}.
|
||||||
* <p>
|
|
||||||
* This method can safely be called while holding a lock.
|
|
||||||
*/
|
*/
|
||||||
void broadcast(Event e);
|
void broadcast(Event e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.io;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public interface TimeoutMonitor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an {@link InputStream} that wraps the given stream and allows
|
|
||||||
* 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
|
|
||||||
* detected eventually but are not guaranteed to be detected immediately.
|
|
||||||
*/
|
|
||||||
InputStream createTimeoutInputStream(InputStream in, long timeoutMs);
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,8 @@ 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_BT_ENABLE = "enable";
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package org.briarproject.bramble.api.connection;
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of which contacts are currently connected by which transports.
|
||||||
|
*/
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface ConnectionRegistry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a connection with the given contact over the given transport.
|
||||||
|
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
|
||||||
|
* {@link ConnectionStatusChangedEvent} if this is the only connection with
|
||||||
|
* the contact.
|
||||||
|
*/
|
||||||
|
void registerConnection(ContactId c, TransportId t, boolean incoming);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a connection with the given contact over the given transport.
|
||||||
|
* Broadcasts {@link ConnectionClosedEvent}. Also broadcasts
|
||||||
|
* {@link ConnectionStatusChangedEvent} if this is the only connection with
|
||||||
|
* the contact.
|
||||||
|
*/
|
||||||
|
void unregisterConnection(ContactId c, TransportId t, boolean incoming);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns any contacts that are connected via the given transport.
|
||||||
|
*/
|
||||||
|
Collection<ContactId> getConnectedContacts(TransportId t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given contact is connected via the given transport.
|
||||||
|
*/
|
||||||
|
boolean isConnected(ContactId c, TransportId t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the connection status of the given contact via all transports.
|
||||||
|
*/
|
||||||
|
ConnectionStatus getConnectionStatus(ContactId c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a connection with the given pending contact. Broadcasts
|
||||||
|
* {@link RendezvousConnectionOpenedEvent} if this is the only connection
|
||||||
|
* with the pending contact.
|
||||||
|
*
|
||||||
|
* @return True if this is the only connection with the pending contact,
|
||||||
|
* false if it is redundant and should be closed
|
||||||
|
*/
|
||||||
|
boolean registerConnection(PendingContactId p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a connection with the given pending contact. Broadcasts
|
||||||
|
* {@link RendezvousConnectionClosedEvent}.
|
||||||
|
*/
|
||||||
|
void unregisterConnection(PendingContactId p, boolean success);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
|
public enum ConnectionStatus {
|
||||||
|
CONNECTED, RECENTLY_CONNECTED, DISCONNECTED
|
||||||
|
}
|
||||||
@@ -7,12 +7,7 @@ public interface LanTcpConstants {
|
|||||||
// Transport properties (shared with contacts)
|
// Transport properties (shared with contacts)
|
||||||
String PROP_IP_PORTS = "ipPorts";
|
String PROP_IP_PORTS = "ipPorts";
|
||||||
String PROP_PORT = "port";
|
String PROP_PORT = "port";
|
||||||
String PROP_IPV6 = "ipv6";
|
|
||||||
|
|
||||||
// Local settings (not shared with contacts)
|
// A local setting
|
||||||
String PREF_LAN_IP_PORTS = "ipPorts";
|
String PREF_LAN_IP_PORTS = "ipPorts";
|
||||||
String PREF_IPV6 = "ipv6";
|
|
||||||
|
|
||||||
// Default value for PREF_PLUGIN_ENABLE
|
|
||||||
boolean DEFAULT_PREF_PLUGIN_ENABLE = true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,56 +3,12 @@ package org.briarproject.bramble.api.plugin;
|
|||||||
import org.briarproject.bramble.api.Pair;
|
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.system.Wakeful;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface Plugin {
|
public interface Plugin {
|
||||||
|
|
||||||
enum State {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The plugin has not finished starting or has been stopped.
|
|
||||||
*/
|
|
||||||
STARTING_STOPPING,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The plugin is disabled by settings. Use {@link #getReasonsDisabled()}
|
|
||||||
* to find out which settings are responsible.
|
|
||||||
*/
|
|
||||||
DISABLED,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The plugin is being enabled and can't yet make or receive
|
|
||||||
* connections.
|
|
||||||
*/
|
|
||||||
ENABLING,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The plugin is enabled and can make or receive connections.
|
|
||||||
*/
|
|
||||||
ACTIVE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The plugin is enabled but can't make or receive connections
|
|
||||||
*/
|
|
||||||
INACTIVE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The string for the boolean preference
|
|
||||||
* to use with the {@link SettingsManager} to enable or disable the plugin.
|
|
||||||
*/
|
|
||||||
String PREF_PLUGIN_ENABLE = "enable";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reason flag returned by {@link #getReasonsDisabled()} to indicate that
|
|
||||||
* the plugin has been disabled by the user.
|
|
||||||
*/
|
|
||||||
int REASON_USER = 1;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the plugin's transport identifier.
|
* Returns the plugin's transport identifier.
|
||||||
*/
|
*/
|
||||||
@@ -71,28 +27,17 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current state of the plugin.
|
* Returns true if the plugin is running.
|
||||||
*/
|
*/
|
||||||
State getState();
|
boolean isRunning();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a set of flags indicating why the plugin is
|
|
||||||
* {@link State#DISABLED disabled}, or 0 if the plugin is not disabled.
|
|
||||||
* <p>
|
|
||||||
* The flags used are plugin-specific, except the generic flag
|
|
||||||
* {@link #REASON_USER}, which may be used by any plugin.
|
|
||||||
*/
|
|
||||||
int getReasonsDisabled();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the plugin should be polled periodically to attempt to
|
* Returns true if the plugin should be polled periodically to attempt to
|
||||||
@@ -109,7 +54,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
package org.briarproject.bramble.api.plugin;
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin.State;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
|
||||||
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 +21,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
|
||||||
*/
|
*/
|
||||||
@@ -43,17 +32,12 @@ public interface PluginCallback extends ConnectionHandler {
|
|||||||
void mergeLocalProperties(TransportProperties p);
|
void mergeLocalProperties(TransportProperties p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informs the callback of the plugin's current state.
|
* Signals that the transport is enabled.
|
||||||
* <p>
|
|
||||||
* If the current state is different from the previous state, the callback
|
|
||||||
* will broadcast a {@link TransportStateEvent}. If the current state is
|
|
||||||
* {@link State#ACTIVE} and the previous state was not
|
|
||||||
* {@link State#ACTIVE}, the callback will broadcast a
|
|
||||||
* {@link TransportActiveEvent}. If the current state is not
|
|
||||||
* {@link State#ACTIVE} and the previous state was {@link State#ACTIVE},
|
|
||||||
* the callback will broadcast a {@link TransportInactiveEvent}.
|
|
||||||
* <p>
|
|
||||||
* This method can safely be called while holding a lock.
|
|
||||||
*/
|
*/
|
||||||
void pluginStateChanged(State state);
|
void transportEnabled();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that the transport is disabled.
|
||||||
|
*/
|
||||||
|
void transportDisabled();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ 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 java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface PluginConfig {
|
public interface PluginConfig {
|
||||||
@@ -16,11 +14,4 @@ public interface PluginConfig {
|
|||||||
Collection<SimplexPluginFactory> getSimplexFactories();
|
Collection<SimplexPluginFactory> getSimplexFactories();
|
||||||
|
|
||||||
boolean shouldPoll();
|
boolean shouldPoll();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a map representing transport preferences. For each entry in the
|
|
||||||
* map, connections via the transports identified by the value are
|
|
||||||
* preferred to connections via the transport identified by the key.
|
|
||||||
*/
|
|
||||||
Map<TransportId, List<TransportId>> getTransportPreferences();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,17 +41,4 @@ public interface PluginManager {
|
|||||||
* Returns any duplex plugins that support rendezvous.
|
* Returns any duplex plugins that support rendezvous.
|
||||||
*/
|
*/
|
||||||
Collection<DuplexPlugin> getRendezvousPlugins();
|
Collection<DuplexPlugin> getRendezvousPlugins();
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disables the plugin
|
|
||||||
* identified by the given {@link TransportId}.
|
|
||||||
* <p>
|
|
||||||
* Note that this applies the change asynchronously
|
|
||||||
* and there are no order guarantees.
|
|
||||||
* <p>
|
|
||||||
* If no plugin with the given {@link TransportId} is registered,
|
|
||||||
* this is a no-op.
|
|
||||||
*/
|
|
||||||
void setPluginEnabled(TransportId t, boolean enabled);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,45 +13,14 @@ 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
|
|
||||||
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()}.
|
|
||||||
*/
|
|
||||||
int REASON_BATTERY = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reason flag returned by {@link Plugin#getReasonsDisabled()}.
|
|
||||||
*/
|
|
||||||
int REASON_MOBILE_DATA = 4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reason flag returned by {@link Plugin#getReasonsDisabled()}.
|
|
||||||
*/
|
|
||||||
int REASON_COUNTRY_BLOCKED = 8;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
}
|
||||||
@@ -13,14 +13,13 @@ public class ConnectionClosedEvent extends Event {
|
|||||||
|
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final boolean incoming, exception;
|
private final boolean incoming;
|
||||||
|
|
||||||
public ConnectionClosedEvent(ContactId contactId, TransportId transportId,
|
public ConnectionClosedEvent(ContactId contactId, TransportId transportId,
|
||||||
boolean incoming, boolean exception) {
|
boolean incoming) {
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
this.incoming = incoming;
|
this.incoming = incoming;
|
||||||
this.exception = exception;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContactId getContactId() {
|
public ContactId getContactId() {
|
||||||
@@ -34,8 +33,4 @@ public class ConnectionClosedEvent extends Event {
|
|||||||
public boolean isIncoming() {
|
public boolean isIncoming() {
|
||||||
return incoming;
|
return incoming;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isException() {
|
|
||||||
return exception;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,31 @@ package org.briarproject.bramble.api.plugin.event;
|
|||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event that is broadcast when a contact connects that was not previously
|
* An event that is broadcast when a contact's connection status changes.
|
||||||
* connected via any transport.
|
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ContactConnectedEvent extends Event {
|
public class ConnectionStatusChangedEvent extends Event {
|
||||||
|
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
|
private final ConnectionStatus status;
|
||||||
|
|
||||||
public ContactConnectedEvent(ContactId contactId) {
|
public ConnectionStatusChangedEvent(ContactId contactId,
|
||||||
|
ConnectionStatus status) {
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContactId getContactId() {
|
public ContactId getContactId() {
|
||||||
return contactId;
|
return contactId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConnectionStatus getConnectionStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.plugin.event;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An event that is broadcast when a contact disconnects and is no longer
|
|
||||||
* connected via any transport.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class ContactDisconnectedEvent extends Event {
|
|
||||||
|
|
||||||
private final ContactId contactId;
|
|
||||||
|
|
||||||
public ContactDisconnectedEvent(ContactId contactId) {
|
|
||||||
this.contactId = contactId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContactId getContactId() {
|
|
||||||
return contactId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 {
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
}
|
||||||
@@ -2,22 +2,20 @@ package org.briarproject.bramble.api.plugin.event;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin.State;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event that is broadcast when a plugin enters the {@link State#ACTIVE}
|
* An event that is broadcast when a transport is disabled.
|
||||||
* state.
|
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class TransportActiveEvent extends Event {
|
public class TransportDisabledEvent extends Event {
|
||||||
|
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
|
|
||||||
public TransportActiveEvent(TransportId transportId) {
|
public TransportDisabledEvent(TransportId transportId) {
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2,22 +2,20 @@ package org.briarproject.bramble.api.plugin.event;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin.State;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event that is broadcast when a plugin leaves the {@link State#ACTIVE}
|
* An event that is broadcast when a transport is enabled.
|
||||||
* state.
|
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class TransportInactiveEvent extends Event {
|
public class TransportEnabledEvent extends Event {
|
||||||
|
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
|
|
||||||
public TransportInactiveEvent(TransportId transportId) {
|
public TransportEnabledEvent(TransportId transportId) {
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.plugin.event;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.Plugin.State;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An event that is broadcast when the {@link State state} of a plugin changes.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class TransportStateEvent extends Event {
|
|
||||||
|
|
||||||
private final TransportId transportId;
|
|
||||||
private final State state;
|
|
||||||
|
|
||||||
public TransportStateEvent(TransportId transportId, State state) {
|
|
||||||
this.transportId = transportId;
|
|
||||||
this.state = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransportId getTransportId() {
|
|
||||||
return transportId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public State getState() {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.sync;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A record containing a nonce for choosing between redundant sessions.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class Priority {
|
|
||||||
|
|
||||||
private final byte[] nonce;
|
|
||||||
|
|
||||||
public Priority(byte[] nonce) {
|
|
||||||
this.nonce = nonce;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getNonce() {
|
|
||||||
return nonce;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.sync;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for handling a {@link Priority} record received by an
|
|
||||||
* incoming {@link SyncSession}.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface PriorityHandler {
|
|
||||||
|
|
||||||
void handle(Priority p);
|
|
||||||
}
|
|
||||||
@@ -10,5 +10,4 @@ public interface RecordTypes {
|
|||||||
byte OFFER = 2;
|
byte OFFER = 2;
|
||||||
byte REQUEST = 3;
|
byte REQUEST = 3;
|
||||||
byte VERSIONS = 4;
|
byte VERSIONS = 4;
|
||||||
byte PRIORITY = 5;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,10 +49,4 @@ public interface SyncConstants {
|
|||||||
* simultaneously.
|
* simultaneously.
|
||||||
*/
|
*/
|
||||||
int MAX_SUPPORTED_VERSIONS = 10;
|
int MAX_SUPPORTED_VERSIONS = 10;
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the priority nonce used for choosing between redundant
|
|
||||||
* connections.
|
|
||||||
*/
|
|
||||||
int PRIORITY_NONCE_BYTES = 16;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,4 @@ public interface SyncRecordReader {
|
|||||||
boolean hasVersions() throws IOException;
|
boolean hasVersions() throws IOException;
|
||||||
|
|
||||||
Versions readVersions() throws IOException;
|
Versions readVersions() throws IOException;
|
||||||
|
|
||||||
boolean hasPriority() throws IOException;
|
|
||||||
|
|
||||||
Priority readPriority() throws IOException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,5 @@ public interface SyncRecordWriter {
|
|||||||
|
|
||||||
void writeVersions(Versions v) throws IOException;
|
void writeVersions(Versions v) throws IOException;
|
||||||
|
|
||||||
void writePriority(Priority p) throws IOException;
|
|
||||||
|
|
||||||
void flush() throws IOException;
|
void flush() throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,18 @@ package org.briarproject.bramble.api.sync;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface SyncSessionFactory {
|
public interface SyncSessionFactory {
|
||||||
|
|
||||||
SyncSession createIncomingSession(ContactId c, InputStream in,
|
SyncSession createIncomingSession(ContactId c, InputStream in);
|
||||||
PriorityHandler handler);
|
|
||||||
|
|
||||||
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
|
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
|
||||||
int maxLatency, StreamWriter streamWriter);
|
StreamWriter streamWriter);
|
||||||
|
|
||||||
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
|
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
|
||||||
int maxLatency, int maxIdleTime, StreamWriter streamWriter,
|
int maxIdleTime, StreamWriter streamWriter);
|
||||||
@Nullable Priority priority);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.sync.event;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An event that is broadcast when all sync connections using a given
|
|
||||||
* transport should be closed.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class CloseSyncConnectionsEvent extends Event {
|
|
||||||
|
|
||||||
private final TransportId transportId;
|
|
||||||
|
|
||||||
public CloseSyncConnectionsEvent(TransportId transportId) {
|
|
||||||
this.transportId = transportId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransportId getTransportId() {
|
|
||||||
return transportId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 {
|
||||||
}
|
}
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 {
|
|
||||||
}
|
|
||||||
@@ -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 {
|
|
||||||
}
|
|
||||||
@@ -2,17 +2,13 @@ package org.briarproject.bramble.util;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.isValidMac;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class PrivacyUtils {
|
public class PrivacyUtils {
|
||||||
|
|
||||||
@@ -23,7 +19,7 @@ public class PrivacyUtils {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static String scrubMacAddress(@Nullable String address) {
|
public static String scrubMacAddress(@Nullable String address) {
|
||||||
if (isNullOrEmpty(address) || !isValidMac(address)) return address;
|
if (address == null || address.length() == 0) return null;
|
||||||
// this is a fake address we need to know about
|
// this is a fake address we need to know about
|
||||||
if (address.equals("02:00:00:00:00:00")) return address;
|
if (address.equals("02:00:00:00:00:00")) return address;
|
||||||
// keep first and last octet of MAC address
|
// keep first and last octet of MAC address
|
||||||
@@ -31,37 +27,39 @@ public class PrivacyUtils {
|
|||||||
+ address.substring(14, 17);
|
+ address.substring(14, 17);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public static String scrubInetAddress(InetAddress address) {
|
public static String scrubInetAddress(InetAddress address) {
|
||||||
if (address instanceof Inet4Address) {
|
// don't scrub link and site local addresses
|
||||||
// Don't scrub local IPv4 addresses
|
if (address.isLinkLocalAddress() || address.isSiteLocalAddress())
|
||||||
if (address.isLoopbackAddress() || address.isLinkLocalAddress() ||
|
return address.toString();
|
||||||
address.isSiteLocalAddress()) {
|
// completely scrub IPv6 addresses
|
||||||
return address.getHostAddress();
|
if (address instanceof Inet6Address) return "[scrubbed]";
|
||||||
}
|
// keep first and last octet of IPv4 addresses
|
||||||
// Keep first and last octet of non-local IPv4 addresses
|
return scrubInetAddress(address.toString());
|
||||||
return scrubIpv4Address(address.getAddress());
|
|
||||||
} else {
|
|
||||||
// Keep first and last octet of IPv6 addresses
|
|
||||||
return scrubIpv6Address(address.getAddress());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String scrubIpv4Address(byte[] ipv4) {
|
@Nullable
|
||||||
return (ipv4[0] & 0xFF) + ".[scrubbed]." + (ipv4[3] & 0xFF);
|
public static String scrubInetAddress(@Nullable String address) {
|
||||||
}
|
if (address == null) return null;
|
||||||
|
|
||||||
private static String scrubIpv6Address(byte[] ipv6) {
|
int firstDot = address.indexOf(".");
|
||||||
String hex = toHexString(ipv6).toLowerCase();
|
if (firstDot == -1) return "[scrubbed]";
|
||||||
return hex.substring(0, 2) + "[scrubbed]" + hex.substring(30);
|
String prefix = address.substring(0, firstDot + 1);
|
||||||
|
int lastDot = address.lastIndexOf(".");
|
||||||
|
String suffix = address.substring(lastDot, address.length());
|
||||||
|
return prefix + "[scrubbed]" + suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public static String scrubSocketAddress(InetSocketAddress address) {
|
public static String scrubSocketAddress(InetSocketAddress address) {
|
||||||
return scrubInetAddress(address.getAddress());
|
InetAddress inetAddress = address.getAddress();
|
||||||
|
return scrubInetAddress(inetAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public static String scrubSocketAddress(SocketAddress address) {
|
public static String scrubSocketAddress(SocketAddress address) {
|
||||||
if (address instanceof InetSocketAddress)
|
if (address instanceof InetSocketAddress)
|
||||||
return scrubSocketAddress((InetSocketAddress) address);
|
return scrubSocketAddress((InetSocketAddress) address);
|
||||||
return "[scrubbed]";
|
return scrubInetAddress(address.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble;
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
import org.briarproject.bramble.client.ClientModule;
|
import org.briarproject.bramble.client.ClientModule;
|
||||||
import org.briarproject.bramble.connection.ConnectionModule;
|
|
||||||
import org.briarproject.bramble.contact.ContactModule;
|
import org.briarproject.bramble.contact.ContactModule;
|
||||||
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
||||||
import org.briarproject.bramble.crypto.CryptoModule;
|
import org.briarproject.bramble.crypto.CryptoModule;
|
||||||
@@ -10,7 +9,6 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.io.IoModule;
|
|
||||||
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
@@ -21,7 +19,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,8 +27,6 @@ import dagger.Module;
|
|||||||
|
|
||||||
@Module(includes = {
|
@Module(includes = {
|
||||||
ClientModule.class,
|
ClientModule.class,
|
||||||
ClockModule.class,
|
|
||||||
ConnectionModule.class,
|
|
||||||
ContactModule.class,
|
ContactModule.class,
|
||||||
CryptoModule.class,
|
CryptoModule.class,
|
||||||
CryptoExecutorModule.class,
|
CryptoExecutorModule.class,
|
||||||
@@ -39,7 +35,6 @@ import dagger.Module;
|
|||||||
DatabaseExecutorModule.class,
|
DatabaseExecutorModule.class,
|
||||||
EventModule.class,
|
EventModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
IoModule.class,
|
|
||||||
KeyAgreementModule.class,
|
KeyAgreementModule.class,
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
@@ -49,6 +44,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
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
import static org.briarproject.bramble.util.IoUtils.read;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class Connection {
|
|
||||||
|
|
||||||
protected static final Logger LOG = getLogger(Connection.class.getName());
|
|
||||||
|
|
||||||
final KeyManager keyManager;
|
|
||||||
final ConnectionRegistry connectionRegistry;
|
|
||||||
final StreamReaderFactory streamReaderFactory;
|
|
||||||
final StreamWriterFactory streamWriterFactory;
|
|
||||||
|
|
||||||
Connection(KeyManager keyManager, ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory) {
|
|
||||||
this.keyManager = keyManager;
|
|
||||||
this.connectionRegistry = connectionRegistry;
|
|
||||||
this.streamReaderFactory = streamReaderFactory;
|
|
||||||
this.streamWriterFactory = streamWriterFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
StreamContext recogniseTag(TransportConnectionReader reader,
|
|
||||||
TransportId transportId) {
|
|
||||||
StreamContext ctx;
|
|
||||||
try {
|
|
||||||
byte[] tag = readTag(reader.getInputStream());
|
|
||||||
return keyManager.getStreamContext(transportId, tag);
|
|
||||||
} catch (IOException | DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] readTag(InputStream in) throws IOException {
|
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
|
||||||
read(in, tag);
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disposeOnError(TransportConnectionReader reader, boolean recognised) {
|
|
||||||
try {
|
|
||||||
reader.dispose(true, recognised);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void disposeOnError(TransportConnectionWriter writer) {
|
|
||||||
try {
|
|
||||||
writer.dispose(true);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class ConnectionManagerImpl implements ConnectionManager {
|
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
|
||||||
private final KeyManager keyManager;
|
|
||||||
private final StreamReaderFactory streamReaderFactory;
|
|
||||||
private final StreamWriterFactory streamWriterFactory;
|
|
||||||
private final SyncSessionFactory syncSessionFactory;
|
|
||||||
private final HandshakeManager handshakeManager;
|
|
||||||
private final ContactExchangeManager contactExchangeManager;
|
|
||||||
private final ConnectionRegistry connectionRegistry;
|
|
||||||
private final TransportPropertyManager transportPropertyManager;
|
|
||||||
private final SecureRandom secureRandom;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ConnectionManagerImpl(@IoExecutor Executor ioExecutor,
|
|
||||||
KeyManager keyManager, StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
HandshakeManager handshakeManager,
|
|
||||||
ContactExchangeManager contactExchangeManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
SecureRandom secureRandom) {
|
|
||||||
this.ioExecutor = ioExecutor;
|
|
||||||
this.keyManager = keyManager;
|
|
||||||
this.streamReaderFactory = streamReaderFactory;
|
|
||||||
this.streamWriterFactory = streamWriterFactory;
|
|
||||||
this.syncSessionFactory = syncSessionFactory;
|
|
||||||
this.handshakeManager = handshakeManager;
|
|
||||||
this.contactExchangeManager = contactExchangeManager;
|
|
||||||
this.connectionRegistry = connectionRegistry;
|
|
||||||
this.transportPropertyManager = transportPropertyManager;
|
|
||||||
this.secureRandom = secureRandom;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageIncomingConnection(TransportId t,
|
|
||||||
TransportConnectionReader r) {
|
|
||||||
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
syncSessionFactory, transportPropertyManager, t, r));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageIncomingConnection(TransportId t,
|
|
||||||
DuplexTransportConnection d) {
|
|
||||||
ioExecutor.execute(new IncomingDuplexSyncConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
syncSessionFactory, transportPropertyManager, ioExecutor,
|
|
||||||
t, d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageIncomingConnection(PendingContactId p, TransportId t,
|
|
||||||
DuplexTransportConnection d) {
|
|
||||||
ioExecutor.execute(new IncomingHandshakeConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
handshakeManager, contactExchangeManager, this, p, t, d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageOutgoingConnection(ContactId c, TransportId t,
|
|
||||||
TransportConnectionWriter w) {
|
|
||||||
ioExecutor.execute(new OutgoingSimplexSyncConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
syncSessionFactory, transportPropertyManager, c, t, w));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageOutgoingConnection(ContactId c, TransportId t,
|
|
||||||
DuplexTransportConnection d) {
|
|
||||||
ioExecutor.execute(new OutgoingDuplexSyncConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
syncSessionFactory, transportPropertyManager, ioExecutor,
|
|
||||||
secureRandom, c, t, d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageOutgoingConnection(PendingContactId p, TransportId t,
|
|
||||||
DuplexTransportConnection d) {
|
|
||||||
ioExecutor.execute(new OutgoingHandshakeConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
handshakeManager, contactExchangeManager, this, p, t, d));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class ConnectionModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
ConnectionManager provideConnectionManager(
|
|
||||||
ConnectionManagerImpl connectionManager) {
|
|
||||||
return connectionManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
ConnectionRegistry provideConnectionRegistry(
|
|
||||||
ConnectionRegistryImpl connectionRegistry) {
|
|
||||||
return connectionRegistry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.connection.InterruptibleConnection;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
|
||||||
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class ConnectionRegistryImpl implements ConnectionRegistry {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(ConnectionRegistryImpl.class.getName());
|
|
||||||
|
|
||||||
private final EventBus eventBus;
|
|
||||||
private final Map<TransportId, List<TransportId>> transportPrefs;
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private final Map<ContactId, List<ConnectionRecord>> contactConnections;
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private final Set<PendingContactId> connectedPendingContacts;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ConnectionRegistryImpl(EventBus eventBus, PluginConfig pluginConfig) {
|
|
||||||
this.eventBus = eventBus;
|
|
||||||
transportPrefs = pluginConfig.getTransportPreferences();
|
|
||||||
contactConnections = new HashMap<>();
|
|
||||||
connectedPendingContacts = new HashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerIncomingConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn) {
|
|
||||||
registerConnection(c, t, conn, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerOutgoingConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, Priority priority) {
|
|
||||||
registerConnection(c, t, conn, false);
|
|
||||||
setPriority(c, t, conn, priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, boolean incoming) {
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
if (incoming) LOG.info("Incoming connection registered: " + t);
|
|
||||||
else LOG.info("Outgoing connection registered: " + t);
|
|
||||||
}
|
|
||||||
boolean firstConnection;
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ConnectionRecord> recs = contactConnections.get(c);
|
|
||||||
if (recs == null) {
|
|
||||||
recs = new ArrayList<>();
|
|
||||||
contactConnections.put(c, recs);
|
|
||||||
}
|
|
||||||
firstConnection = recs.isEmpty();
|
|
||||||
recs.add(new ConnectionRecord(t, conn));
|
|
||||||
}
|
|
||||||
eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming));
|
|
||||||
if (firstConnection) {
|
|
||||||
LOG.info("Contact connected");
|
|
||||||
eventBus.broadcast(new ContactConnectedEvent(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPriority(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, Priority priority) {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Setting connection priority: " + t);
|
|
||||||
List<InterruptibleConnection> toInterrupt;
|
|
||||||
boolean interruptNewConnection = false;
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ConnectionRecord> recs = contactConnections.get(c);
|
|
||||||
if (recs == null) throw new IllegalArgumentException();
|
|
||||||
toInterrupt = new ArrayList<>(recs.size());
|
|
||||||
for (ConnectionRecord rec : recs) {
|
|
||||||
if (rec.conn == conn) {
|
|
||||||
// Store the priority of this connection
|
|
||||||
rec.priority = priority;
|
|
||||||
} else if (rec.priority != null) {
|
|
||||||
int compare = compareConnections(t, priority,
|
|
||||||
rec.transportId, rec.priority);
|
|
||||||
if (compare == -1) {
|
|
||||||
// The old connection is better than the new one
|
|
||||||
interruptNewConnection = true;
|
|
||||||
} else if (compare == 1 && !rec.interrupted) {
|
|
||||||
// The new connection is better than the old one
|
|
||||||
toInterrupt.add(rec.conn);
|
|
||||||
rec.interrupted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (interruptNewConnection) {
|
|
||||||
LOG.info("Interrupting new connection");
|
|
||||||
conn.interruptOutgoingSession();
|
|
||||||
}
|
|
||||||
for (InterruptibleConnection old : toInterrupt) {
|
|
||||||
LOG.info("Interrupting old connection");
|
|
||||||
old.interruptOutgoingSession();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int compareConnections(TransportId tA, Priority pA, TransportId tB,
|
|
||||||
Priority pB) {
|
|
||||||
if (getBetterTransports(tA).contains(tB)) return -1;
|
|
||||||
if (getBetterTransports(tB).contains(tA)) return 1;
|
|
||||||
return tA.equals(tB) ? Bytes.compare(pA.getNonce(), pB.getNonce()) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TransportId> getBetterTransports(TransportId t) {
|
|
||||||
List<TransportId> better = transportPrefs.get(t);
|
|
||||||
return better == null ? emptyList() : better;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unregisterConnection(ContactId c, TransportId t,
|
|
||||||
InterruptibleConnection conn, boolean incoming, boolean exception) {
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
if (incoming) LOG.info("Incoming connection unregistered: " + t);
|
|
||||||
else LOG.info("Outgoing connection unregistered: " + t);
|
|
||||||
}
|
|
||||||
boolean lastConnection;
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ConnectionRecord> recs = contactConnections.get(c);
|
|
||||||
if (recs == null || !recs.remove(new ConnectionRecord(t, conn)))
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
lastConnection = recs.isEmpty();
|
|
||||||
}
|
|
||||||
eventBus.broadcast(
|
|
||||||
new ConnectionClosedEvent(c, t, incoming, exception));
|
|
||||||
if (lastConnection) {
|
|
||||||
LOG.info("Contact disconnected");
|
|
||||||
eventBus.broadcast(new ContactDisconnectedEvent(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<ContactId> getConnectedContacts(TransportId t) {
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ContactId> contactIds = new ArrayList<>();
|
|
||||||
for (Entry<ContactId, List<ConnectionRecord>> e :
|
|
||||||
contactConnections.entrySet()) {
|
|
||||||
for (ConnectionRecord rec : e.getValue()) {
|
|
||||||
if (rec.transportId.equals(t)) {
|
|
||||||
contactIds.add(e.getKey());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info(contactIds.size() + " contacts connected: " + t);
|
|
||||||
}
|
|
||||||
return contactIds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<ContactId> getConnectedOrBetterContacts(TransportId t) {
|
|
||||||
synchronized (lock) {
|
|
||||||
List<TransportId> better = getBetterTransports(t);
|
|
||||||
List<ContactId> contactIds = new ArrayList<>();
|
|
||||||
for (Entry<ContactId, List<ConnectionRecord>> e :
|
|
||||||
contactConnections.entrySet()) {
|
|
||||||
for (ConnectionRecord rec : e.getValue()) {
|
|
||||||
if (rec.transportId.equals(t) ||
|
|
||||||
better.contains(rec.transportId)) {
|
|
||||||
contactIds.add(e.getKey());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info(contactIds.size()
|
|
||||||
+ " contacts connected or better: " + t);
|
|
||||||
}
|
|
||||||
return contactIds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConnected(ContactId c, TransportId t) {
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ConnectionRecord> recs = contactConnections.get(c);
|
|
||||||
if (recs == null) return false;
|
|
||||||
for (ConnectionRecord rec : recs) {
|
|
||||||
if (rec.transportId.equals(t)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConnected(ContactId c) {
|
|
||||||
synchronized (lock) {
|
|
||||||
List<ConnectionRecord> recs = contactConnections.get(c);
|
|
||||||
return recs != null && !recs.isEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean registerConnection(PendingContactId p) {
|
|
||||||
boolean added;
|
|
||||||
synchronized (lock) {
|
|
||||||
added = connectedPendingContacts.add(p);
|
|
||||||
}
|
|
||||||
if (added) eventBus.broadcast(new RendezvousConnectionOpenedEvent(p));
|
|
||||||
return added;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unregisterConnection(PendingContactId p, boolean success) {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (!connectedPendingContacts.remove(p))
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ConnectionRecord {
|
|
||||||
|
|
||||||
private final TransportId transportId;
|
|
||||||
private final InterruptibleConnection conn;
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private Priority priority = null;
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean interrupted = false;
|
|
||||||
|
|
||||||
private ConnectionRecord(TransportId transportId,
|
|
||||||
InterruptibleConnection conn) {
|
|
||||||
this.transportId = transportId;
|
|
||||||
this.conn = conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (o instanceof ConnectionRecord) {
|
|
||||||
return conn == ((ConnectionRecord) o).conn;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return conn.hashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.connection.InterruptibleConnection;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class DuplexSyncConnection extends SyncConnection
|
|
||||||
implements InterruptibleConnection {
|
|
||||||
|
|
||||||
final Executor ioExecutor;
|
|
||||||
final TransportId transportId;
|
|
||||||
final TransportConnectionReader reader;
|
|
||||||
final TransportConnectionWriter writer;
|
|
||||||
final TransportProperties remote;
|
|
||||||
|
|
||||||
private final Object interruptLock = new Object();
|
|
||||||
|
|
||||||
@GuardedBy("interruptLock")
|
|
||||||
@Nullable
|
|
||||||
private SyncSession outgoingSession = null;
|
|
||||||
@GuardedBy("interruptLock")
|
|
||||||
private boolean interruptWaiting = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void interruptOutgoingSession() {
|
|
||||||
SyncSession out = null;
|
|
||||||
synchronized (interruptLock) {
|
|
||||||
if (outgoingSession == null) interruptWaiting = true;
|
|
||||||
else out = outgoingSession;
|
|
||||||
}
|
|
||||||
if (out != null) out.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setOutgoingSession(SyncSession outgoingSession) {
|
|
||||||
boolean interruptWasWaiting = false;
|
|
||||||
synchronized (interruptLock) {
|
|
||||||
this.outgoingSession = outgoingSession;
|
|
||||||
if (interruptWaiting) {
|
|
||||||
interruptWasWaiting = true;
|
|
||||||
interruptWaiting = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (interruptWasWaiting) outgoingSession.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
DuplexSyncConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
Executor ioExecutor, TransportId transportId,
|
|
||||||
DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, syncSessionFactory,
|
|
||||||
transportPropertyManager);
|
|
||||||
this.ioExecutor = ioExecutor;
|
|
||||||
this.transportId = transportId;
|
|
||||||
reader = connection.getReader();
|
|
||||||
writer = connection.getWriter();
|
|
||||||
remote = connection.getRemoteProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onReadError(boolean recognised) {
|
|
||||||
disposeOnError(reader, recognised);
|
|
||||||
disposeOnError(writer);
|
|
||||||
interruptOutgoingSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onWriteError() {
|
|
||||||
disposeOnError(reader, true);
|
|
||||||
disposeOnError(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncSession createDuplexOutgoingSession(StreamContext ctx,
|
|
||||||
TransportConnectionWriter w, @Nullable Priority priority)
|
|
||||||
throws IOException {
|
|
||||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
|
||||||
w.getOutputStream(), ctx);
|
|
||||||
ContactId c = requireNonNull(ctx.getContactId());
|
|
||||||
return syncSessionFactory.createDuplexOutgoingSession(c,
|
|
||||||
ctx.getTransportId(), w.getMaxLatency(), w.getMaxIdleTime(),
|
|
||||||
streamWriter, priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class HandshakeConnection extends Connection {
|
|
||||||
|
|
||||||
final HandshakeManager handshakeManager;
|
|
||||||
final ContactExchangeManager contactExchangeManager;
|
|
||||||
final ConnectionManager connectionManager;
|
|
||||||
final PendingContactId pendingContactId;
|
|
||||||
final TransportId transportId;
|
|
||||||
final DuplexTransportConnection connection;
|
|
||||||
final TransportConnectionReader reader;
|
|
||||||
final TransportConnectionWriter writer;
|
|
||||||
|
|
||||||
HandshakeConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
HandshakeManager handshakeManager,
|
|
||||||
ContactExchangeManager contactExchangeManager,
|
|
||||||
ConnectionManager connectionManager,
|
|
||||||
PendingContactId pendingContactId,
|
|
||||||
TransportId transportId, DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory);
|
|
||||||
this.handshakeManager = handshakeManager;
|
|
||||||
this.contactExchangeManager = contactExchangeManager;
|
|
||||||
this.connectionManager = connectionManager;
|
|
||||||
this.pendingContactId = pendingContactId;
|
|
||||||
this.transportId = transportId;
|
|
||||||
this.connection = connection;
|
|
||||||
reader = connection.getReader();
|
|
||||||
writer = connection.getWriter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
StreamContext allocateStreamContext(PendingContactId pendingContactId,
|
|
||||||
TransportId transportId) {
|
|
||||||
try {
|
|
||||||
return keyManager.getStreamContext(pendingContactId, transportId);
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onError(boolean recognised) {
|
|
||||||
disposeOnError(reader, recognised);
|
|
||||||
disposeOnError(writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.PriorityHandler;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class IncomingDuplexSyncConnection extends DuplexSyncConnection
|
|
||||||
implements Runnable {
|
|
||||||
|
|
||||||
IncomingDuplexSyncConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
Executor ioExecutor, TransportId transportId,
|
|
||||||
DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, syncSessionFactory,
|
|
||||||
transportPropertyManager, ioExecutor, transportId, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Read and recognise the tag
|
|
||||||
StreamContext ctx = recogniseTag(reader, transportId);
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.info("Unrecognised tag");
|
|
||||||
onReadError(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ContactId contactId = ctx.getContactId();
|
|
||||||
if (contactId == null) {
|
|
||||||
LOG.warning("Expected contact tag, got rendezvous tag");
|
|
||||||
onReadError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ctx.isHandshakeMode()) {
|
|
||||||
// TODO: Support handshake mode for contacts
|
|
||||||
LOG.warning("Received handshake tag, expected rotation mode");
|
|
||||||
onReadError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connectionRegistry.registerIncomingConnection(contactId, transportId,
|
|
||||||
this);
|
|
||||||
// Start the outgoing session on another thread
|
|
||||||
ioExecutor.execute(() -> runOutgoingSession(contactId));
|
|
||||||
try {
|
|
||||||
// Store any transport properties discovered from the connection
|
|
||||||
transportPropertyManager.addRemotePropertiesFromConnection(
|
|
||||||
contactId, transportId, remote);
|
|
||||||
// Update the connection registry when we receive our priority
|
|
||||||
PriorityHandler handler = p -> connectionRegistry.setPriority(
|
|
||||||
contactId, transportId, this, p);
|
|
||||||
// Create and run the incoming session
|
|
||||||
createIncomingSession(ctx, reader, handler).run();
|
|
||||||
reader.dispose(false, true);
|
|
||||||
interruptOutgoingSession();
|
|
||||||
connectionRegistry.unregisterConnection(contactId, transportId,
|
|
||||||
this, true, false);
|
|
||||||
} catch (DbException | IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onReadError(true);
|
|
||||||
connectionRegistry.unregisterConnection(contactId, transportId,
|
|
||||||
this, true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runOutgoingSession(ContactId contactId) {
|
|
||||||
// Allocate a stream context
|
|
||||||
StreamContext ctx = allocateStreamContext(contactId, transportId);
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.warning("Could not allocate stream context");
|
|
||||||
onWriteError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Create and run the outgoing session
|
|
||||||
SyncSession out = createDuplexOutgoingSession(ctx, writer, null);
|
|
||||||
setOutgoingSession(out);
|
|
||||||
out.run();
|
|
||||||
writer.dispose(false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onWriteError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class IncomingHandshakeConnection extends HandshakeConnection
|
|
||||||
implements Runnable {
|
|
||||||
|
|
||||||
IncomingHandshakeConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
HandshakeManager handshakeManager,
|
|
||||||
ContactExchangeManager contactExchangeManager,
|
|
||||||
ConnectionManager connectionManager,
|
|
||||||
PendingContactId pendingContactId,
|
|
||||||
TransportId transportId, DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, handshakeManager, contactExchangeManager,
|
|
||||||
connectionManager, pendingContactId, transportId, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Read and recognise the tag
|
|
||||||
StreamContext ctxIn = recogniseTag(reader, transportId);
|
|
||||||
if (ctxIn == null) {
|
|
||||||
LOG.info("Unrecognised tag");
|
|
||||||
onError(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
|
|
||||||
if (inPendingContactId == null) {
|
|
||||||
LOG.warning("Expected rendezvous tag, got contact tag");
|
|
||||||
onError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Allocate the outgoing stream context
|
|
||||||
StreamContext ctxOut =
|
|
||||||
allocateStreamContext(pendingContactId, transportId);
|
|
||||||
if (ctxOut == null) {
|
|
||||||
LOG.warning("Could not allocate stream context");
|
|
||||||
onError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Close the connection if it's redundant
|
|
||||||
if (!connectionRegistry.registerConnection(pendingContactId)) {
|
|
||||||
LOG.info("Redundant rendezvous connection");
|
|
||||||
onError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Handshake and exchange contacts
|
|
||||||
try {
|
|
||||||
InputStream in = streamReaderFactory.createStreamReader(
|
|
||||||
reader.getInputStream(), ctxIn);
|
|
||||||
// Flush the output stream to send the outgoing stream header
|
|
||||||
StreamWriter out = streamWriterFactory.createStreamWriter(
|
|
||||||
writer.getOutputStream(), ctxOut);
|
|
||||||
out.getOutputStream().flush();
|
|
||||||
HandshakeResult result =
|
|
||||||
handshakeManager.handshake(pendingContactId, in, out);
|
|
||||||
contactExchangeManager.exchangeContacts(pendingContactId,
|
|
||||||
connection, result.getMasterKey(), result.isAlice(), false);
|
|
||||||
connectionRegistry.unregisterConnection(pendingContactId, true);
|
|
||||||
// Reuse the connection as a transport connection
|
|
||||||
connectionManager.manageIncomingConnection(transportId, connection);
|
|
||||||
} catch (IOException | DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError(true);
|
|
||||||
connectionRegistry.unregisterConnection(pendingContactId, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.PriorityHandler;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|
||||||
|
|
||||||
private final TransportId transportId;
|
|
||||||
private final TransportConnectionReader reader;
|
|
||||||
|
|
||||||
IncomingSimplexSyncConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
TransportId transportId, TransportConnectionReader reader) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, syncSessionFactory,
|
|
||||||
transportPropertyManager);
|
|
||||||
this.transportId = transportId;
|
|
||||||
this.reader = reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Read and recognise the tag
|
|
||||||
StreamContext ctx = recogniseTag(reader, transportId);
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.info("Unrecognised tag");
|
|
||||||
onError(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ContactId contactId = ctx.getContactId();
|
|
||||||
if (contactId == null) {
|
|
||||||
LOG.warning("Received rendezvous stream, expected contact");
|
|
||||||
onError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ctx.isHandshakeMode()) {
|
|
||||||
// TODO: Support handshake mode for contacts
|
|
||||||
LOG.warning("Received handshake tag, expected rotation mode");
|
|
||||||
onError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// We don't expect to receive a priority for this connection
|
|
||||||
PriorityHandler handler = p ->
|
|
||||||
LOG.info("Ignoring priority for simplex connection");
|
|
||||||
// Create and run the incoming session
|
|
||||||
createIncomingSession(ctx, reader, handler).run();
|
|
||||||
reader.dispose(false, true);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onError(boolean recognised) {
|
|
||||||
disposeOnError(reader, recognised);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
|
||||||
import org.briarproject.bramble.api.sync.PriorityHandler;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class OutgoingDuplexSyncConnection extends DuplexSyncConnection
|
|
||||||
implements Runnable {
|
|
||||||
|
|
||||||
private final SecureRandom secureRandom;
|
|
||||||
private final ContactId contactId;
|
|
||||||
|
|
||||||
OutgoingDuplexSyncConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
Executor ioExecutor, SecureRandom secureRandom, ContactId contactId,
|
|
||||||
TransportId transportId, DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, syncSessionFactory,
|
|
||||||
transportPropertyManager, ioExecutor, transportId, connection);
|
|
||||||
this.secureRandom = secureRandom;
|
|
||||||
this.contactId = contactId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Allocate a stream context
|
|
||||||
StreamContext ctx = allocateStreamContext(contactId, transportId);
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.warning("Could not allocate stream context");
|
|
||||||
onWriteError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ctx.isHandshakeMode()) {
|
|
||||||
// TODO: Support handshake mode for contacts
|
|
||||||
LOG.warning("Cannot use handshake mode stream context");
|
|
||||||
onWriteError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Start the incoming session on another thread
|
|
||||||
Priority priority = generatePriority();
|
|
||||||
ioExecutor.execute(() -> runIncomingSession(priority));
|
|
||||||
try {
|
|
||||||
// Create and run the outgoing session
|
|
||||||
SyncSession out =
|
|
||||||
createDuplexOutgoingSession(ctx, writer, priority);
|
|
||||||
setOutgoingSession(out);
|
|
||||||
out.run();
|
|
||||||
writer.dispose(false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onWriteError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runIncomingSession(Priority priority) {
|
|
||||||
// Read and recognise the tag
|
|
||||||
StreamContext ctx = recogniseTag(reader, transportId);
|
|
||||||
// Unrecognised tags are suspicious in this case
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.warning("Unrecognised tag for returning stream");
|
|
||||||
onReadError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Check that the stream comes from the expected contact
|
|
||||||
ContactId inContactId = ctx.getContactId();
|
|
||||||
if (inContactId == null) {
|
|
||||||
LOG.warning("Expected contact tag, got rendezvous tag");
|
|
||||||
onReadError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!contactId.equals(inContactId)) {
|
|
||||||
LOG.warning("Wrong contact ID for returning stream");
|
|
||||||
onReadError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ctx.isHandshakeMode()) {
|
|
||||||
// TODO: Support handshake mode for contacts
|
|
||||||
LOG.warning("Received handshake tag, expected rotation mode");
|
|
||||||
onReadError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connectionRegistry.registerOutgoingConnection(contactId, transportId,
|
|
||||||
this, priority);
|
|
||||||
try {
|
|
||||||
// Store any transport properties discovered from the connection
|
|
||||||
transportPropertyManager.addRemotePropertiesFromConnection(
|
|
||||||
contactId, transportId, remote);
|
|
||||||
// We don't expect to receive a priority for this connection
|
|
||||||
PriorityHandler handler = p ->
|
|
||||||
LOG.info("Ignoring priority for outgoing connection");
|
|
||||||
// Create and run the incoming session
|
|
||||||
createIncomingSession(ctx, reader, handler).run();
|
|
||||||
reader.dispose(false, true);
|
|
||||||
interruptOutgoingSession();
|
|
||||||
connectionRegistry.unregisterConnection(contactId, transportId,
|
|
||||||
this, false, false);
|
|
||||||
} catch (DbException | IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onReadError();
|
|
||||||
connectionRegistry.unregisterConnection(contactId, transportId,
|
|
||||||
this, false, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onReadError() {
|
|
||||||
// 'Recognised' is always true for outgoing connections
|
|
||||||
onReadError(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Priority generatePriority() {
|
|
||||||
byte[] nonce = new byte[PRIORITY_NONCE_BYTES];
|
|
||||||
secureRandom.nextBytes(nonce);
|
|
||||||
return new Priority(nonce);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class OutgoingHandshakeConnection extends HandshakeConnection
|
|
||||||
implements Runnable {
|
|
||||||
|
|
||||||
OutgoingHandshakeConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
HandshakeManager handshakeManager,
|
|
||||||
ContactExchangeManager contactExchangeManager,
|
|
||||||
ConnectionManager connectionManager,
|
|
||||||
PendingContactId pendingContactId,
|
|
||||||
TransportId transportId, DuplexTransportConnection connection) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, handshakeManager, contactExchangeManager,
|
|
||||||
connectionManager, pendingContactId, transportId, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Allocate the outgoing stream context
|
|
||||||
StreamContext ctxOut =
|
|
||||||
allocateStreamContext(pendingContactId, transportId);
|
|
||||||
if (ctxOut == null) {
|
|
||||||
LOG.warning("Could not allocate stream context");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Flush the output stream to send the outgoing stream header
|
|
||||||
StreamWriter out;
|
|
||||||
try {
|
|
||||||
out = streamWriterFactory.createStreamWriter(
|
|
||||||
writer.getOutputStream(), ctxOut);
|
|
||||||
out.getOutputStream().flush();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Read and recognise the tag
|
|
||||||
StreamContext ctxIn = recogniseTag(reader, transportId);
|
|
||||||
// Unrecognised tags are suspicious in this case
|
|
||||||
if (ctxIn == null) {
|
|
||||||
LOG.warning("Unrecognised tag for returning stream");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Check that the stream comes from the expected pending contact
|
|
||||||
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
|
|
||||||
if (inPendingContactId == null) {
|
|
||||||
LOG.warning("Expected rendezvous tag, got contact tag");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!inPendingContactId.equals(pendingContactId)) {
|
|
||||||
LOG.warning("Wrong pending contact ID for returning stream");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Close the connection if it's redundant
|
|
||||||
if (!connectionRegistry.registerConnection(pendingContactId)) {
|
|
||||||
LOG.info("Redundant rendezvous connection");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Handshake and exchange contacts
|
|
||||||
try {
|
|
||||||
InputStream in = streamReaderFactory.createStreamReader(
|
|
||||||
reader.getInputStream(), ctxIn);
|
|
||||||
HandshakeResult result =
|
|
||||||
handshakeManager.handshake(pendingContactId, in, out);
|
|
||||||
Contact contact = contactExchangeManager.exchangeContacts(
|
|
||||||
pendingContactId, connection, result.getMasterKey(),
|
|
||||||
result.isAlice(), false);
|
|
||||||
connectionRegistry.unregisterConnection(pendingContactId, true);
|
|
||||||
// Reuse the connection as a transport connection
|
|
||||||
connectionManager.manageOutgoingConnection(contact.getId(),
|
|
||||||
transportId, connection);
|
|
||||||
} catch (IOException | DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError();
|
|
||||||
connectionRegistry.unregisterConnection(pendingContactId, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onError() {
|
|
||||||
// 'Recognised' is always true for outgoing connections
|
|
||||||
onError(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class OutgoingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|
||||||
|
|
||||||
private final ContactId contactId;
|
|
||||||
private final TransportId transportId;
|
|
||||||
private final TransportConnectionWriter writer;
|
|
||||||
|
|
||||||
OutgoingSimplexSyncConnection(KeyManager keyManager,
|
|
||||||
ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
ContactId contactId, TransportId transportId,
|
|
||||||
TransportConnectionWriter writer) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory, syncSessionFactory,
|
|
||||||
transportPropertyManager);
|
|
||||||
this.contactId = contactId;
|
|
||||||
this.transportId = transportId;
|
|
||||||
this.writer = writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Allocate a stream context
|
|
||||||
StreamContext ctx = allocateStreamContext(contactId, transportId);
|
|
||||||
if (ctx == null) {
|
|
||||||
LOG.warning("Could not allocate stream context");
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Create and run the outgoing session
|
|
||||||
createSimplexOutgoingSession(ctx, writer).run();
|
|
||||||
writer.dispose(false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onError() {
|
|
||||||
disposeOnError(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
|
|
||||||
TransportConnectionWriter w) throws IOException {
|
|
||||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
|
||||||
w.getOutputStream(), ctx);
|
|
||||||
ContactId c = requireNonNull(ctx.getContactId());
|
|
||||||
return syncSessionFactory.createSimplexOutgoingSession(c,
|
|
||||||
ctx.getTransportId(), w.getMaxLatency(), streamWriter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package org.briarproject.bramble.connection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.bramble.api.sync.PriorityHandler;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class SyncConnection extends Connection {
|
|
||||||
|
|
||||||
final SyncSessionFactory syncSessionFactory;
|
|
||||||
final TransportPropertyManager transportPropertyManager;
|
|
||||||
|
|
||||||
SyncConnection(KeyManager keyManager, ConnectionRegistry connectionRegistry,
|
|
||||||
StreamReaderFactory streamReaderFactory,
|
|
||||||
StreamWriterFactory streamWriterFactory,
|
|
||||||
SyncSessionFactory syncSessionFactory,
|
|
||||||
TransportPropertyManager transportPropertyManager) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
|
||||||
streamWriterFactory);
|
|
||||||
this.syncSessionFactory = syncSessionFactory;
|
|
||||||
this.transportPropertyManager = transportPropertyManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
StreamContext allocateStreamContext(ContactId contactId,
|
|
||||||
TransportId transportId) {
|
|
||||||
try {
|
|
||||||
return keyManager.getStreamContext(contactId, transportId);
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncSession createIncomingSession(StreamContext ctx,
|
|
||||||
TransportConnectionReader r, PriorityHandler handler)
|
|
||||||
throws IOException {
|
|
||||||
InputStream streamReader = streamReaderFactory.createStreamReader(
|
|
||||||
r.getInputStream(), ctx);
|
|
||||||
ContactId c = requireNonNull(ctx.getContactId());
|
|
||||||
return syncSessionFactory
|
|
||||||
.createIncomingSession(c, streamReader, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class IoModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
TimeoutMonitor provideTimeoutMonitor(TimeoutMonitorImpl timeoutMonitor) {
|
|
||||||
return timeoutMonitor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class TimeoutInputStream extends InputStream {
|
|
||||||
|
|
||||||
private final Clock clock;
|
|
||||||
private final InputStream in;
|
|
||||||
private final long timeoutMs;
|
|
||||||
private final CloseListener listener;
|
|
||||||
private final Object lock = new Object();
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private long readStartedMs = -1;
|
|
||||||
|
|
||||||
TimeoutInputStream(Clock clock, InputStream in, long timeoutMs,
|
|
||||||
CloseListener listener) {
|
|
||||||
this.clock = clock;
|
|
||||||
this.in = in;
|
|
||||||
this.timeoutMs = timeoutMs;
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
synchronized (lock) {
|
|
||||||
readStartedMs = clock.currentTimeMillis();
|
|
||||||
}
|
|
||||||
int input = in.read();
|
|
||||||
synchronized (lock) {
|
|
||||||
readStartedMs = -1;
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b) throws IOException {
|
|
||||||
return read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
|
||||||
synchronized (lock) {
|
|
||||||
readStartedMs = clock.currentTimeMillis();
|
|
||||||
}
|
|
||||||
int read = in.read(b, off, len);
|
|
||||||
synchronized (lock) {
|
|
||||||
readStartedMs = -1;
|
|
||||||
}
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
try {
|
|
||||||
in.close();
|
|
||||||
} finally {
|
|
||||||
listener.onClose(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int available() throws IOException {
|
|
||||||
return in.available();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mark(int readlimit) {
|
|
||||||
in.mark(readlimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean markSupported() {
|
|
||||||
return in.markSupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() throws IOException {
|
|
||||||
in.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long skip(long n) throws IOException {
|
|
||||||
return in.skip(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasTimedOut() {
|
|
||||||
synchronized (lock) {
|
|
||||||
return readStartedMs != -1 &&
|
|
||||||
clock.currentTimeMillis() - readStartedMs > timeoutMs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CloseListener {
|
|
||||||
|
|
||||||
void onClose(TimeoutInputStream closed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
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.api.system.Wakeful;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
class TimeoutMonitorImpl implements TimeoutMonitor {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(TimeoutMonitorImpl.class.getName());
|
|
||||||
|
|
||||||
private static final long CHECK_INTERVAL_MS = SECONDS.toMillis(10);
|
|
||||||
|
|
||||||
private final TaskScheduler scheduler;
|
|
||||||
private final Executor ioExecutor;
|
|
||||||
private final Clock clock;
|
|
||||||
private final Object lock = new Object();
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private final List<TimeoutInputStream> streams = new ArrayList<>();
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private Cancellable cancellable = null;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
TimeoutMonitorImpl(TaskScheduler scheduler,
|
|
||||||
@IoExecutor Executor ioExecutor, Clock clock) {
|
|
||||||
this.scheduler = scheduler;
|
|
||||||
this.ioExecutor = ioExecutor;
|
|
||||||
this.clock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream createTimeoutInputStream(InputStream in,
|
|
||||||
long timeoutMs) {
|
|
||||||
TimeoutInputStream stream = new TimeoutInputStream(clock, in,
|
|
||||||
timeoutMs, this::removeStream);
|
|
||||||
synchronized (lock) {
|
|
||||||
if (streams.isEmpty()) {
|
|
||||||
cancellable = scheduler.scheduleWithFixedDelay(
|
|
||||||
this::checkTimeouts, ioExecutor, CHECK_INTERVAL_MS,
|
|
||||||
CHECK_INTERVAL_MS, MILLISECONDS);
|
|
||||||
}
|
|
||||||
streams.add(stream);
|
|
||||||
}
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeStream(TimeoutInputStream stream) {
|
|
||||||
Cancellable toCancel = null;
|
|
||||||
synchronized (lock) {
|
|
||||||
if (streams.remove(stream) && streams.isEmpty()) {
|
|
||||||
toCancel = cancellable;
|
|
||||||
cancellable = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (toCancel != null) {
|
|
||||||
LOG.info("Cancelling timeout monitor task");
|
|
||||||
toCancel.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@IoExecutor
|
|
||||||
@Wakeful
|
|
||||||
private void checkTimeouts() {
|
|
||||||
List<TimeoutInputStream> snapshot;
|
|
||||||
synchronized (lock) {
|
|
||||||
snapshot = new ArrayList<>(streams);
|
|
||||||
}
|
|
||||||
for (TimeoutInputStream stream : snapshot) {
|
|
||||||
if (stream.hasTimedOut()) {
|
|
||||||
LOG.info("Input stream has timed out");
|
|
||||||
try {
|
|
||||||
stream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, INFO, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,709 @@
|
|||||||
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.HandshakeManager;
|
||||||
|
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||||
|
import org.briarproject.bramble.api.sync.SyncSession;
|
||||||
|
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
||||||
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
|
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||||
|
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||||
|
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.IoUtils.read;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class ConnectionManagerImpl implements ConnectionManager {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(ConnectionManagerImpl.class.getName());
|
||||||
|
|
||||||
|
private final Executor ioExecutor;
|
||||||
|
private final KeyManager keyManager;
|
||||||
|
private final StreamReaderFactory streamReaderFactory;
|
||||||
|
private final StreamWriterFactory streamWriterFactory;
|
||||||
|
private final SyncSessionFactory syncSessionFactory;
|
||||||
|
private final HandshakeManager handshakeManager;
|
||||||
|
private final ContactExchangeManager contactExchangeManager;
|
||||||
|
private final ConnectionRegistry connectionRegistry;
|
||||||
|
private final TransportPropertyManager transportPropertyManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ConnectionManagerImpl(@IoExecutor Executor ioExecutor,
|
||||||
|
KeyManager keyManager, StreamReaderFactory streamReaderFactory,
|
||||||
|
StreamWriterFactory streamWriterFactory,
|
||||||
|
SyncSessionFactory syncSessionFactory,
|
||||||
|
HandshakeManager handshakeManager,
|
||||||
|
ContactExchangeManager contactExchangeManager,
|
||||||
|
ConnectionRegistry connectionRegistry,
|
||||||
|
TransportPropertyManager transportPropertyManager) {
|
||||||
|
this.ioExecutor = ioExecutor;
|
||||||
|
this.keyManager = keyManager;
|
||||||
|
this.streamReaderFactory = streamReaderFactory;
|
||||||
|
this.streamWriterFactory = streamWriterFactory;
|
||||||
|
this.syncSessionFactory = syncSessionFactory;
|
||||||
|
this.handshakeManager = handshakeManager;
|
||||||
|
this.contactExchangeManager = contactExchangeManager;
|
||||||
|
this.connectionRegistry = connectionRegistry;
|
||||||
|
this.transportPropertyManager = transportPropertyManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageIncomingConnection(TransportId t,
|
||||||
|
TransportConnectionReader r) {
|
||||||
|
ioExecutor.execute(new ManageIncomingSimplexConnection(t, r));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageIncomingConnection(TransportId t,
|
||||||
|
DuplexTransportConnection d) {
|
||||||
|
ioExecutor.execute(new ManageIncomingDuplexConnection(t, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageIncomingConnection(PendingContactId p, TransportId t,
|
||||||
|
DuplexTransportConnection d) {
|
||||||
|
ioExecutor.execute(new ManageIncomingHandshakeConnection(p, t, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageOutgoingConnection(ContactId c, TransportId t,
|
||||||
|
TransportConnectionWriter w) {
|
||||||
|
ioExecutor.execute(new ManageOutgoingSimplexConnection(c, t, w));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageOutgoingConnection(ContactId c, TransportId t,
|
||||||
|
DuplexTransportConnection d) {
|
||||||
|
ioExecutor.execute(new ManageOutgoingDuplexConnection(c, t, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageOutgoingConnection(PendingContactId p, TransportId t,
|
||||||
|
DuplexTransportConnection d) {
|
||||||
|
ioExecutor.execute(new ManageOutgoingHandshakeConnection(p, t, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readTag(InputStream in) throws IOException {
|
||||||
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
|
read(in, tag);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SyncSession createIncomingSession(StreamContext ctx,
|
||||||
|
TransportConnectionReader r) throws IOException {
|
||||||
|
InputStream streamReader = streamReaderFactory.createStreamReader(
|
||||||
|
r.getInputStream(), ctx);
|
||||||
|
ContactId c = requireNonNull(ctx.getContactId());
|
||||||
|
return syncSessionFactory.createIncomingSession(c, streamReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
|
||||||
|
TransportConnectionWriter w) throws IOException {
|
||||||
|
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||||
|
w.getOutputStream(), ctx);
|
||||||
|
ContactId c = requireNonNull(ctx.getContactId());
|
||||||
|
return syncSessionFactory.createSimplexOutgoingSession(c,
|
||||||
|
w.getMaxLatency(), streamWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SyncSession createDuplexOutgoingSession(StreamContext ctx,
|
||||||
|
TransportConnectionWriter w) throws IOException {
|
||||||
|
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||||
|
w.getOutputStream(), ctx);
|
||||||
|
ContactId c = requireNonNull(ctx.getContactId());
|
||||||
|
return syncSessionFactory.createDuplexOutgoingSession(c,
|
||||||
|
w.getMaxLatency(), w.getMaxIdleTime(), streamWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disposeOnError(TransportConnectionReader reader,
|
||||||
|
boolean recognised) {
|
||||||
|
try {
|
||||||
|
reader.dispose(true, recognised);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disposeOnError(TransportConnectionWriter writer) {
|
||||||
|
try {
|
||||||
|
writer.dispose(true);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageIncomingSimplexConnection implements Runnable {
|
||||||
|
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final TransportConnectionReader reader;
|
||||||
|
|
||||||
|
private ManageIncomingSimplexConnection(TransportId transportId,
|
||||||
|
TransportConnectionReader reader) {
|
||||||
|
this.transportId = transportId;
|
||||||
|
this.reader = reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Read and recognise the tag
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
|
ctx = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.info("Unrecognised tag");
|
||||||
|
onError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ContactId contactId = ctx.getContactId();
|
||||||
|
if (contactId == null) {
|
||||||
|
LOG.warning("Received rendezvous stream, expected contact");
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx.isHandshakeMode()) {
|
||||||
|
// TODO: Support handshake mode for contacts
|
||||||
|
LOG.warning("Received handshake tag, expected rotation mode");
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectionRegistry.registerConnection(contactId, transportId, true);
|
||||||
|
try {
|
||||||
|
// Create and run the incoming session
|
||||||
|
createIncomingSession(ctx, reader).run();
|
||||||
|
reader.dispose(false, true);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError(true);
|
||||||
|
} finally {
|
||||||
|
connectionRegistry.unregisterConnection(contactId, transportId,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError(boolean recognised) {
|
||||||
|
disposeOnError(reader, recognised);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageOutgoingSimplexConnection implements Runnable {
|
||||||
|
|
||||||
|
private final ContactId contactId;
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final TransportConnectionWriter writer;
|
||||||
|
|
||||||
|
private ManageOutgoingSimplexConnection(ContactId contactId,
|
||||||
|
TransportId transportId, TransportConnectionWriter writer) {
|
||||||
|
this.contactId = contactId;
|
||||||
|
this.transportId = transportId;
|
||||||
|
this.writer = writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Allocate a stream context
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
ctx = keyManager.getStreamContext(contactId, transportId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.warning("Could not allocate stream context");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectionRegistry.registerConnection(contactId, transportId,
|
||||||
|
false);
|
||||||
|
try {
|
||||||
|
// Create and run the outgoing session
|
||||||
|
createSimplexOutgoingSession(ctx, writer).run();
|
||||||
|
writer.dispose(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
} finally {
|
||||||
|
connectionRegistry.unregisterConnection(contactId, transportId,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError() {
|
||||||
|
disposeOnError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageIncomingDuplexConnection implements Runnable {
|
||||||
|
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final TransportConnectionReader reader;
|
||||||
|
private final TransportConnectionWriter writer;
|
||||||
|
private final TransportProperties remote;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private volatile SyncSession outgoingSession = null;
|
||||||
|
|
||||||
|
private ManageIncomingDuplexConnection(TransportId transportId,
|
||||||
|
DuplexTransportConnection connection) {
|
||||||
|
this.transportId = transportId;
|
||||||
|
reader = connection.getReader();
|
||||||
|
writer = connection.getWriter();
|
||||||
|
remote = connection.getRemoteProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Read and recognise the tag
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
|
ctx = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onReadError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.info("Unrecognised tag");
|
||||||
|
onReadError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ContactId contactId = ctx.getContactId();
|
||||||
|
if (contactId == null) {
|
||||||
|
LOG.warning("Expected contact tag, got rendezvous tag");
|
||||||
|
onReadError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx.isHandshakeMode()) {
|
||||||
|
// TODO: Support handshake mode for contacts
|
||||||
|
LOG.warning("Received handshake tag, expected rotation mode");
|
||||||
|
onReadError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectionRegistry.registerConnection(contactId, transportId, true);
|
||||||
|
// Start the outgoing session on another thread
|
||||||
|
ioExecutor.execute(() -> runOutgoingSession(contactId));
|
||||||
|
try {
|
||||||
|
// Store any transport properties discovered from the connection
|
||||||
|
transportPropertyManager.addRemotePropertiesFromConnection(
|
||||||
|
contactId, transportId, remote);
|
||||||
|
// Create and run the incoming session
|
||||||
|
createIncomingSession(ctx, reader).run();
|
||||||
|
reader.dispose(false, true);
|
||||||
|
// Interrupt the outgoing session so it finishes cleanly
|
||||||
|
SyncSession out = outgoingSession;
|
||||||
|
if (out != null) out.interrupt();
|
||||||
|
} catch (DbException | IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onReadError(true);
|
||||||
|
} finally {
|
||||||
|
connectionRegistry.unregisterConnection(contactId, transportId,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runOutgoingSession(ContactId contactId) {
|
||||||
|
// Allocate a stream context
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
ctx = keyManager.getStreamContext(contactId, transportId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onWriteError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.warning("Could not allocate stream context");
|
||||||
|
onWriteError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Create and run the outgoing session
|
||||||
|
SyncSession out = createDuplexOutgoingSession(ctx, writer);
|
||||||
|
outgoingSession = out;
|
||||||
|
out.run();
|
||||||
|
writer.dispose(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onWriteError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onReadError(boolean recognised) {
|
||||||
|
disposeOnError(reader, recognised);
|
||||||
|
disposeOnError(writer);
|
||||||
|
// Interrupt the outgoing session so it finishes
|
||||||
|
SyncSession out = outgoingSession;
|
||||||
|
if (out != null) out.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onWriteError() {
|
||||||
|
disposeOnError(reader, true);
|
||||||
|
disposeOnError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageOutgoingDuplexConnection implements Runnable {
|
||||||
|
|
||||||
|
private final ContactId contactId;
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final TransportConnectionReader reader;
|
||||||
|
private final TransportConnectionWriter writer;
|
||||||
|
private final TransportProperties remote;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private volatile SyncSession outgoingSession = null;
|
||||||
|
|
||||||
|
private ManageOutgoingDuplexConnection(ContactId contactId,
|
||||||
|
TransportId transportId, DuplexTransportConnection connection) {
|
||||||
|
this.contactId = contactId;
|
||||||
|
this.transportId = transportId;
|
||||||
|
reader = connection.getReader();
|
||||||
|
writer = connection.getWriter();
|
||||||
|
remote = connection.getRemoteProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Allocate a stream context
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
ctx = keyManager.getStreamContext(contactId, transportId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onWriteError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.warning("Could not allocate stream context");
|
||||||
|
onWriteError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx.isHandshakeMode()) {
|
||||||
|
// TODO: Support handshake mode for contacts
|
||||||
|
LOG.warning("Cannot use handshake mode stream context");
|
||||||
|
onWriteError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Start the incoming session on another thread
|
||||||
|
ioExecutor.execute(this::runIncomingSession);
|
||||||
|
try {
|
||||||
|
// Create and run the outgoing session
|
||||||
|
SyncSession out = createDuplexOutgoingSession(ctx, writer);
|
||||||
|
outgoingSession = out;
|
||||||
|
out.run();
|
||||||
|
writer.dispose(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onWriteError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runIncomingSession() {
|
||||||
|
// Read and recognise the tag
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
|
ctx = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onReadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Unrecognised tags are suspicious in this case
|
||||||
|
if (ctx == null) {
|
||||||
|
LOG.warning("Unrecognised tag for returning stream");
|
||||||
|
onReadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check that the stream comes from the expected contact
|
||||||
|
ContactId inContactId = ctx.getContactId();
|
||||||
|
if (inContactId == null) {
|
||||||
|
LOG.warning("Expected contact tag, got rendezvous tag");
|
||||||
|
onReadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!contactId.equals(inContactId)) {
|
||||||
|
LOG.warning("Wrong contact ID for returning stream");
|
||||||
|
onReadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx.isHandshakeMode()) {
|
||||||
|
// TODO: Support handshake mode for contacts
|
||||||
|
LOG.warning("Received handshake tag, expected rotation mode");
|
||||||
|
onReadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectionRegistry.registerConnection(contactId, transportId,
|
||||||
|
false);
|
||||||
|
try {
|
||||||
|
// Store any transport properties discovered from the connection
|
||||||
|
transportPropertyManager.addRemotePropertiesFromConnection(
|
||||||
|
contactId, transportId, remote);
|
||||||
|
// Create and run the incoming session
|
||||||
|
createIncomingSession(ctx, reader).run();
|
||||||
|
reader.dispose(false, true);
|
||||||
|
// Interrupt the outgoing session so it finishes cleanly
|
||||||
|
SyncSession out = outgoingSession;
|
||||||
|
if (out != null) out.interrupt();
|
||||||
|
} catch (DbException | IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onReadError();
|
||||||
|
} finally {
|
||||||
|
connectionRegistry.unregisterConnection(contactId, transportId,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onReadError() {
|
||||||
|
// 'Recognised' is always true for outgoing connections
|
||||||
|
disposeOnError(reader, true);
|
||||||
|
disposeOnError(writer);
|
||||||
|
// Interrupt the outgoing session so it finishes
|
||||||
|
SyncSession out = outgoingSession;
|
||||||
|
if (out != null) out.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onWriteError() {
|
||||||
|
disposeOnError(reader, true);
|
||||||
|
disposeOnError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageIncomingHandshakeConnection implements Runnable {
|
||||||
|
|
||||||
|
private final PendingContactId pendingContactId;
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final DuplexTransportConnection connection;
|
||||||
|
private final TransportConnectionReader reader;
|
||||||
|
private final TransportConnectionWriter writer;
|
||||||
|
|
||||||
|
private ManageIncomingHandshakeConnection(
|
||||||
|
PendingContactId pendingContactId, TransportId transportId,
|
||||||
|
DuplexTransportConnection connection) {
|
||||||
|
this.pendingContactId = pendingContactId;
|
||||||
|
this.transportId = transportId;
|
||||||
|
this.connection = connection;
|
||||||
|
reader = connection.getReader();
|
||||||
|
writer = connection.getWriter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Read and recognise the tag
|
||||||
|
StreamContext ctxIn;
|
||||||
|
try {
|
||||||
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
|
ctxIn = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctxIn == null) {
|
||||||
|
LOG.info("Unrecognised tag");
|
||||||
|
onError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
|
||||||
|
if (inPendingContactId == null) {
|
||||||
|
LOG.warning("Expected rendezvous tag, got contact tag");
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Allocate the outgoing stream context
|
||||||
|
StreamContext ctxOut;
|
||||||
|
try {
|
||||||
|
ctxOut = keyManager.getStreamContext(pendingContactId,
|
||||||
|
transportId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctxOut == null) {
|
||||||
|
LOG.warning("Could not allocate stream context");
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Close the connection if it's redundant
|
||||||
|
if (!connectionRegistry.registerConnection(pendingContactId)) {
|
||||||
|
LOG.info("Redundant rendezvous connection");
|
||||||
|
onError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Handshake and exchange contacts
|
||||||
|
try {
|
||||||
|
InputStream in = streamReaderFactory.createStreamReader(
|
||||||
|
reader.getInputStream(), ctxIn);
|
||||||
|
// Flush the output stream to send the outgoing stream header
|
||||||
|
StreamWriter out = streamWriterFactory.createStreamWriter(
|
||||||
|
writer.getOutputStream(), ctxOut);
|
||||||
|
out.getOutputStream().flush();
|
||||||
|
HandshakeResult result = handshakeManager.handshake(
|
||||||
|
pendingContactId, in, out);
|
||||||
|
Contact contact = contactExchangeManager.exchangeContacts(
|
||||||
|
pendingContactId, connection, result.getMasterKey(),
|
||||||
|
result.isAlice(), false);
|
||||||
|
connectionRegistry.unregisterConnection(pendingContactId, true);
|
||||||
|
// Reuse the connection as a transport connection
|
||||||
|
manageOutgoingConnection(contact.getId(), transportId,
|
||||||
|
connection);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError(true);
|
||||||
|
connectionRegistry.unregisterConnection(pendingContactId,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError(boolean recognised) {
|
||||||
|
disposeOnError(reader, recognised);
|
||||||
|
disposeOnError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManageOutgoingHandshakeConnection implements Runnable {
|
||||||
|
|
||||||
|
private final PendingContactId pendingContactId;
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final DuplexTransportConnection connection;
|
||||||
|
private final TransportConnectionReader reader;
|
||||||
|
private final TransportConnectionWriter writer;
|
||||||
|
|
||||||
|
private ManageOutgoingHandshakeConnection(
|
||||||
|
PendingContactId pendingContactId, TransportId transportId,
|
||||||
|
DuplexTransportConnection connection) {
|
||||||
|
this.pendingContactId = pendingContactId;
|
||||||
|
this.transportId = transportId;
|
||||||
|
this.connection = connection;
|
||||||
|
reader = connection.getReader();
|
||||||
|
writer = connection.getWriter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Allocate the outgoing stream context
|
||||||
|
StreamContext ctxOut;
|
||||||
|
try {
|
||||||
|
ctxOut = keyManager.getStreamContext(pendingContactId,
|
||||||
|
transportId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctxOut == null) {
|
||||||
|
LOG.warning("Could not allocate stream context");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Flush the output stream to send the outgoing stream header
|
||||||
|
StreamWriter out;
|
||||||
|
try {
|
||||||
|
out = streamWriterFactory.createStreamWriter(
|
||||||
|
writer.getOutputStream(), ctxOut);
|
||||||
|
out.getOutputStream().flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Read and recognise the tag
|
||||||
|
StreamContext ctxIn;
|
||||||
|
try {
|
||||||
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
|
ctxIn = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Unrecognised tags are suspicious in this case
|
||||||
|
if (ctxIn == null) {
|
||||||
|
LOG.warning("Unrecognised tag for returning stream");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check that the stream comes from the expected pending contact
|
||||||
|
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
|
||||||
|
if (inPendingContactId == null) {
|
||||||
|
LOG.warning("Expected rendezvous tag, got contact tag");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!inPendingContactId.equals(pendingContactId)) {
|
||||||
|
LOG.warning("Wrong pending contact ID for returning stream");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Close the connection if it's redundant
|
||||||
|
if (!connectionRegistry.registerConnection(pendingContactId)) {
|
||||||
|
LOG.info("Redundant rendezvous connection");
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Handshake and exchange contacts
|
||||||
|
try {
|
||||||
|
InputStream in = streamReaderFactory.createStreamReader(
|
||||||
|
reader.getInputStream(), ctxIn);
|
||||||
|
HandshakeResult result = handshakeManager.handshake(
|
||||||
|
pendingContactId, in, out);
|
||||||
|
Contact contact = contactExchangeManager.exchangeContacts(
|
||||||
|
pendingContactId, connection, result.getMasterKey(),
|
||||||
|
result.isAlice(), false);
|
||||||
|
connectionRegistry.unregisterConnection(pendingContactId, true);
|
||||||
|
// Reuse the connection as a transport connection
|
||||||
|
manageOutgoingConnection(contact.getId(), transportId,
|
||||||
|
connection);
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
connectionRegistry.unregisterConnection(pendingContactId,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError() {
|
||||||
|
// 'Recognised' is always true for outgoing connections
|
||||||
|
disposeOnError(reader, true);
|
||||||
|
disposeOnError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Multiset;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
|
||||||
|
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.system.Scheduler;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.DISCONNECTED;
|
||||||
|
import static org.briarproject.bramble.api.plugin.ConnectionStatus.RECENTLY_CONNECTED;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
class ConnectionRegistryImpl implements ConnectionRegistry {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(ConnectionRegistryImpl.class.getName());
|
||||||
|
|
||||||
|
private static final long RECENTLY_CONNECTED_MS = MINUTES.toMillis(1);
|
||||||
|
private static final long EXPIRY_INTERVAL_MS = SECONDS.toMillis(10);
|
||||||
|
|
||||||
|
private final EventBus eventBus;
|
||||||
|
private final Clock clock;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final Map<TransportId, Multiset<ContactId>> contactConnections;
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final Map<ContactId, Counter> contactCounts;
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final Set<PendingContactId> connectedPendingContacts;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ConnectionRegistryImpl(EventBus eventBus, Clock clock,
|
||||||
|
@Scheduler ScheduledExecutorService scheduler) {
|
||||||
|
this.eventBus = eventBus;
|
||||||
|
this.clock = clock;
|
||||||
|
contactConnections = new HashMap<>();
|
||||||
|
contactCounts = new HashMap<>();
|
||||||
|
connectedPendingContacts = new HashSet<>();
|
||||||
|
scheduler.scheduleWithFixedDelay(this::expireRecentConnections,
|
||||||
|
EXPIRY_INTERVAL_MS, EXPIRY_INTERVAL_MS, MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerConnection(ContactId c, TransportId t,
|
||||||
|
boolean incoming) {
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
if (incoming) LOG.info("Incoming connection registered: " + t);
|
||||||
|
else LOG.info("Outgoing connection registered: " + t);
|
||||||
|
}
|
||||||
|
boolean firstConnection = false;
|
||||||
|
synchronized (lock) {
|
||||||
|
Multiset<ContactId> m = contactConnections.get(t);
|
||||||
|
if (m == null) {
|
||||||
|
m = new Multiset<>();
|
||||||
|
contactConnections.put(t, m);
|
||||||
|
}
|
||||||
|
m.add(c);
|
||||||
|
|
||||||
|
Counter counter = contactCounts.get(c);
|
||||||
|
if (counter == null) {
|
||||||
|
counter = new Counter();
|
||||||
|
contactCounts.put(c, counter);
|
||||||
|
}
|
||||||
|
if (counter.connections == 0) {
|
||||||
|
counter.disconnectedTime = 0;
|
||||||
|
firstConnection = true;
|
||||||
|
}
|
||||||
|
counter.connections++;
|
||||||
|
}
|
||||||
|
eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming));
|
||||||
|
if (firstConnection) {
|
||||||
|
LOG.info("Contact connected");
|
||||||
|
eventBus.broadcast(new ConnectionStatusChangedEvent(c, CONNECTED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterConnection(ContactId c, TransportId t,
|
||||||
|
boolean incoming) {
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
if (incoming) LOG.info("Incoming connection unregistered: " + t);
|
||||||
|
else LOG.info("Outgoing connection unregistered: " + t);
|
||||||
|
}
|
||||||
|
boolean lastConnection = false;
|
||||||
|
synchronized (lock) {
|
||||||
|
Multiset<ContactId> m = contactConnections.get(t);
|
||||||
|
if (m == null || !m.contains(c))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
m.remove(c);
|
||||||
|
|
||||||
|
Counter counter = contactCounts.get(c);
|
||||||
|
if (counter == null || counter.connections == 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
counter.connections--;
|
||||||
|
if (counter.connections == 0) {
|
||||||
|
counter.disconnectedTime = clock.currentTimeMillis();
|
||||||
|
lastConnection = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventBus.broadcast(new ConnectionClosedEvent(c, t, incoming));
|
||||||
|
if (lastConnection) {
|
||||||
|
LOG.info("Contact disconnected");
|
||||||
|
eventBus.broadcast(
|
||||||
|
new ConnectionStatusChangedEvent(c, RECENTLY_CONNECTED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ContactId> getConnectedContacts(TransportId t) {
|
||||||
|
synchronized (lock) {
|
||||||
|
Multiset<ContactId> m = contactConnections.get(t);
|
||||||
|
if (m == null) return emptyList();
|
||||||
|
List<ContactId> ids = new ArrayList<>(m.keySet());
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info(ids.size() + " contacts connected: " + t);
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnected(ContactId c, TransportId t) {
|
||||||
|
synchronized (lock) {
|
||||||
|
Multiset<ContactId> m = contactConnections.get(t);
|
||||||
|
return m != null && m.contains(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionStatus getConnectionStatus(ContactId c) {
|
||||||
|
synchronized (lock) {
|
||||||
|
Counter counter = contactCounts.get(c);
|
||||||
|
if (counter == null) return DISCONNECTED;
|
||||||
|
return counter.connections > 0 ? CONNECTED : RECENTLY_CONNECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean registerConnection(PendingContactId p) {
|
||||||
|
boolean added;
|
||||||
|
synchronized (lock) {
|
||||||
|
added = connectedPendingContacts.add(p);
|
||||||
|
}
|
||||||
|
if (added) eventBus.broadcast(new RendezvousConnectionOpenedEvent(p));
|
||||||
|
return added;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterConnection(PendingContactId p, boolean success) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!connectedPendingContacts.remove(p))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduler
|
||||||
|
private void expireRecentConnections() {
|
||||||
|
long now = clock.currentTimeMillis();
|
||||||
|
List<ContactId> disconnected = new ArrayList<>();
|
||||||
|
synchronized (lock) {
|
||||||
|
Iterator<Entry<ContactId, Counter>> it =
|
||||||
|
contactCounts.entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Entry<ContactId, Counter> e = it.next();
|
||||||
|
if (e.getValue().isExpired(now)) {
|
||||||
|
disconnected.add(e.getKey());
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ContactId c : disconnected) {
|
||||||
|
eventBus.broadcast(
|
||||||
|
new ConnectionStatusChangedEvent(c, DISCONNECTED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Counter {
|
||||||
|
|
||||||
|
private int connections = 0;
|
||||||
|
private long disconnectedTime = 0;
|
||||||
|
|
||||||
|
private boolean isExpired(long now) {
|
||||||
|
return connections == 0 &&
|
||||||
|
now - disconnectedTime > RECENTLY_CONNECTED_MS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
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;
|
||||||
import org.briarproject.bramble.api.lifecycle.Service;
|
import org.briarproject.bramble.api.lifecycle.Service;
|
||||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin.State;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
import org.briarproject.bramble.api.plugin.PluginException;
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
@@ -20,16 +18,14 @@ 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.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||||
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.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;
|
||||||
@@ -40,21 +36,15 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import 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.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.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;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
@@ -66,7 +56,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 +69,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 +101,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 +117,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 +129,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
|
||||||
@@ -195,27 +177,7 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
return supported;
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class PluginStarter implements Runnable {
|
||||||
public void setPluginEnabled(TransportId t, boolean enabled) {
|
|
||||||
Plugin plugin = plugins.get(t);
|
|
||||||
if (plugin == null) return;
|
|
||||||
|
|
||||||
Settings s = new Settings();
|
|
||||||
s.putBoolean(PREF_PLUGIN_ENABLE, enabled);
|
|
||||||
ioExecutor.execute(() -> mergeSettings(s, t.getString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void mergeSettings(Settings s, String namespace) {
|
|
||||||
try {
|
|
||||||
long start = now();
|
|
||||||
settingsManager.mergeSettings(s, namespace);
|
|
||||||
logDuration(LOG, "Merging settings", start);
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PluginStarter implements Runnable {
|
|
||||||
|
|
||||||
private final Plugin plugin;
|
private final Plugin plugin;
|
||||||
private final CountDownLatch startLatch;
|
private final CountDownLatch startLatch;
|
||||||
@@ -245,7 +207,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;
|
||||||
@@ -288,8 +250,7 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
private class Callback implements PluginCallback {
|
private class Callback implements PluginCallback {
|
||||||
|
|
||||||
private final TransportId id;
|
private final TransportId id;
|
||||||
private final AtomicReference<State> state =
|
private final AtomicBoolean enabled = new AtomicBoolean(false);
|
||||||
new AtomicReference<>(STARTING_STOPPING);
|
|
||||||
|
|
||||||
private Callback(TransportId id) {
|
private Callback(TransportId id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -316,22 +277,14 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<TransportProperties> getRemoteProperties() {
|
public void mergeSettings(Settings s) {
|
||||||
try {
|
try {
|
||||||
Map<ContactId, TransportProperties> remote =
|
settingsManager.mergeSettings(s, id.getString());
|
||||||
transportPropertyManager.getRemoteProperties(id);
|
|
||||||
return remote.values();
|
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
return emptyList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mergeSettings(Settings s) {
|
|
||||||
PluginManagerImpl.this.mergeSettings(s, id.getString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mergeLocalProperties(TransportProperties p) {
|
public void mergeLocalProperties(TransportProperties p) {
|
||||||
try {
|
try {
|
||||||
@@ -342,24 +295,15 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pluginStateChanged(State newState) {
|
public void transportEnabled() {
|
||||||
State oldState = state.getAndSet(newState);
|
if (!enabled.getAndSet(true))
|
||||||
if (newState != oldState) {
|
eventBus.broadcast(new TransportEnabledEvent(id));
|
||||||
if (LOG.isLoggable(INFO)) {
|
}
|
||||||
LOG.info(id + " changed from state " + oldState
|
|
||||||
+ " to " + newState);
|
@Override
|
||||||
}
|
public void transportDisabled() {
|
||||||
eventBus.broadcast(new TransportStateEvent(id, newState));
|
if (enabled.getAndSet(false))
|
||||||
if (newState == ACTIVE) {
|
eventBus.broadcast(new TransportDisabledEvent(id));
|
||||||
eventBus.broadcast(new TransportActiveEvent(id));
|
|
||||||
} else if (oldState == ACTIVE) {
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package org.briarproject.bramble.plugin;
|
|||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
|
|
||||||
@@ -27,6 +29,20 @@ public class PluginModule {
|
|||||||
return new BackoffFactoryImpl();
|
return new BackoffFactoryImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ConnectionManager provideConnectionManager(
|
||||||
|
ConnectionManagerImpl connectionManager) {
|
||||||
|
return connectionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ConnectionRegistry provideConnectionRegistry(
|
||||||
|
ConnectionRegistryImpl connectionRegistry) {
|
||||||
|
return connectionRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
PluginManager providePluginManager(LifecycleManager lifecycleManager,
|
PluginManager providePluginManager(LifecycleManager lifecycleManager,
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Pair;
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -11,6 +9,8 @@ import org.briarproject.bramble.api.event.EventListener;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
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.ConnectionHandler;
|
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
@@ -20,16 +20,13 @@ 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.ConnectionClosedEvent;
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.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;
|
||||||
@@ -103,21 +98,21 @@ class PollerImpl implements Poller, EventListener {
|
|||||||
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
|
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
|
||||||
// Reschedule polling, the polling interval may have decreased
|
// Reschedule polling, the polling interval may have decreased
|
||||||
reschedule(c.getTransportId());
|
reschedule(c.getTransportId());
|
||||||
// If an outgoing connection failed, try to reconnect
|
if (!c.isIncoming()) {
|
||||||
if (!c.isIncoming() && c.isException()) {
|
// Connect to the disconnected contact
|
||||||
connectToContact(c.getContactId(), c.getTransportId());
|
connectToContact(c.getContactId(), c.getTransportId());
|
||||||
}
|
}
|
||||||
} else if (e instanceof ConnectionOpenedEvent) {
|
} else if (e instanceof ConnectionOpenedEvent) {
|
||||||
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
|
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
|
||||||
// Reschedule polling, the polling interval may have decreased
|
// Reschedule polling, the polling interval may have decreased
|
||||||
reschedule(c.getTransportId());
|
reschedule(c.getTransportId());
|
||||||
} else if (e instanceof TransportActiveEvent) {
|
} else if (e instanceof TransportEnabledEvent) {
|
||||||
TransportActiveEvent t = (TransportActiveEvent) e;
|
TransportEnabledEvent t = (TransportEnabledEvent) e;
|
||||||
// Poll the newly activated transport
|
// Poll the newly enabled transport
|
||||||
pollNow(t.getTransportId());
|
pollNow(t.getTransportId());
|
||||||
} else if (e instanceof TransportInactiveEvent) {
|
} else if (e instanceof TransportDisabledEvent) {
|
||||||
TransportInactiveEvent t = (TransportInactiveEvent) e;
|
TransportDisabledEvent t = (TransportDisabledEvent) e;
|
||||||
// Cancel polling for the deactivated transport
|
// Cancel polling for the disabled transport
|
||||||
cancel(t.getTransportId());
|
cancel(t.getTransportId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
@@ -220,7 +215,7 @@ class PollerImpl implements Poller, EventListener {
|
|||||||
Map<ContactId, TransportProperties> remote =
|
Map<ContactId, TransportProperties> remote =
|
||||||
transportPropertyManager.getRemoteProperties(t);
|
transportPropertyManager.getRemoteProperties(t);
|
||||||
Collection<ContactId> connected =
|
Collection<ContactId> connected =
|
||||||
connectionRegistry.getConnectedOrBetterContacts(t);
|
connectionRegistry.getConnectedContacts(t);
|
||||||
Collection<Pair<TransportProperties, ConnectionHandler>>
|
Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||||
properties = new ArrayList<>();
|
properties = new ArrayList<>();
|
||||||
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -23,9 +23,17 @@ interface BluetoothConnectionLimiter {
|
|||||||
boolean canOpenContactConnection();
|
boolean canOpenContactConnection();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informs the limiter that the given connection has been opened.
|
* Informs the limiter that a contact connection has been opened. The
|
||||||
|
* limiter may close the new connection if key agreement is in progress.
|
||||||
|
* <p/>
|
||||||
|
* Returns false if the limiter has closed the new connection.
|
||||||
*/
|
*/
|
||||||
void connectionOpened(DuplexTransportConnection conn);
|
boolean contactConnectionOpened(DuplexTransportConnection conn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Informs the limiter that a key agreement connection has been opened.
|
||||||
|
*/
|
||||||
|
void keyAgreementConnectionOpened(DuplexTransportConnection conn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informs the limiter that the given connection has been closed.
|
* Informs the limiter that the given connection has been closed.
|
||||||
|
|||||||
@@ -1,48 +1,46 @@
|
|||||||
package org.briarproject.bramble.plugin.bluetooth;
|
package org.briarproject.bramble.plugin.bluetooth;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.sync.event.CloseSyncConnectionsEvent;
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
|
class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(BluetoothConnectionLimiterImpl.class.getName());
|
Logger.getLogger(BluetoothConnectionLimiterImpl.class.getName());
|
||||||
|
|
||||||
private final EventBus eventBus;
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
@GuardedBy("lock")
|
// The following are locking: lock
|
||||||
private final List<DuplexTransportConnection> connections =
|
private final LinkedList<DuplexTransportConnection> connections =
|
||||||
new LinkedList<>();
|
new LinkedList<>();
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean keyAgreementInProgress = false;
|
private boolean keyAgreementInProgress = false;
|
||||||
|
|
||||||
BluetoothConnectionLimiterImpl(EventBus eventBus) {
|
|
||||||
this.eventBus = eventBus;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void keyAgreementStarted() {
|
public void keyAgreementStarted() {
|
||||||
|
List<DuplexTransportConnection> close;
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
keyAgreementInProgress = true;
|
keyAgreementInProgress = true;
|
||||||
|
close = new ArrayList<>(connections);
|
||||||
|
connections.clear();
|
||||||
}
|
}
|
||||||
LOG.info("Key agreement started");
|
if (LOG.isLoggable(INFO)) {
|
||||||
eventBus.broadcast(new CloseSyncConnectionsEvent(ID));
|
LOG.info("Key agreement started, closing " + close.size() +
|
||||||
|
" connections");
|
||||||
|
}
|
||||||
|
for (DuplexTransportConnection conn : close) tryToClose(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,22 +65,44 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connectionOpened(DuplexTransportConnection conn) {
|
public boolean contactConnectionOpened(DuplexTransportConnection conn) {
|
||||||
|
boolean accept = true;
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
connections.add(conn);
|
if (keyAgreementInProgress) {
|
||||||
if (LOG.isLoggable(INFO)) {
|
LOG.info("Refusing contact connection during key agreement");
|
||||||
LOG.info("Connection opened, " + connections.size() + " open");
|
accept = false;
|
||||||
|
} else {
|
||||||
|
LOG.info("Accepting contact connection");
|
||||||
|
connections.add(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!accept) tryToClose(conn);
|
||||||
|
return accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyAgreementConnectionOpened(DuplexTransportConnection conn) {
|
||||||
|
synchronized (lock) {
|
||||||
|
LOG.info("Accepting key agreement connection");
|
||||||
|
connections.add(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryToClose(DuplexTransportConnection conn) {
|
||||||
|
try {
|
||||||
|
conn.getWriter().dispose(false);
|
||||||
|
conn.getReader().dispose(false, false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connectionClosed(DuplexTransportConnection conn) {
|
public void connectionClosed(DuplexTransportConnection conn) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
connections.remove(conn);
|
connections.remove(conn);
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Connection closed, " + connections.size() + " open");
|
LOG.info("Connection closed, " + connections.size() + " open");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user