mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
1 Commits
remove-off
...
recently-o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1cda4fb1e |
8
.idea/codeStyles/Project.xml
generated
8
.idea/codeStyles/Project.xml
generated
@@ -1,5 +1,8 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<AndroidXmlCodeStyleSettings>
|
||||
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
|
||||
</AndroidXmlCodeStyleSettings>
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
@@ -28,11 +31,6 @@
|
||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||
</JavaCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value />
|
||||
</option>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<XML>
|
||||
|
||||
@@ -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
|
||||
1
bramble-android/.gitignore
vendored
1
bramble-android/.gitignore
vendored
@@ -3,4 +3,3 @@ gen
|
||||
build
|
||||
.settings
|
||||
src/main/res/raw/*.zip
|
||||
src/main/jniLibs
|
||||
@@ -5,14 +5,14 @@ apply plugin: 'witness'
|
||||
apply from: 'witness.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion '29.0.2'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode rootProject.ext.versionCode
|
||||
versionName rootProject.ext.versionName
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 28
|
||||
versionCode 10207
|
||||
versionName "1.2.7"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
@@ -53,12 +53,10 @@ dependencies {
|
||||
}
|
||||
|
||||
def torBinariesDir = 'src/main/res/raw'
|
||||
def torLibsDir = 'src/main/jniLibs'
|
||||
|
||||
task cleanTorBinaries {
|
||||
doLast {
|
||||
delete fileTree(torBinariesDir) { include '*.zip' }
|
||||
delete fileTree(torLibsDir) { include '**/*.so' }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,36 +67,8 @@ task unpackTorBinaries {
|
||||
copy {
|
||||
from configurations.tor.collect { zipTree(it) }
|
||||
into torBinariesDir
|
||||
include 'geoip.zip'
|
||||
}
|
||||
configurations.tor.each { outer ->
|
||||
zipTree(outer).each { inner ->
|
||||
if (inner.name.endsWith('_arm_pie.zip')) {
|
||||
copy {
|
||||
from zipTree(inner)
|
||||
into torLibsDir
|
||||
rename '(.*)', 'armeabi-v7a/lib$1.so'
|
||||
}
|
||||
} else if (inner.name.endsWith('_arm64_pie.zip')) {
|
||||
copy {
|
||||
from zipTree(inner)
|
||||
into torLibsDir
|
||||
rename '(.*)', 'arm64-v8a/lib$1.so'
|
||||
}
|
||||
} else if (inner.name.endsWith('_x86_pie.zip')) {
|
||||
copy {
|
||||
from zipTree(inner)
|
||||
into torLibsDir
|
||||
rename '(.*)', 'x86/lib$1.so'
|
||||
}
|
||||
} else if (inner.name.endsWith('_x86_64_pie.zip')) {
|
||||
copy {
|
||||
from zipTree(inner)
|
||||
into torLibsDir
|
||||
rename '(.*)', 'x86_64/lib$1.so'
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Remove after next Tor upgrade, which won't include non-PIE binaries
|
||||
include 'geoip.zip', '*_pie.zip'
|
||||
}
|
||||
}
|
||||
dependsOn cleanTorBinaries
|
||||
@@ -106,6 +76,5 @@ task unpackTorBinaries {
|
||||
|
||||
tasks.withType(MergeResources) {
|
||||
inputs.dir torBinariesDir
|
||||
inputs.dir torLibsDir
|
||||
dependsOn unpackTorBinaries
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true">
|
||||
|
||||
<receiver android:name=".system.AlarmReceiver" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -6,8 +6,6 @@ import org.briarproject.bramble.plugin.tor.CircumventionModule;
|
||||
import org.briarproject.bramble.reporting.ReportingModule;
|
||||
import org.briarproject.bramble.socks.SocksModule;
|
||||
import org.briarproject.bramble.system.AndroidSystemModule;
|
||||
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
|
||||
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
|
||||
|
||||
import dagger.Module;
|
||||
|
||||
@@ -15,8 +13,6 @@ import dagger.Module;
|
||||
AndroidBatteryModule.class,
|
||||
AndroidNetworkModule.class,
|
||||
AndroidSystemModule.class,
|
||||
AndroidTaskSchedulerModule.class,
|
||||
AndroidWakefulIoExecutorModule.class,
|
||||
CircumventionModule.class,
|
||||
ReportingModule.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 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.network.NetworkManager;
|
||||
import org.briarproject.bramble.api.network.NetworkStatus;
|
||||
import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
|
||||
import org.briarproject.bramble.api.system.Scheduler;
|
||||
|
||||
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.concurrent.atomic.AtomicReference;
|
||||
@@ -51,22 +50,20 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
||||
private static final String WIFI_AP_STATE_CHANGED_ACTION =
|
||||
"android.net.wifi.WIFI_AP_STATE_CHANGED";
|
||||
|
||||
private final TaskScheduler scheduler;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final EventBus eventBus;
|
||||
private final Executor eventExecutor;
|
||||
private final Context appContext;
|
||||
private final AtomicReference<Cancellable> connectivityCheck =
|
||||
private final AtomicReference<Future<?>> connectivityCheck =
|
||||
new AtomicReference<>();
|
||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||
|
||||
private volatile BroadcastReceiver networkStateReceiver = null;
|
||||
|
||||
@Inject
|
||||
AndroidNetworkManager(TaskScheduler scheduler, EventBus eventBus,
|
||||
@EventExecutor Executor eventExecutor, Application app) {
|
||||
AndroidNetworkManager(@Scheduler ScheduledExecutorService scheduler,
|
||||
EventBus eventBus, Application app) {
|
||||
this.scheduler = scheduler;
|
||||
this.eventBus = eventBus;
|
||||
this.eventExecutor = eventExecutor;
|
||||
this.appContext = app.getApplicationContext();
|
||||
}
|
||||
|
||||
@@ -107,12 +104,11 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
||||
}
|
||||
|
||||
private void scheduleConnectionStatusUpdate(int delay, TimeUnit unit) {
|
||||
Cancellable newConnectivityCheck =
|
||||
scheduler.schedule(this::updateConnectionStatus, eventExecutor,
|
||||
delay, unit);
|
||||
Cancellable oldConnectivityCheck =
|
||||
Future<?> newConnectivityCheck =
|
||||
scheduler.schedule(this::updateConnectionStatus, delay, unit);
|
||||
Future<?> oldConnectivityCheck =
|
||||
connectivityCheck.getAndSet(newConnectivityCheck);
|
||||
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel();
|
||||
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(false);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import android.app.Application;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothServerSocket;
|
||||
@@ -59,8 +58,7 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
class AndroidBluetoothPlugin
|
||||
extends BluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
|
||||
class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(AndroidBluetoothPlugin.class.getName());
|
||||
@@ -68,31 +66,23 @@ class AndroidBluetoothPlugin
|
||||
private static final int MAX_DISCOVERY_MS = 10_000;
|
||||
|
||||
private final AndroidExecutor androidExecutor;
|
||||
private final Application app;
|
||||
private final Context appContext;
|
||||
private final Clock clock;
|
||||
|
||||
private volatile boolean wasEnabledByUs = false;
|
||||
private volatile BluetoothStateReceiver receiver = null;
|
||||
|
||||
// Non-null if the plugin started successfully
|
||||
private volatile BluetoothAdapter adapter = null;
|
||||
|
||||
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
||||
BluetoothConnectionFactory<BluetoothSocket> connectionFactory,
|
||||
Executor ioExecutor,
|
||||
Executor wakefulIoExecutor,
|
||||
SecureRandom secureRandom,
|
||||
AndroidExecutor androidExecutor,
|
||||
Application app,
|
||||
Clock clock,
|
||||
Backoff backoff,
|
||||
PluginCallback callback,
|
||||
int maxLatency,
|
||||
int maxIdleTime) {
|
||||
super(connectionLimiter, connectionFactory, ioExecutor,
|
||||
wakefulIoExecutor, secureRandom, backoff, callback,
|
||||
maxLatency, maxIdleTime);
|
||||
Executor ioExecutor, AndroidExecutor androidExecutor,
|
||||
Context appContext, SecureRandom secureRandom, Clock clock,
|
||||
Backoff backoff, PluginCallback callback, int maxLatency) {
|
||||
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
|
||||
maxLatency);
|
||||
this.androidExecutor = androidExecutor;
|
||||
this.app = app;
|
||||
this.appContext = appContext;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@@ -104,13 +94,13 @@ class AndroidBluetoothPlugin
|
||||
filter.addAction(ACTION_STATE_CHANGED);
|
||||
filter.addAction(ACTION_SCAN_MODE_CHANGED);
|
||||
receiver = new BluetoothStateReceiver();
|
||||
app.registerReceiver(receiver, filter);
|
||||
appContext.registerReceiver(receiver, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
if (receiver != null) app.unregisterReceiver(receiver);
|
||||
if (receiver != null) appContext.unregisterReceiver(receiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,11 +122,36 @@ class AndroidBluetoothPlugin
|
||||
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
|
||||
@Nullable
|
||||
String getBluetoothAddress() {
|
||||
if (adapter == null) return null;
|
||||
String address = AndroidUtils.getBluetoothAddress(app, adapter);
|
||||
String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
|
||||
return address.isEmpty() ? null : address;
|
||||
}
|
||||
|
||||
@@ -154,7 +169,12 @@ class AndroidBluetoothPlugin
|
||||
@Override
|
||||
DuplexTransportConnection acceptConnection(BluetoothServerSocket ss)
|
||||
throws IOException {
|
||||
return connectionFactory.wrapSocket(this, ss.accept());
|
||||
return wrapSocket(ss.accept());
|
||||
}
|
||||
|
||||
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
|
||||
return new AndroidBluetoothTransportConnection(this,
|
||||
connectionLimiter, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -171,7 +191,7 @@ class AndroidBluetoothPlugin
|
||||
try {
|
||||
s = d.createInsecureRfcommSocketToServiceRecord(u);
|
||||
s.connect();
|
||||
return connectionFactory.wrapSocket(this, s);
|
||||
return wrapSocket(s);
|
||||
} catch (IOException e) {
|
||||
IoUtils.tryToClose(s, LOG, WARNING);
|
||||
throw e;
|
||||
@@ -206,7 +226,7 @@ class AndroidBluetoothPlugin
|
||||
filter.addAction(ACTION_DISCOVERY_STARTED);
|
||||
filter.addAction(ACTION_DISCOVERY_FINISHED);
|
||||
filter.addAction(ACTION_FOUND);
|
||||
app.registerReceiver(receiver, filter);
|
||||
appContext.registerReceiver(receiver, filter);
|
||||
try {
|
||||
if (adapter.startDiscovery()) {
|
||||
long now = clock.currentTimeMillis();
|
||||
@@ -243,7 +263,7 @@ class AndroidBluetoothPlugin
|
||||
} finally {
|
||||
LOG.info("Cancelling discovery");
|
||||
adapter.cancelDiscovery();
|
||||
app.unregisterReceiver(receiver);
|
||||
appContext.unregisterReceiver(receiver);
|
||||
}
|
||||
// Shuffle the addresses so we don't always try the same one first
|
||||
shuffle(addresses);
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package org.briarproject.bramble.plugin.bluetooth;
|
||||
|
||||
import android.app.Application;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.content.Context;
|
||||
|
||||
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.plugin.Backoff;
|
||||
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.DuplexPluginFactory;
|
||||
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.WakefulIoExecutor;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
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 {
|
||||
|
||||
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 MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
||||
private static final double BACKOFF_BASE = 1.2;
|
||||
|
||||
private final Executor ioExecutor, wakefulIoExecutor;
|
||||
private final Executor ioExecutor;
|
||||
private final AndroidExecutor androidExecutor;
|
||||
private final AndroidWakeLockManager wakeLockManager;
|
||||
private final Application app;
|
||||
private final Context appContext;
|
||||
private final SecureRandom secureRandom;
|
||||
private final EventBus eventBus;
|
||||
private final Clock clock;
|
||||
private final TimeoutMonitor timeoutMonitor;
|
||||
private final BackoffFactory backoffFactory;
|
||||
|
||||
@Inject
|
||||
public AndroidBluetoothPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||
AndroidExecutor androidExecutor,
|
||||
AndroidWakeLockManager wakeLockManager,
|
||||
Application app,
|
||||
SecureRandom secureRandom,
|
||||
EventBus eventBus,
|
||||
Clock clock,
|
||||
TimeoutMonitor timeoutMonitor,
|
||||
public AndroidBluetoothPluginFactory(Executor ioExecutor,
|
||||
AndroidExecutor androidExecutor, Context appContext,
|
||||
SecureRandom secureRandom, EventBus eventBus, Clock clock,
|
||||
BackoffFactory backoffFactory) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.wakefulIoExecutor = wakefulIoExecutor;
|
||||
this.androidExecutor = androidExecutor;
|
||||
this.wakeLockManager = wakeLockManager;
|
||||
this.app = app;
|
||||
this.appContext = appContext;
|
||||
this.secureRandom = secureRandom;
|
||||
this.eventBus = eventBus;
|
||||
this.clock = clock;
|
||||
this.timeoutMonitor = timeoutMonitor;
|
||||
this.backoffFactory = backoffFactory;
|
||||
}
|
||||
|
||||
@@ -82,16 +63,12 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
||||
@Override
|
||||
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||
BluetoothConnectionLimiter connectionLimiter =
|
||||
new BluetoothConnectionLimiterImpl(eventBus);
|
||||
BluetoothConnectionFactory<BluetoothSocket> connectionFactory =
|
||||
new AndroidBluetoothConnectionFactory(connectionLimiter,
|
||||
wakeLockManager, timeoutMonitor);
|
||||
new BluetoothConnectionLimiterImpl();
|
||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
||||
connectionLimiter, connectionFactory, ioExecutor,
|
||||
wakefulIoExecutor, secureRandom, androidExecutor, app,
|
||||
clock, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
|
||||
connectionLimiter, ioExecutor, androidExecutor, appContext,
|
||||
secureRandom, clock, backoff, callback, MAX_LATENCY);
|
||||
eventBus.addListener(plugin);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,9 @@ 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.Plugin;
|
||||
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.InputStream;
|
||||
@@ -20,30 +17,22 @@ import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress
|
||||
class AndroidBluetoothTransportConnection
|
||||
extends AbstractDuplexTransportConnection {
|
||||
|
||||
private final BluetoothConnectionLimiter connectionLimiter;
|
||||
private final BluetoothConnectionLimiter connectionManager;
|
||||
private final BluetoothSocket socket;
|
||||
private final InputStream in;
|
||||
private final AndroidWakeLock wakeLock;
|
||||
|
||||
AndroidBluetoothTransportConnection(Plugin plugin,
|
||||
BluetoothConnectionLimiter connectionLimiter,
|
||||
AndroidWakeLockManager wakeLockManager,
|
||||
TimeoutMonitor timeoutMonitor,
|
||||
BluetoothSocket socket) throws IOException {
|
||||
BluetoothConnectionLimiter connectionManager,
|
||||
BluetoothSocket socket) {
|
||||
super(plugin);
|
||||
this.connectionLimiter = connectionLimiter;
|
||||
this.connectionManager = connectionManager;
|
||||
this.socket = socket;
|
||||
in = timeoutMonitor.createTimeoutInputStream(
|
||||
socket.getInputStream(), plugin.getMaxIdleTime() * 2);
|
||||
wakeLock = wakeLockManager.createWakeLock("BluetoothConnection");
|
||||
wakeLock.acquire();
|
||||
String address = socket.getRemoteDevice().getAddress();
|
||||
if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream getInputStream() {
|
||||
return in;
|
||||
protected InputStream getInputStream() throws IOException {
|
||||
return socket.getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,10 +44,8 @@ class AndroidBluetoothTransportConnection
|
||||
protected void closeConnection(boolean exception) throws IOException {
|
||||
try {
|
||||
socket.close();
|
||||
in.close();
|
||||
} finally {
|
||||
wakeLock.release();
|
||||
connectionLimiter.connectionClosed(this);
|
||||
connectionManager.connectionClosed(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
package org.briarproject.bramble.plugin.tcp;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
|
||||
import org.briarproject.bramble.PoliteExecutor;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
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.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.Backoff;
|
||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.List;
|
||||
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.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 java.util.Collections.emptyList;
|
||||
import static java.util.Collections.list;
|
||||
import static java.util.Collections.singletonList;
|
||||
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.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
|
||||
class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(AndroidLanTcpPlugin.class.getName());
|
||||
@@ -62,22 +47,20 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
|
||||
private volatile SocketFactory socketFactory;
|
||||
|
||||
AndroidLanTcpPlugin(Executor ioExecutor,
|
||||
Executor wakefulIoExecutor,
|
||||
Application app,
|
||||
Backoff backoff,
|
||||
PluginCallback callback,
|
||||
int maxLatency,
|
||||
int maxIdleTime,
|
||||
int connectionTimeout) {
|
||||
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,
|
||||
maxIdleTime, connectionTimeout);
|
||||
AndroidLanTcpPlugin(Executor ioExecutor, Context appContext,
|
||||
Backoff backoff, PluginCallback callback, int maxLatency,
|
||||
int maxIdleTime, int connectionTimeout) {
|
||||
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime,
|
||||
connectionTimeout);
|
||||
// Don't execute more than one connection status check at a time
|
||||
connectionStatusExecutor =
|
||||
new PoliteExecutor("AndroidLanTcpPlugin", ioExecutor, 1);
|
||||
connectivityManager = (ConnectivityManager)
|
||||
requireNonNull(app.getSystemService(CONNECTIVITY_SERVICE));
|
||||
wifiManager = (WifiManager) app.getSystemService(WIFI_SERVICE);
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager)
|
||||
appContext.getSystemService(CONNECTIVITY_SERVICE);
|
||||
if (connectivityManager == null) throw new AssertionError();
|
||||
this.connectivityManager = connectivityManager;
|
||||
wifiManager = (WifiManager) appContext.getApplicationContext()
|
||||
.getSystemService(WIFI_SERVICE);
|
||||
socketFactory = SocketFactory.getDefault();
|
||||
}
|
||||
|
||||
@@ -85,137 +68,37 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
public void start() {
|
||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||
initialisePortProperty();
|
||||
Settings settings = callback.getSettings();
|
||||
state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE,
|
||||
DEFAULT_PREF_PLUGIN_ENABLE));
|
||||
running = true;
|
||||
updateConnectionStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
running = false;
|
||||
tryToClose(socket);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Socket createSocket() throws IOException {
|
||||
return socketFactory.createSocket();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<InetAddress> getUsableLocalInetAddresses(boolean ipv4) {
|
||||
InetAddress addr = getWifiAddress(ipv4);
|
||||
return addr == null ? emptyList() : singletonList(addr);
|
||||
}
|
||||
|
||||
@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;
|
||||
protected List<InetAddress> getUsableLocalInetAddresses() {
|
||||
// If the device doesn't have wifi, don't open any sockets
|
||||
if (wifiManager == null) return emptyList();
|
||||
// If we're connected to a wifi network, return its address
|
||||
WifiInfo info = wifiManager.getConnectionInfo();
|
||||
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 providing a normal access point, return its address
|
||||
for (InterfaceAddress ifAddr : ifAddrs) {
|
||||
if (isAndroidWifiApAddress(ifAddr)) {
|
||||
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;
|
||||
// If we're running an access point, return its address
|
||||
for (InetAddress addr : getLocalInetAddresses()) {
|
||||
if (addr.equals(WIFI_AP_ADDRESS)) return singletonList(addr);
|
||||
if (addr.equals(WIFI_DIRECT_AP_ADDRESS)) return singletonList(addr);
|
||||
}
|
||||
// No suitable addresses
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
private InetAddress intToInetAddress(int ip) {
|
||||
@@ -237,11 +120,9 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
private SocketFactory getSocketFactory() {
|
||||
if (SDK_INT < 21) return SocketFactory.getDefault();
|
||||
for (Network net : connectivityManager.getAllNetworks()) {
|
||||
NetworkCapabilities caps =
|
||||
connectivityManager.getNetworkCapabilities(net);
|
||||
if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) {
|
||||
NetworkInfo info = connectivityManager.getNetworkInfo(net);
|
||||
if (info != null && info.getType() == TYPE_WIFI)
|
||||
return net.getSocketFactory();
|
||||
}
|
||||
}
|
||||
LOG.warning("Could not find suitable socket factory");
|
||||
return SocketFactory.getDefault();
|
||||
@@ -249,59 +130,31 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
super.eventOccurred(e);
|
||||
if (e instanceof NetworkStatusEvent) updateConnectionStatus();
|
||||
}
|
||||
|
||||
private void updateConnectionStatus() {
|
||||
connectionStatusExecutor.execute(() -> {
|
||||
State s = getState();
|
||||
if (s != ACTIVE && s != INACTIVE) return;
|
||||
Pair<InetAddress, Boolean> wifi = getPreferredWifiAddress();
|
||||
if (wifi == null) {
|
||||
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()) {
|
||||
if (!running) return;
|
||||
List<InetAddress> addrs = getUsableLocalInetAddresses();
|
||||
if (addrs.contains(WIFI_AP_ADDRESS)
|
||||
|| addrs.contains(WIFI_DIRECT_AP_ADDRESS)) {
|
||||
LOG.info("Providing wifi hotspot");
|
||||
// There's no corresponding Network object and thus no way
|
||||
// to get a suitable socket factory, so we won't be able to
|
||||
// make outgoing connections on API 21+ if another network
|
||||
// has internet access
|
||||
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 {
|
||||
LOG.info("Connected to wifi");
|
||||
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;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.Backoff;
|
||||
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.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
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 double BACKOFF_BASE = 1.2;
|
||||
|
||||
private final Executor ioExecutor, wakefulIoExecutor;
|
||||
private final Executor ioExecutor;
|
||||
private final EventBus eventBus;
|
||||
private final BackoffFactory backoffFactory;
|
||||
private final Application app;
|
||||
private final Context appContext;
|
||||
|
||||
@Inject
|
||||
public AndroidLanTcpPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||
EventBus eventBus,
|
||||
BackoffFactory backoffFactory,
|
||||
Application app) {
|
||||
public AndroidLanTcpPluginFactory(Executor ioExecutor, EventBus eventBus,
|
||||
BackoffFactory backoffFactory, Context appContext) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.wakefulIoExecutor = wakefulIoExecutor;
|
||||
this.eventBus = eventBus;
|
||||
this.backoffFactory = backoffFactory;
|
||||
this.app = app;
|
||||
this.appContext = appContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,8 +56,8 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
|
||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||
AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor,
|
||||
wakefulIoExecutor, app, backoff, callback,
|
||||
MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT);
|
||||
appContext, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME,
|
||||
CONNECTION_TIMEOUT);
|
||||
eventBus.addListener(plugin);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package org.briarproject.bramble.plugin.tor;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.PowerManager;
|
||||
|
||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||
import org.briarproject.bramble.api.network.NetworkManager;
|
||||
@@ -11,76 +12,48 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.Backoff;
|
||||
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.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.bramble.util.RenewableWakeLock;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
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
|
||||
@ParametersNotNullByDefault
|
||||
class AndroidTorPlugin extends TorPlugin {
|
||||
|
||||
private static final List<String> LIBRARY_ARCHITECTURES =
|
||||
asList("armeabi-v7a", "arm64-v8a", "x86", "x86_64");
|
||||
private final Context appContext;
|
||||
private final RenewableWakeLock wakeLock;
|
||||
|
||||
private static final String TOR_LIB_NAME = "libtor.so";
|
||||
private static final String OBFS4_LIB_NAME = "libobfs4proxy.so";
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(AndroidTorPlugin.class.getName());
|
||||
|
||||
private final Application app;
|
||||
private final AndroidWakeLock wakeLock;
|
||||
private final File torLib, obfs4Lib;
|
||||
|
||||
AndroidTorPlugin(Executor ioExecutor,
|
||||
Executor wakefulIoExecutor,
|
||||
Application app,
|
||||
NetworkManager networkManager,
|
||||
LocationUtils locationUtils,
|
||||
SocketFactory torSocketFactory,
|
||||
Clock clock,
|
||||
ResourceProvider resourceProvider,
|
||||
AndroidTorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
|
||||
Context appContext, NetworkManager networkManager,
|
||||
LocationUtils locationUtils, SocketFactory torSocketFactory,
|
||||
Clock clock, ResourceProvider resourceProvider,
|
||||
CircumventionProvider circumventionProvider,
|
||||
BatteryManager batteryManager,
|
||||
AndroidWakeLockManager wakeLockManager,
|
||||
Backoff backoff,
|
||||
BatteryManager batteryManager, Backoff backoff,
|
||||
TorRendezvousCrypto torRendezvousCrypto,
|
||||
PluginCallback callback,
|
||||
String architecture,
|
||||
int maxLatency,
|
||||
int maxIdleTime,
|
||||
File torDirectory) {
|
||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||
torSocketFactory, clock, resourceProvider,
|
||||
circumventionProvider, batteryManager, backoff,
|
||||
torRendezvousCrypto, callback, architecture, maxLatency,
|
||||
maxIdleTime, torDirectory);
|
||||
this.app = app;
|
||||
wakeLock = wakeLockManager.createWakeLock("TorPlugin");
|
||||
String nativeLibDir = app.getApplicationInfo().nativeLibraryDir;
|
||||
torLib = new File(nativeLibDir, TOR_LIB_NAME);
|
||||
obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME);
|
||||
PluginCallback callback, String architecture, int maxLatency,
|
||||
int maxIdleTime) {
|
||||
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
|
||||
clock, resourceProvider, circumventionProvider, batteryManager,
|
||||
backoff, torRendezvousCrypto, callback, architecture, maxLatency, maxIdleTime,
|
||||
appContext.getDir("tor", MODE_PRIVATE));
|
||||
this.appContext = appContext;
|
||||
PowerManager pm = (PowerManager)
|
||||
appContext.getSystemService(POWER_SERVICE);
|
||||
if (pm == null) throw new AssertionError();
|
||||
wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK,
|
||||
getWakeLockTag(), 1, MINUTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,8 +64,8 @@ class AndroidTorPlugin extends TorPlugin {
|
||||
@Override
|
||||
protected long getLastUpdateTime() {
|
||||
try {
|
||||
PackageManager pm = app.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(app.getPackageName(), 0);
|
||||
PackageManager pm = appContext.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(appContext.getPackageName(), 0);
|
||||
return pi.lastUpdateTime;
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new AssertionError(e);
|
||||
@@ -101,6 +74,7 @@ class AndroidTorPlugin extends TorPlugin {
|
||||
|
||||
@Override
|
||||
protected void enableNetwork(boolean enable) throws IOException {
|
||||
if (!running) return;
|
||||
if (enable) wakeLock.acquire();
|
||||
super.enableNetwork(enable);
|
||||
if (!enable) wakeLock.release();
|
||||
@@ -112,111 +86,16 @@ class AndroidTorPlugin extends TorPlugin {
|
||||
wakeLock.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getTorExecutableFile() {
|
||||
return torLib.exists() ? torLib : super.getTorExecutableFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getObfs4ExecutableFile() {
|
||||
return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installTorExecutable() throws IOException {
|
||||
File extracted = super.getTorExecutableFile();
|
||||
if (torLib.exists()) {
|
||||
// If an older version left behind a Tor binary, delete it
|
||||
if (extracted.exists()) {
|
||||
if (extracted.delete()) LOG.info("Deleted Tor binary");
|
||||
else LOG.info("Failed to delete Tor binary");
|
||||
}
|
||||
} else if (SDK_INT < 29) {
|
||||
// The binary wasn't extracted at install time. Try to extract it
|
||||
extractLibraryFromApk(TOR_LIB_NAME, extracted);
|
||||
} else {
|
||||
// No point extracting the binary, we won't be allowed to execute it
|
||||
throw new FileNotFoundException(torLib.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installObfs4Executable() throws IOException {
|
||||
File extracted = super.getObfs4ExecutableFile();
|
||||
if (obfs4Lib.exists()) {
|
||||
// If an older version left behind an obfs4 binary, delete it
|
||||
if (extracted.exists()) {
|
||||
if (extracted.delete()) LOG.info("Deleted obfs4 binary");
|
||||
else LOG.info("Failed to delete obfs4 binary");
|
||||
}
|
||||
} else if (SDK_INT < 29) {
|
||||
// The binary wasn't extracted at install time. Try to extract it
|
||||
extractLibraryFromApk(OBFS4_LIB_NAME, extracted);
|
||||
} else {
|
||||
// No point extracting the binary, we won't be allowed to execute it
|
||||
throw new FileNotFoundException(obfs4Lib.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void extractLibraryFromApk(String libName, File dest)
|
||||
throws IOException {
|
||||
File sourceDir = new File(app.getApplicationInfo().sourceDir);
|
||||
if (sourceDir.isFile()) {
|
||||
// Look for other APK files in the same directory, if we're allowed
|
||||
File parent = sourceDir.getParentFile();
|
||||
if (parent != null) sourceDir = parent;
|
||||
}
|
||||
List<String> libPaths = getSupportedLibraryPaths(libName);
|
||||
for (File apk : findApkFiles(sourceDir)) {
|
||||
ZipInputStream zin = new ZipInputStream(new FileInputStream(apk));
|
||||
for (ZipEntry e = zin.getNextEntry(); e != null;
|
||||
e = zin.getNextEntry()) {
|
||||
if (libPaths.contains(e.getName())) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Extracting " + e.getName()
|
||||
+ " from " + apk.getAbsolutePath());
|
||||
}
|
||||
extract(zin, dest); // Zip input stream will be closed
|
||||
return;
|
||||
}
|
||||
}
|
||||
zin.close();
|
||||
}
|
||||
throw new FileNotFoundException(libName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all files with the extension .apk or .APK under the given root.
|
||||
*/
|
||||
private List<File> findApkFiles(File root) {
|
||||
List<File> files = new ArrayList<>();
|
||||
findApkFiles(root, files);
|
||||
return files;
|
||||
}
|
||||
|
||||
private void findApkFiles(File f, List<File> files) {
|
||||
if (f.isFile() && f.getName().toLowerCase().endsWith(".apk")) {
|
||||
files.add(f);
|
||||
} else if (f.isDirectory()) {
|
||||
File[] children = f.listFiles();
|
||||
if (children != null) {
|
||||
for (File child : children) findApkFiles(child, files);
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths at which libraries with the given name would be found
|
||||
* inside an APK file, for all architectures supported by the device, in
|
||||
* order of preference.
|
||||
*/
|
||||
private List<String> getSupportedLibraryPaths(String libName) {
|
||||
List<String> architectures = new ArrayList<>();
|
||||
for (String abi : AndroidUtils.getSupportedArchitectures()) {
|
||||
if (LIBRARY_ARCHITECTURES.contains(abi)) {
|
||||
architectures.add("lib/" + abi + "/" + libName);
|
||||
}
|
||||
}
|
||||
return architectures;
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
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.event.EventBus;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.network.NetworkManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.Backoff;
|
||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||
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.duplex.DuplexPlugin;
|
||||
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.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
@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 double BACKOFF_BASE = 1.2;
|
||||
|
||||
private final Executor ioExecutor, wakefulIoExecutor;
|
||||
private final Application app;
|
||||
private final Executor ioExecutor;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Context appContext;
|
||||
private final NetworkManager networkManager;
|
||||
private final LocationUtils locationUtils;
|
||||
private final EventBus eventBus;
|
||||
@@ -53,28 +49,18 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
|
||||
private final ResourceProvider resourceProvider;
|
||||
private final CircumventionProvider circumventionProvider;
|
||||
private final BatteryManager batteryManager;
|
||||
private final AndroidWakeLockManager wakeLockManager;
|
||||
private final Clock clock;
|
||||
private final File torDirectory;
|
||||
|
||||
@Inject
|
||||
public AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||
Application app,
|
||||
NetworkManager networkManager,
|
||||
LocationUtils locationUtils,
|
||||
EventBus eventBus,
|
||||
SocketFactory torSocketFactory,
|
||||
BackoffFactory backoffFactory,
|
||||
ResourceProvider resourceProvider,
|
||||
public AndroidTorPluginFactory(Executor ioExecutor,
|
||||
ScheduledExecutorService scheduler, Context appContext,
|
||||
NetworkManager networkManager, LocationUtils locationUtils,
|
||||
EventBus eventBus, SocketFactory torSocketFactory,
|
||||
BackoffFactory backoffFactory, ResourceProvider resourceProvider,
|
||||
CircumventionProvider circumventionProvider,
|
||||
BatteryManager batteryManager,
|
||||
AndroidWakeLockManager wakeLockManager,
|
||||
Clock clock,
|
||||
@TorDirectory File torDirectory) {
|
||||
BatteryManager batteryManager, Clock clock) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.wakefulIoExecutor = wakefulIoExecutor;
|
||||
this.app = app;
|
||||
this.scheduler = scheduler;
|
||||
this.appContext = appContext;
|
||||
this.networkManager = networkManager;
|
||||
this.locationUtils = locationUtils;
|
||||
this.eventBus = eventBus;
|
||||
@@ -83,9 +69,7 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
|
||||
this.resourceProvider = resourceProvider;
|
||||
this.circumventionProvider = circumventionProvider;
|
||||
this.batteryManager = batteryManager;
|
||||
this.wakeLockManager = wakeLockManager;
|
||||
this.clock = clock;
|
||||
this.torDirectory = torDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,12 +112,11 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
|
||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
|
||||
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor,
|
||||
wakefulIoExecutor, app, networkManager, locationUtils,
|
||||
torSocketFactory, clock, resourceProvider,
|
||||
circumventionProvider, batteryManager, wakeLockManager,
|
||||
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, scheduler,
|
||||
appContext, networkManager, locationUtils, torSocketFactory,
|
||||
clock, resourceProvider, circumventionProvider, batteryManager,
|
||||
backoff, torRendezvousCrypto, callback, architecture,
|
||||
MAX_LATENCY, MAX_IDLE_TIME, torDirectory);
|
||||
MAX_LATENCY, MAX_IDLE_TIME);
|
||||
eventBus.addListener(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() {
|
||||
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
|
||||
TelephonyManager tm = (TelephonyManager) o;
|
||||
return tm == null ? "" : tm.getNetworkCountryIso();
|
||||
return tm.getNetworkCountryIso();
|
||||
}
|
||||
|
||||
private String getCountryFromSimCard() {
|
||||
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
|
||||
TelephonyManager tm = (TelephonyManager) o;
|
||||
return tm == null ? "" : tm.getSimCountryIso();
|
||||
return tm.getSimCountryIso();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
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.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -21,23 +16,6 @@ import dagger.Provides;
|
||||
@Module
|
||||
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
|
||||
@Singleton
|
||||
SecureRandomProvider provideSecureRandomProvider(
|
||||
@@ -69,11 +47,4 @@ public class AndroidSystemModule {
|
||||
ResourceProvider provideResourceProvider(AndroidResourceProvider 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,25 +0,0 @@
|
||||
package org.briarproject.bramble.api;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An item that can be consumed once.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
public class Consumable<T> {
|
||||
|
||||
private final AtomicReference<T> reference;
|
||||
|
||||
public Consumable(T item) {
|
||||
reference = new AtomicReference<>(item);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T consume() {
|
||||
return reference.getAndSet(null);
|
||||
}
|
||||
}
|
||||
@@ -35,24 +35,24 @@ public interface ClientHelper {
|
||||
|
||||
Message createMessageForStoringMetadata(GroupId g);
|
||||
|
||||
Message getSmallMessage(MessageId m) throws DbException;
|
||||
Message getMessage(MessageId m) throws DbException;
|
||||
|
||||
Message getSmallMessage(Transaction txn, MessageId m) throws DbException;
|
||||
Message getMessage(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
BdfList getSmallMessageAsList(MessageId m)
|
||||
throws DbException, FormatException;
|
||||
BdfList getMessageAsList(MessageId m) throws DbException, FormatException;
|
||||
|
||||
BdfList getSmallMessageAsList(Transaction txn, MessageId m)
|
||||
throws DbException, FormatException;
|
||||
BdfList getMessageAsList(Transaction txn, MessageId m) throws DbException,
|
||||
FormatException;
|
||||
|
||||
BdfDictionary getGroupMetadataAsDictionary(GroupId g)
|
||||
throws DbException, FormatException;
|
||||
BdfDictionary getGroupMetadataAsDictionary(GroupId g) throws DbException,
|
||||
FormatException;
|
||||
|
||||
BdfDictionary getGroupMetadataAsDictionary(Transaction txn, GroupId g)
|
||||
throws DbException, FormatException;
|
||||
|
||||
BdfDictionary getMessageMetadataAsDictionary(MessageId m)
|
||||
throws DbException, FormatException;
|
||||
throws DbException,
|
||||
FormatException;
|
||||
|
||||
BdfDictionary getMessageMetadataAsDictionary(Transaction txn, MessageId m)
|
||||
throws DbException, FormatException;
|
||||
@@ -67,8 +67,8 @@ public interface ClientHelper {
|
||||
BdfDictionary query) throws DbException, FormatException;
|
||||
|
||||
Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary(
|
||||
Transaction txn, GroupId g, BdfDictionary query)
|
||||
throws DbException, FormatException;
|
||||
Transaction txn, GroupId g, BdfDictionary query) throws DbException,
|
||||
FormatException;
|
||||
|
||||
void mergeGroupMetadata(GroupId g, BdfDictionary metadata)
|
||||
throws DbException, FormatException;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -163,23 +163,27 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
* less than or equal to the given length, for transmission over a
|
||||
* transport with the given maximum latency. Returns null if there are no
|
||||
* sendable messages that fit in the given length.
|
||||
*
|
||||
* @param small True if only single-block messages should be sent
|
||||
*/
|
||||
@Nullable
|
||||
Collection<Message> generateBatch(Transaction txn, ContactId c,
|
||||
int maxLength, int maxLatency, boolean small) throws DbException;
|
||||
int maxLength, int maxLatency) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns an offer for the given contact for transmission over a
|
||||
* transport with the given maximum latency, or null if there are no
|
||||
* messages to offer.
|
||||
*
|
||||
* @param small True if only single-block messages should be offered
|
||||
*/
|
||||
@Nullable
|
||||
Offer generateOffer(Transaction txn, ContactId c, int maxMessages,
|
||||
int maxLatency, boolean small) throws DbException;
|
||||
int maxLatency) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns a request for the given contact, or null if there are no
|
||||
* messages to request.
|
||||
*/
|
||||
@Nullable
|
||||
Request generateRequest(Transaction txn, ContactId c, int maxMessages)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns a batch of messages for the given contact, with a total length
|
||||
@@ -268,14 +272,13 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
Collection<Identity> getIdentities(Transaction txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the single-block message with the given ID.
|
||||
* Returns the message with the given ID.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*
|
||||
* @throws MessageDeletedException if the message has been deleted
|
||||
* @throws MessageTooLargeException if the message has more than one block
|
||||
*/
|
||||
Message getSmallMessage(Transaction txn, MessageId m) throws DbException;
|
||||
Message getMessage(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of all delivered messages in the given group.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package org.briarproject.bramble.api.db;
|
||||
|
||||
/**
|
||||
* Thrown when a multi-block message is requested from the database via a
|
||||
* method that is only suitable for requesting single-block messages.
|
||||
*/
|
||||
public class MessageTooLargeException extends DbException {
|
||||
}
|
||||
@@ -18,8 +18,6 @@ public interface EventBus {
|
||||
/**
|
||||
* Asynchronously notifies all listeners of an event. Listeners are
|
||||
* notified on the {@link EventExecutor}.
|
||||
* <p>
|
||||
* This method can safely be called while holding a lock.
|
||||
*/
|
||||
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.Transaction;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Wakeful;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@@ -66,7 +65,6 @@ public interface LifecycleManager {
|
||||
* Opens the {@link DatabaseComponent} using the given key and starts any
|
||||
* registered {@link Service Services}.
|
||||
*/
|
||||
@Wakeful
|
||||
StartResult startServices(SecretKey dbKey);
|
||||
|
||||
/**
|
||||
@@ -74,7 +72,6 @@ public interface LifecycleManager {
|
||||
* registered {@link ExecutorService ExecutorServices}, and closes the
|
||||
* {@link DatabaseComponent}.
|
||||
*/
|
||||
@Wakeful
|
||||
void stopServices();
|
||||
|
||||
/**
|
||||
@@ -107,7 +104,6 @@ public interface LifecycleManager {
|
||||
*
|
||||
* @param txn A read-write transaction
|
||||
*/
|
||||
@Wakeful
|
||||
void onDatabaseOpened(Transaction txn) throws DbException;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,16 @@
|
||||
package org.briarproject.bramble.api.lifecycle;
|
||||
|
||||
import org.briarproject.bramble.api.system.Wakeful;
|
||||
|
||||
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()}.
|
||||
*/
|
||||
@Wakeful
|
||||
void startService() throws ServiceException;
|
||||
|
||||
/**
|
||||
* Stops the service. This method must not be called concurrently with
|
||||
* {@link #startService()}.
|
||||
*/
|
||||
@Wakeful
|
||||
void stopService() throws ServiceException;
|
||||
}
|
||||
|
||||
@@ -6,16 +6,8 @@ public interface BluetoothConstants {
|
||||
|
||||
int UUID_BYTES = 16;
|
||||
|
||||
// Transport properties
|
||||
String PROP_ADDRESS = "address";
|
||||
String PROP_UUID = "uuid";
|
||||
|
||||
// Local settings (not shared with contacts)
|
||||
String PREF_ADDRESS_IS_REFLECTED = "addressIsReflected";
|
||||
String PREF_EVER_CONNECTED = "everConnected";
|
||||
|
||||
// Default values for local settings
|
||||
boolean DEFAULT_PREF_PLUGIN_ENABLE = false;
|
||||
boolean DEFAULT_PREF_ADDRESS_IS_REFLECTED = false;
|
||||
boolean DEFAULT_PREF_EVER_CONNECTED = false;
|
||||
String PREF_BT_ENABLE = "enable";
|
||||
}
|
||||
|
||||
@@ -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.PendingContactId;
|
||||
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;
|
||||
|
||||
@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)
|
||||
String PROP_IP_PORTS = "ipPorts";
|
||||
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_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.nullsafety.NotNullByDefault;
|
||||
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;
|
||||
|
||||
@NotNullByDefault
|
||||
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.
|
||||
*/
|
||||
@@ -71,28 +27,17 @@ public interface Plugin {
|
||||
/**
|
||||
* Starts the plugin.
|
||||
*/
|
||||
@Wakeful
|
||||
void start() throws PluginException;
|
||||
|
||||
/**
|
||||
* Stops the plugin.
|
||||
*/
|
||||
@Wakeful
|
||||
void stop() throws PluginException;
|
||||
|
||||
/**
|
||||
* Returns the current state of the plugin.
|
||||
* Returns true if the plugin is running.
|
||||
*/
|
||||
State getState();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
boolean isRunning();
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* passing any created connections to the corresponding handlers.
|
||||
*/
|
||||
@Wakeful
|
||||
void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||
properties);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
package org.briarproject.bramble.api.plugin;
|
||||
|
||||
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.settings.Settings;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* An interface through which a transport plugin interacts with the rest of
|
||||
* the application.
|
||||
@@ -27,11 +21,6 @@ public interface PluginCallback extends ConnectionHandler {
|
||||
*/
|
||||
TransportProperties getLocalProperties();
|
||||
|
||||
/**
|
||||
* Returns the plugin's remote transport properties.
|
||||
*/
|
||||
Collection<TransportProperties> getRemoteProperties();
|
||||
|
||||
/**
|
||||
* Merges the given settings with the plugin's settings
|
||||
*/
|
||||
@@ -43,17 +32,12 @@ public interface PluginCallback extends ConnectionHandler {
|
||||
void mergeLocalProperties(TransportProperties p);
|
||||
|
||||
/**
|
||||
* Informs the callback of the plugin's current state.
|
||||
* <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.
|
||||
* Signals that the transport is enabled.
|
||||
*/
|
||||
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 java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface PluginConfig {
|
||||
@@ -16,11 +14,4 @@ public interface PluginConfig {
|
||||
Collection<SimplexPluginFactory> getSimplexFactories();
|
||||
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
|
||||
public interface TorConstants {
|
||||
|
||||
TransportId ID = new TransportId("org.briarproject.bramble.tor");
|
||||
|
||||
// Transport properties
|
||||
String PROP_ONION_V2 = "onion";
|
||||
String PROP_ONION_V3 = "onion3";
|
||||
|
||||
@@ -16,45 +13,14 @@ public interface TorConstants {
|
||||
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
|
||||
int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds
|
||||
|
||||
// Local settings (not shared with contacts)
|
||||
String PREF_TOR_NETWORK = "network2";
|
||||
String PREF_TOR_PORT = "port";
|
||||
String PREF_TOR_MOBILE = "useMobileData";
|
||||
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_WITHOUT_BRIDGES = 1;
|
||||
int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
|
||||
// TODO: Remove when settings migration code is removed
|
||||
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");
|
||||
|
||||
// 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.rendezvous.KeyMaterialSource;
|
||||
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
||||
import org.briarproject.bramble.api.system.Wakeful;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -22,7 +21,6 @@ public interface DuplexPlugin extends Plugin {
|
||||
* Attempts to create and return a connection using the given transport
|
||||
* properties. Returns null if a connection cannot be created.
|
||||
*/
|
||||
@Wakeful
|
||||
@Nullable
|
||||
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 TransportId transportId;
|
||||
private final boolean incoming, exception;
|
||||
private final boolean incoming;
|
||||
|
||||
public ConnectionClosedEvent(ContactId contactId, TransportId transportId,
|
||||
boolean incoming, boolean exception) {
|
||||
boolean incoming) {
|
||||
this.contactId = contactId;
|
||||
this.transportId = transportId;
|
||||
this.incoming = incoming;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
@@ -34,8 +33,4 @@ public class ConnectionClosedEvent extends Event {
|
||||
public boolean isIncoming() {
|
||||
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.event.Event;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.ConnectionStatus;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when a contact disconnects and is no longer
|
||||
* connected via any transport.
|
||||
* An event that is broadcast when a contact's connection status changes.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class ContactDisconnectedEvent extends Event {
|
||||
public class ConnectionStatusChangedEvent extends Event {
|
||||
|
||||
private final ContactId contactId;
|
||||
private final ConnectionStatus status;
|
||||
|
||||
public ContactDisconnectedEvent(ContactId contactId) {
|
||||
public ConnectionStatusChangedEvent(ContactId contactId,
|
||||
ConnectionStatus status) {
|
||||
this.contactId = contactId;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
public ConnectionStatus getConnectionStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -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.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 a plugin enters the {@link State#ACTIVE}
|
||||
* state.
|
||||
* An event that is broadcast when a transport is disabled.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class TransportActiveEvent extends Event {
|
||||
public class TransportDisabledEvent extends Event {
|
||||
|
||||
private final TransportId transportId;
|
||||
|
||||
public TransportActiveEvent(TransportId transportId) {
|
||||
public TransportDisabledEvent(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.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 a plugin leaves the {@link State#ACTIVE}
|
||||
* state.
|
||||
* An event that is broadcast when a transport is enabled.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class TransportInactiveEvent extends Event {
|
||||
public class TransportEnabledEvent extends Event {
|
||||
|
||||
private final TransportId transportId;
|
||||
|
||||
public TransportInactiveEvent(TransportId transportId) {
|
||||
public TransportEnabledEvent(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.TransportConnectionWriter;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.bramble.api.system.Wakeful;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -19,7 +18,6 @@ public interface SimplexPlugin extends Plugin {
|
||||
* Attempts to create and return a reader for the given transport
|
||||
* properties. Returns null if a reader cannot be created.
|
||||
*/
|
||||
@Wakeful
|
||||
@Nullable
|
||||
TransportConnectionReader createReader(TransportProperties p);
|
||||
|
||||
@@ -27,7 +25,6 @@ public interface SimplexPlugin extends Plugin {
|
||||
* Attempts to create and return a writer for the given transport
|
||||
* properties. Returns null if a writer cannot be created.
|
||||
*/
|
||||
@Wakeful
|
||||
@Nullable
|
||||
TransportConnectionWriter createWriter(TransportProperties p);
|
||||
}
|
||||
|
||||
@@ -12,11 +12,6 @@ public interface TransportPropertyConstants {
|
||||
*/
|
||||
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,
|
||||
* 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 REQUEST = 3;
|
||||
byte VERSIONS = 4;
|
||||
byte PRIORITY = 5;
|
||||
}
|
||||
|
||||
@@ -29,15 +29,10 @@ public interface SyncConstants {
|
||||
*/
|
||||
int MESSAGE_HEADER_LENGTH = UniqueId.LENGTH + 8;
|
||||
|
||||
/**
|
||||
* The maximum length of a block in bytes.
|
||||
*/
|
||||
int MAX_BLOCK_LENGTH = 32 * 1024; // 32 KiB
|
||||
|
||||
/**
|
||||
* The maximum length of a message body in bytes.
|
||||
*/
|
||||
int MAX_MESSAGE_BODY_LENGTH = MAX_BLOCK_LENGTH;
|
||||
int MAX_MESSAGE_BODY_LENGTH = 32 * 1024; // 32 KiB
|
||||
|
||||
/**
|
||||
* The maximum length of a message in bytes.
|
||||
@@ -54,10 +49,4 @@ public interface SyncConstants {
|
||||
* simultaneously.
|
||||
*/
|
||||
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;
|
||||
|
||||
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 writePriority(Priority p) 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.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface SyncSessionFactory {
|
||||
|
||||
SyncSession createIncomingSession(ContactId c, InputStream in,
|
||||
PriorityHandler handler);
|
||||
SyncSession createIncomingSession(ContactId c, InputStream in);
|
||||
|
||||
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
|
||||
int maxLatency, StreamWriter streamWriter);
|
||||
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
|
||||
StreamWriter streamWriter);
|
||||
|
||||
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
|
||||
int maxLatency, int maxIdleTime, StreamWriter streamWriter,
|
||||
@Nullable Priority priority);
|
||||
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
|
||||
int maxIdleTime, StreamWriter streamWriter);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -7,16 +7,16 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when one or more messages are received from, or
|
||||
* offered by, a contact and need to be acknowledged.
|
||||
* An event that is broadcast when a message is received from, or offered by, a
|
||||
* contact and needs to be acknowledged.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class MessagesToAckEvent extends Event {
|
||||
public class MessageToAckEvent extends Event {
|
||||
|
||||
private final ContactId contactId;
|
||||
|
||||
public MessagesToAckEvent(ContactId contactId) {
|
||||
public MessageToAckEvent(ContactId contactId) {
|
||||
this.contactId = contactId;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.bramble.api.plugin.event;
|
||||
package org.briarproject.bramble.api.sync.event;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
@@ -7,16 +7,16 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when a contact connects that was not previously
|
||||
* connected via any transport.
|
||||
* An event that is broadcast when a message is offered by a contact and needs
|
||||
* to be requested.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class ContactConnectedEvent extends Event {
|
||||
public class MessageToRequestEvent extends Event {
|
||||
|
||||
private final ContactId contactId;
|
||||
|
||||
public ContactConnectedEvent(ContactId contactId) {
|
||||
public MessageToRequestEvent(ContactId contactId) {
|
||||
this.contactId = contactId;
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.briarproject.bramble.api.sync.event;
|
||||
|
||||
import org.briarproject.bramble.api.Consumable;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when one or more messages are offered by a
|
||||
* contact and need to be requested.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class MessagesToRequestEvent extends Event {
|
||||
|
||||
private final ContactId contactId;
|
||||
private final Consumable<Collection<MessageId>> ids;
|
||||
|
||||
public MessagesToRequestEvent(ContactId contactId,
|
||||
Collection<MessageId> ids) {
|
||||
this.contactId = contactId;
|
||||
this.ids = new Consumable<>(ids);
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Collection<MessageId> consumeIds() {
|
||||
return ids.consume();
|
||||
}
|
||||
}
|
||||
@@ -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.Target;
|
||||
|
||||
@@ -12,11 +11,15 @@ import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Annotation for injecting the {@link File directory} where the Tor plugin
|
||||
* should store its state.
|
||||
* Annotation for injecting a scheduled executor service
|
||||
* 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
|
||||
@Target({FIELD, METHOD, PARAMETER})
|
||||
@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 java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
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
|
||||
public class PrivacyUtils {
|
||||
|
||||
@@ -23,7 +19,7 @@ public class PrivacyUtils {
|
||||
|
||||
@Nullable
|
||||
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
|
||||
if (address.equals("02:00:00:00:00:00")) return address;
|
||||
// keep first and last octet of MAC address
|
||||
@@ -31,37 +27,39 @@ public class PrivacyUtils {
|
||||
+ address.substring(14, 17);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String scrubInetAddress(InetAddress address) {
|
||||
if (address instanceof Inet4Address) {
|
||||
// Don't scrub local IPv4 addresses
|
||||
if (address.isLoopbackAddress() || address.isLinkLocalAddress() ||
|
||||
address.isSiteLocalAddress()) {
|
||||
return address.getHostAddress();
|
||||
}
|
||||
// Keep first and last octet of non-local IPv4 addresses
|
||||
return scrubIpv4Address(address.getAddress());
|
||||
} else {
|
||||
// Keep first and last octet of IPv6 addresses
|
||||
return scrubIpv6Address(address.getAddress());
|
||||
}
|
||||
// don't scrub link and site local addresses
|
||||
if (address.isLinkLocalAddress() || address.isSiteLocalAddress())
|
||||
return address.toString();
|
||||
// completely scrub IPv6 addresses
|
||||
if (address instanceof Inet6Address) return "[scrubbed]";
|
||||
// keep first and last octet of IPv4 addresses
|
||||
return scrubInetAddress(address.toString());
|
||||
}
|
||||
|
||||
private static String scrubIpv4Address(byte[] ipv4) {
|
||||
return (ipv4[0] & 0xFF) + ".[scrubbed]." + (ipv4[3] & 0xFF);
|
||||
}
|
||||
|
||||
private static String scrubIpv6Address(byte[] ipv6) {
|
||||
String hex = toHexString(ipv6).toLowerCase();
|
||||
return hex.substring(0, 2) + "[scrubbed]" + hex.substring(30);
|
||||
@Nullable
|
||||
public static String scrubInetAddress(@Nullable String address) {
|
||||
if (address == null) return null;
|
||||
|
||||
int firstDot = address.indexOf(".");
|
||||
if (firstDot == -1) return "[scrubbed]";
|
||||
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) {
|
||||
return scrubInetAddress(address.getAddress());
|
||||
InetAddress inetAddress = address.getAddress();
|
||||
return scrubInetAddress(inetAddress);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String scrubSocketAddress(SocketAddress address) {
|
||||
if (address instanceof InetSocketAddress)
|
||||
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.rendezvous.RendezvousModule;
|
||||
import org.briarproject.bramble.sync.validation.ValidationModule;
|
||||
import org.briarproject.bramble.system.SystemModule;
|
||||
import org.briarproject.bramble.transport.TransportModule;
|
||||
import org.briarproject.bramble.versioning.VersioningModule;
|
||||
|
||||
@@ -30,6 +31,8 @@ public interface BrambleCoreEagerSingletons {
|
||||
|
||||
void inject(RendezvousModule.EagerSingletons init);
|
||||
|
||||
void inject(SystemModule.EagerSingletons init);
|
||||
|
||||
void inject(TransportModule.EagerSingletons init);
|
||||
|
||||
void inject(ValidationModule.EagerSingletons init);
|
||||
@@ -47,6 +50,7 @@ public interface BrambleCoreEagerSingletons {
|
||||
c.inject(new RendezvousModule.EagerSingletons());
|
||||
c.inject(new PluginModule.EagerSingletons());
|
||||
c.inject(new PropertiesModule.EagerSingletons());
|
||||
c.inject(new SystemModule.EagerSingletons());
|
||||
c.inject(new TransportModule.EagerSingletons());
|
||||
c.inject(new ValidationModule.EagerSingletons());
|
||||
c.inject(new VersioningModule.EagerSingletons());
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.bramble;
|
||||
|
||||
import org.briarproject.bramble.client.ClientModule;
|
||||
import org.briarproject.bramble.connection.ConnectionModule;
|
||||
import org.briarproject.bramble.contact.ContactModule;
|
||||
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
||||
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.event.EventModule;
|
||||
import org.briarproject.bramble.identity.IdentityModule;
|
||||
import org.briarproject.bramble.io.IoModule;
|
||||
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||
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.sync.SyncModule;
|
||||
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.versioning.VersioningModule;
|
||||
|
||||
@@ -29,8 +27,6 @@ import dagger.Module;
|
||||
|
||||
@Module(includes = {
|
||||
ClientModule.class,
|
||||
ClockModule.class,
|
||||
ConnectionModule.class,
|
||||
ContactModule.class,
|
||||
CryptoModule.class,
|
||||
CryptoExecutorModule.class,
|
||||
@@ -39,7 +35,6 @@ import dagger.Module;
|
||||
DatabaseExecutorModule.class,
|
||||
EventModule.class,
|
||||
IdentityModule.class,
|
||||
IoModule.class,
|
||||
KeyAgreementModule.class,
|
||||
LifecycleModule.class,
|
||||
PluginModule.class,
|
||||
@@ -49,6 +44,7 @@ import dagger.Module;
|
||||
RendezvousModule.class,
|
||||
SettingsModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class,
|
||||
ValidationModule.class,
|
||||
VersioningModule.class
|
||||
|
||||
@@ -116,27 +116,25 @@ class ClientHelperImpl implements ClientHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getSmallMessage(MessageId m) throws DbException {
|
||||
return db.transactionWithResult(true, txn -> getSmallMessage(txn, m));
|
||||
public Message getMessage(MessageId m) throws DbException {
|
||||
return db.transactionWithResult(true, txn -> getMessage(txn, m));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getSmallMessage(Transaction txn, MessageId m)
|
||||
throws DbException {
|
||||
return db.getSmallMessage(txn, m);
|
||||
public Message getMessage(Transaction txn, MessageId m) throws DbException {
|
||||
return db.getMessage(txn, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList getSmallMessageAsList(MessageId m)
|
||||
public BdfList getMessageAsList(MessageId m) throws DbException,
|
||||
FormatException {
|
||||
return db.transactionWithResult(true, txn -> getMessageAsList(txn, m));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList getMessageAsList(Transaction txn, MessageId m)
|
||||
throws DbException, FormatException {
|
||||
return db.transactionWithResult(true, txn ->
|
||||
getSmallMessageAsList(txn, m));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList getSmallMessageAsList(Transaction txn, MessageId m)
|
||||
throws DbException, FormatException {
|
||||
return toList(db.getSmallMessage(txn, m).getBody());
|
||||
return toList(db.getMessage(txn, m).getBody());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.db.DataTooOldException;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.MessageDeletedException;
|
||||
import org.briarproject.bramble.api.db.MessageTooLargeException;
|
||||
import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
@@ -126,6 +125,11 @@ interface Database<T> {
|
||||
void addMessageDependency(T txn, Message dependent, MessageId dependency,
|
||||
MessageState dependentState) throws DbException;
|
||||
|
||||
/**
|
||||
* Records that a message has been offered by the given contact.
|
||||
*/
|
||||
void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a pending contact.
|
||||
*/
|
||||
@@ -214,6 +218,13 @@ interface Database<T> {
|
||||
boolean containsVisibleMessage(T txn, ContactId c, MessageId m)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the number of messages offered by the given contact.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
int countOfferedMessages(T txn, ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Deletes the message with the given ID. Unlike
|
||||
* {@link #removeMessage(Object, MessageId)}, the message ID and any other
|
||||
@@ -321,14 +332,13 @@ interface Database<T> {
|
||||
Collection<Identity> getIdentities(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the single-block message with the given ID.
|
||||
* Returns the message with the given ID.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*
|
||||
* @throws MessageDeletedException if the message has been deleted
|
||||
* @throws MessageTooLargeException if the message has more than one block
|
||||
*/
|
||||
Message getSmallMessage(T txn, MessageId m) throws DbException;
|
||||
Message getMessage(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs and states of all dependencies of the given message.
|
||||
@@ -447,13 +457,13 @@ interface Database<T> {
|
||||
int maxMessages, int maxLatency) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of some single-block messages that are eligible to be
|
||||
* offered to the given contact, up to the given number of messages.
|
||||
* Returns the IDs of some messages that are eligible to be requested from
|
||||
* the given contact, up to the given number of messages.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<MessageId> getSmallMessagesToOffer(T txn, ContactId c,
|
||||
int maxMessages, int maxLatency) throws DbException;
|
||||
Collection<MessageId> getMessagesToRequest(T txn, ContactId c,
|
||||
int maxMessages) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of some messages that are eligible to be sent to the
|
||||
@@ -464,15 +474,6 @@ interface Database<T> {
|
||||
Collection<MessageId> getMessagesToSend(T txn, ContactId c, int maxLength,
|
||||
int maxLatency) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of some single-block messages that are eligible to be
|
||||
* sent to the given contact, up to the given total length.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<MessageId> getSmallMessagesToSend(T txn, ContactId c,
|
||||
int maxLength, int maxLatency) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any messages that need to be validated.
|
||||
* <p/>
|
||||
@@ -635,6 +636,13 @@ interface Database<T> {
|
||||
*/
|
||||
void removeMessage(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes the given offered messages that were offered by the given
|
||||
* contact.
|
||||
*/
|
||||
void removeOfferedMessages(T txn, ContactId c,
|
||||
Collection<MessageId> requested) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes a pending contact (and all associated state) from the database.
|
||||
*/
|
||||
|
||||
@@ -61,10 +61,10 @@ import org.briarproject.bramble.api.sync.event.MessageAddedEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessageRequestedEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessageSharedEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessageStateChangedEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessagesToAckEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessagesToRequestEvent;
|
||||
import org.briarproject.bramble.api.sync.event.SyncVersionsUpdatedEvent;
|
||||
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
@@ -91,6 +91,7 @@ import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
|
||||
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
@@ -405,22 +406,19 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
@Nullable
|
||||
@Override
|
||||
public Collection<Message> generateBatch(Transaction transaction,
|
||||
ContactId c, int maxLength, int maxLatency, boolean small)
|
||||
throws DbException {
|
||||
ContactId c, int maxLength, int maxLatency) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
Collection<MessageId> ids;
|
||||
if (small)
|
||||
ids = db.getSmallMessagesToSend(txn, c, maxLength, maxLatency);
|
||||
else ids = db.getMessagesToSend(txn, c, maxLength, maxLatency);
|
||||
if (ids.isEmpty()) return null;
|
||||
Collection<MessageId> ids =
|
||||
db.getMessagesToSend(txn, c, maxLength, maxLatency);
|
||||
List<Message> messages = new ArrayList<>(ids.size());
|
||||
for (MessageId m : ids) {
|
||||
messages.add(db.getSmallMessage(txn, m));
|
||||
messages.add(db.getMessage(txn, m));
|
||||
db.updateExpiryTimeAndEta(txn, c, m, maxLatency);
|
||||
}
|
||||
if (ids.isEmpty()) return null;
|
||||
db.lowerRequestedFlag(txn, c, ids);
|
||||
transaction.attach(new MessagesSentEvent(c, ids));
|
||||
return messages;
|
||||
@@ -429,21 +427,34 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
@Nullable
|
||||
@Override
|
||||
public Offer generateOffer(Transaction transaction, ContactId c,
|
||||
int maxMessages, int maxLatency, boolean small) throws DbException {
|
||||
int maxMessages, int maxLatency) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
Collection<MessageId> ids;
|
||||
if (small)
|
||||
ids = db.getSmallMessagesToOffer(txn, c, maxMessages, maxLatency);
|
||||
else ids = db.getMessagesToOffer(txn, c, maxMessages, maxLatency);
|
||||
Collection<MessageId> ids =
|
||||
db.getMessagesToOffer(txn, c, maxMessages, maxLatency);
|
||||
if (ids.isEmpty()) return null;
|
||||
for (MessageId m : ids)
|
||||
db.updateExpiryTimeAndEta(txn, c, m, maxLatency);
|
||||
return new Offer(ids);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Request generateRequest(Transaction transaction, ContactId c,
|
||||
int maxMessages) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
Collection<MessageId> ids = db.getMessagesToRequest(txn, c,
|
||||
maxMessages);
|
||||
if (ids.isEmpty()) return null;
|
||||
db.removeOfferedMessages(txn, c, ids);
|
||||
return new Request(ids);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Collection<Message> generateRequestedBatch(Transaction transaction,
|
||||
@@ -454,12 +465,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
throw new NoSuchContactException();
|
||||
Collection<MessageId> ids =
|
||||
db.getRequestedMessagesToSend(txn, c, maxLength, maxLatency);
|
||||
if (ids.isEmpty()) return null;
|
||||
List<Message> messages = new ArrayList<>(ids.size());
|
||||
for (MessageId m : ids) {
|
||||
messages.add(db.getSmallMessage(txn, m));
|
||||
messages.add(db.getMessage(txn, m));
|
||||
db.updateExpiryTimeAndEta(txn, c, m, maxLatency);
|
||||
}
|
||||
if (ids.isEmpty()) return null;
|
||||
db.lowerRequestedFlag(txn, c, ids);
|
||||
transaction.attach(new MessagesSentEvent(c, ids));
|
||||
return messages;
|
||||
@@ -548,12 +559,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getSmallMessage(Transaction transaction, MessageId m)
|
||||
public Message getMessage(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
return db.getSmallMessage(txn, m);
|
||||
return db.getMessage(txn, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -808,7 +819,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
db.addMessage(txn, m, UNKNOWN, false, false, c);
|
||||
transaction.attach(new MessageAddedEvent(m, c));
|
||||
}
|
||||
transaction.attach(new MessagesToAckEvent(c));
|
||||
transaction.attach(new MessageToAckEvent(c));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -819,20 +830,21 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
boolean ack = false;
|
||||
List<MessageId> request = new ArrayList<>(o.getMessageIds().size());
|
||||
boolean ack = false, request = false;
|
||||
int count = db.countOfferedMessages(txn, c);
|
||||
for (MessageId m : o.getMessageIds()) {
|
||||
if (db.containsVisibleMessage(txn, c, m)) {
|
||||
db.raiseSeenFlag(txn, c, m);
|
||||
db.raiseAckFlag(txn, c, m);
|
||||
ack = true;
|
||||
} else {
|
||||
request.add(m);
|
||||
} else if (count < MAX_OFFERED_MESSAGES) {
|
||||
db.addOfferedMessage(txn, c, m);
|
||||
request = true;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (ack) transaction.attach(new MessagesToAckEvent(c));
|
||||
if (!request.isEmpty())
|
||||
transaction.attach(new MessagesToRequestEvent(c, request));
|
||||
if (ack) transaction.attach(new MessageToAckEvent(c));
|
||||
if (request) transaction.attach(new MessageToRequestEvent(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,6 +6,13 @@ import static java.util.concurrent.TimeUnit.DAYS;
|
||||
|
||||
interface DatabaseConstants {
|
||||
|
||||
/**
|
||||
* The maximum number of offered messages from each contact that will be
|
||||
* stored. If offers arrive more quickly than requests can be sent and this
|
||||
* limit is reached, additional offers will not be stored.
|
||||
*/
|
||||
int MAX_OFFERED_MESSAGES = 1000;
|
||||
|
||||
/**
|
||||
* The namespace of the {@link Settings} where the database schema version
|
||||
* is stored.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user