Compare commits

..

83 Commits

Author SHA1 Message Date
akwizgran
0122282a72 Migrate away from hardware-backed keys until keymaster issues are fixed. 2020-08-14 16:57:09 +01:00
Torsten Grote
c75c8da4b9 Merge branch '1759-fix-periodic-task-cancellation' into 'master'
Fix cancellation of periodic tasks, remove ticker

Closes #1759

See merge request briar/briar!1274
2020-08-14 12:47:20 +00:00
Torsten Grote
2f3f3d256c Merge branch '1745-try-to-bind-on-connectivity-change' into 'master'
Try to bind IPv4 and IPv6 sockets whenever connectivity changes

Closes #1745

See merge request briar/briar!1275
2020-08-14 11:24:18 +00:00
Torsten Grote
1141d01dc7 Merge branch '1758-release-timeout-monitor-resources' into 'master'
Ensure TimeoutInputStreams are closed

Closes #1758

See merge request briar/briar!1273
2020-08-14 11:00:49 +00:00
akwizgran
e06eee2358 Try to bind IPv4 and IPv6 sockets whenever connectivity changes. 2020-08-14 10:44:33 +01:00
akwizgran
c37fe2a246 Fix cancellation of periodic tasks, remove ticker. 2020-08-13 16:35:32 +01:00
akwizgran
79ad5ca07e Ensure TimeoutInputStreams are closed. 2020-08-13 16:35:03 +01:00
akwizgran
0e2d905486 Merge branch '1142-wakeful-lifecycle' into 'master'
Hold a wake lock during app startup and shutdown

See merge request briar/briar!1271
2020-08-13 15:33:09 +00:00
akwizgran
6094014487 Add a comment discouraging use of dedicated threads. 2020-08-13 16:23:44 +01:00
akwizgran
9603ff93e9 Don't sign out when panic button is triggered if configured not to. 2020-08-13 10:19:33 +01:00
akwizgran
e7ac6aef8c Improve readability. 2020-08-13 10:14:13 +01:00
Torsten Grote
4e18115d88 Merge branch '1721-fine-location' into 'master'
Request fine location permission for Bluetooth discovery

Closes #1721

See merge request briar/briar!1272
2020-08-12 19:40:48 +00:00
akwizgran
b57fb9c842 Request fine location permission for Bluetooth discovery. 2020-08-12 17:29:47 +01:00
akwizgran
196a2b7e22 Fix constructor arguments in unit test. 2020-08-12 17:19:13 +01:00
akwizgran
37712203d7 Hold a wake lock while signing out. 2020-08-12 16:47:02 +01:00
akwizgran
cc67237893 Don't redundantly use wakeful IO executor. 2020-08-12 16:47:02 +01:00
akwizgran
79f3a77e1a Annotate methods that should be called with a wake lock. 2020-08-12 16:47:02 +01:00
akwizgran
3ecec61c25 Hold a wake lock while starting plugins. 2020-08-12 16:47:02 +01:00
akwizgran
1e2dc862ef Hold a wake lock during app startup and shutdown. 2020-08-12 16:47:02 +01:00
akwizgran
452c3afbb3 Merge branch '1142-inject-plugin-factories' into 'master'
Use injection to create plugin factories

See merge request briar/briar!1270
2020-08-12 15:46:02 +00:00
akwizgran
9d60fbe957 Merge branch '1142-wakeful-polling' into 'master'
Hold a wake lock while polling

See merge request briar/briar!1269
2020-08-12 15:45:08 +00:00
akwizgran
434b8a37f3 Use wakeful IO executor for polling, reconnection tasks. 2020-08-10 17:24:04 +01:00
akwizgran
5e6a382b4b Merge branch '1142-wakeful-tasks' into 'master'
Hold a wake lock while running scheduled tasks

See merge request briar/briar!1268
2020-08-10 12:54:00 +00:00
akwizgran
b5bb4aff7f Merge branch 'master' into '1142-wakeful-tasks'
# Conflicts:
#   bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskScheduler.java
2020-08-10 12:37:50 +00:00
akwizgran
b0bf9d5a8c Merge branch '1142-wake-lock-refactoring' into 'master'
Share a single wake lock among all holders

See merge request briar/briar!1267
2020-08-10 12:36:06 +00:00
akwizgran
1e6fd8bb74 Merge branch '1142-scheduler-interface' into 'master'
Use TaskScheduler for scheduling tasks

See merge request briar/briar!1266
2020-08-10 10:36:56 +00:00
akwizgran
eac93f43d3 Add comments for wake lock handling. 2020-08-10 11:36:05 +01:00
akwizgran
23f22af6e4 Add a comment, wrap logging in an if. 2020-08-10 11:26:29 +01:00
akwizgran
6e8e955dc2 Update javadocs. 2020-08-07 16:16:31 +01:00
akwizgran
2e2bc2d82f Remove @Immutable annotation, which is no longer true. 2020-08-07 15:51:15 +01:00
akwizgran
1af951f8b4 Use injection to create plugin factories. 2020-08-07 15:11:03 +01:00
akwizgran
086c10abc0 Hold wake lock while polling. 2020-08-07 15:10:18 +01:00
akwizgran
b5341700be Add wakeful IO executor. 2020-08-07 15:10:18 +01:00
akwizgran
d8be340120 Use a unique log tag for each wake lock instance. 2020-08-07 15:09:14 +01:00
akwizgran
7e0d21de38 Add tags for wake lock holders. 2020-08-07 15:09:13 +01:00
akwizgran
1bab15baaf Add fine logging for wake locks. 2020-08-07 15:09:13 +01:00
akwizgran
af1a91c819 Fix circular dependency between scheduler and wake lock manager. 2020-08-07 15:09:13 +01:00
akwizgran
e6c3f82fe2 Fix test expectations. 2020-08-07 15:09:13 +01:00
akwizgran
b2840c1b00 Add method for executing a task on an executor wakefully. 2020-08-07 15:09:13 +01:00
akwizgran
942bb28701 Hold a wake lock while running due tasks. 2020-08-07 15:09:11 +01:00
akwizgran
94dd0a2661 Hold a wake lock while scheduled tasks are running. 2020-08-07 15:08:53 +01:00
akwizgran
3aa00ecb3d Pass executor to scheduler. 2020-08-07 15:08:53 +01:00
akwizgran
d5395d3d01 Shared wake lock must be a singleton. 2020-08-07 15:07:48 +01:00
akwizgran
b6b721e3b1 Use a wider safety margin to allow for scheduler delays. 2020-08-07 15:02:50 +01:00
akwizgran
7cdd05fd67 Log a warning if the wake lock expires before it's renewed. 2020-08-07 15:02:49 +01:00
akwizgran
286f6f492c Share a single OS wake lock among all holders. 2020-08-07 15:02:49 +01:00
akwizgran
eb6b4aa850 Move wake lock properties into factory. 2020-08-07 15:02:49 +01:00
akwizgran
adb657a5b6 Ensure we only call openOutputStream() once. 2020-08-07 15:02:49 +01:00
akwizgran
d794777eb2 Refactor wake lock creation. 2020-08-07 15:02:49 +01:00
akwizgran
090123579d Use elapsed time since boot rather than wall clock time. 2020-08-07 15:02:05 +01:00
akwizgran
f1bde4e75c Fixed delay is easier to reconcile with sleeps than fixed rate. 2020-08-07 15:02:05 +01:00
akwizgran
ac80a90ef3 Add Android task scheduler. 2020-08-07 15:02:05 +01:00
akwizgran
dfefb88b32 Allow Android and headless to use different task schedulers. 2020-08-07 15:02:05 +01:00
akwizgran
86641741a0 Wrap scheduler in an interface. 2020-08-07 15:02:05 +01:00
Torsten Grote
280f87065e Merge branch 'reflected-bt-address' into 'master'
Use reflected BT address if we don't know our own address

See merge request briar/briar!1265
2020-08-07 13:59:00 +00:00
akwizgran
cbe645a4a3 Merge branch 'reflected-transport-properties' into 'master'
Reflect discovered transport properties back to the remote peer

See merge request briar/briar!1264
2020-08-07 13:46:52 +00:00
akwizgran
f4e9e10245 Remove unused constants. 2020-07-21 17:07:37 +01:00
akwizgran
e9f78bc486 Avoid redundantly storing unchanged properties and settings. 2020-07-21 17:06:02 +01:00
akwizgran
a4091be6f7 Ignore reflected address until we've made a Bluetooth connection.
This reduces the opportunities for contacts to reflect a false address.
2020-07-21 17:06:02 +01:00
akwizgran
49f0640278 Use reflected Bluetooth address if we don't know our own address. 2020-07-21 17:06:02 +01:00
akwizgran
d617e67006 Add method for plugins to get remote properties. 2020-07-21 17:06:02 +01:00
akwizgran
2063f6c57c Don't attach contact ID to RemoteTransportPropertiesUpdatedEvent. 2020-07-21 17:05:41 +01:00
akwizgran
f68d8d284f Merge branch 'master' into 'reflected-transport-properties'
# Conflicts:
#   bramble-core/src/main/java/org/briarproject/bramble/properties/TransportPropertyManagerImpl.java
2020-07-21 09:18:29 +00:00
Torsten Grote
3c63fecb5d Merge branch 'v3-hidden-service-migration' into 'master'
Use v3 hidden services (alongside v2 if a service already exists)

See merge request briar/briar!1263
2020-07-20 16:55:08 +00:00
akwizgran
41fdd584ad Test whether event is attached to transaction. 2020-07-17 17:37:22 +01:00
akwizgran
1b37dceb28 Only add the onion hostname to transport properties once. 2020-07-17 16:55:30 +01:00
akwizgran
c183ca0340 Fix a lint warning. 2020-07-17 16:53:26 +01:00
akwizgran
90e91221d9 Reflect discovered properties back to the remote peer. 2020-07-16 14:25:43 +01:00
akwizgran
b91fe66461 Broadcast an event when remote transport properties are updated. 2020-07-16 11:38:55 +01:00
akwizgran
b5ab077afa Merge branch 'htmlcompat' into 'master'
Replace deprecated Html#fromHtml with HtmlCompat#fromHtml.

Closes #1435

See merge request briar/briar!1262
2020-07-14 09:39:21 +00:00
Tobias Preuss
496d4188c7 Replace deprecated Html#fromHtml with HtmlCompat#fromHtml.
Resolves #1435.
2020-07-13 19:52:22 +02:00
Torsten Grote
ab682c82a3 Merge branch 'translation-md' into 'master'
Add TRANSLATION.md file

See merge request briar/briar!1261
2020-07-10 13:31:24 +00:00
akwizgran
375a7276ad Add link to LocLab wiki. 2020-07-10 14:22:06 +01:00
akwizgran
b7084b2486 Add TRANSLATION.md file.
This was recommended by Translate House.
2020-07-10 11:19:57 +01:00
akwizgran
aa152a80d1 Merge branch 'headless-connected' into 'master'
Expose contact connected state to REST API

See merge request briar/briar!1260
2020-07-06 14:50:57 +00:00
Torsten Grote
3f0d9233d9 [headless] expose contact connected state to REST API 2020-07-06 08:21:07 -03:00
akwizgran
9d96ce6db0 Bump version numbers for 1.2.9 release. 2020-07-04 22:40:58 +01:00
akwizgran
45fb5bb445 Merge branch 'expiry-overflow-bug' into 'master'
Fix overflow in expiry warning code, bypass expiry code in release builds

See merge request briar/briar!1259
2020-07-04 21:38:22 +00:00
akwizgran
0756d92ca1 Fix overflow in expiry warning code, bypass code in release builds. 2020-07-04 22:23:06 +01:00
akwizgran
37f80c7682 Bump version numbers for 1.2.8 release. 2020-07-03 16:03:50 +01:00
akwizgran
b409215c57 Update translations. 2020-07-03 15:57:47 +01:00
Torsten Grote
4f0aaf03fd Merge branch '1744-check-socket-is-bound' into 'master'
Check server socket is bound

Closes #1744

See merge request briar/briar!1258
2020-07-03 14:51:05 +00:00
akwizgran
597fef6d50 Check server socket is bound. 2020-07-03 13:04:26 +01:00
164 changed files with 3434 additions and 1914 deletions

View File

@@ -1,8 +1,5 @@
<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">

9
TRANSLATION.md Normal file
View File

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

View File

@@ -11,8 +11,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 10207
versionName "1.2.7"
versionCode 10209
versionName "1.2.9"
consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

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

View File

@@ -6,6 +6,8 @@ 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;
@@ -13,6 +15,8 @@ import dagger.Module;
AndroidBatteryModule.class,
AndroidNetworkModule.class,
AndroidSystemModule.class,
AndroidTaskSchedulerModule.class,
AndroidWakefulIoExecutorModule.class,
CircumventionModule.class,
ReportingModule.class,
SocksModule.class

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,16 +9,17 @@ 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.Scheduler;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -50,20 +51,22 @@ class AndroidNetworkManager implements NetworkManager, Service {
private static final String WIFI_AP_STATE_CHANGED_ACTION =
"android.net.wifi.WIFI_AP_STATE_CHANGED";
private final ScheduledExecutorService scheduler;
private final TaskScheduler scheduler;
private final EventBus eventBus;
private final Executor eventExecutor;
private final Context appContext;
private final AtomicReference<Future<?>> connectivityCheck =
private final AtomicReference<Cancellable> connectivityCheck =
new AtomicReference<>();
private final AtomicBoolean used = new AtomicBoolean(false);
private volatile BroadcastReceiver networkStateReceiver = null;
@Inject
AndroidNetworkManager(@Scheduler ScheduledExecutorService scheduler,
EventBus eventBus, Application app) {
AndroidNetworkManager(TaskScheduler scheduler, EventBus eventBus,
@EventExecutor Executor eventExecutor, Application app) {
this.scheduler = scheduler;
this.eventBus = eventBus;
this.eventExecutor = eventExecutor;
this.appContext = app.getApplicationContext();
}
@@ -104,11 +107,12 @@ class AndroidNetworkManager implements NetworkManager, Service {
}
private void scheduleConnectionStatusUpdate(int delay, TimeUnit unit) {
Future<?> newConnectivityCheck =
scheduler.schedule(this::updateConnectionStatus, delay, unit);
Future<?> oldConnectivityCheck =
Cancellable newConnectivityCheck =
scheduler.schedule(this::updateConnectionStatus, eventExecutor,
delay, unit);
Cancellable oldConnectivityCheck =
connectivityCheck.getAndSet(newConnectivityCheck);
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(false);
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel();
}
private class NetworkStateReceiver extends BroadcastReceiver {

View File

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

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.plugin.bluetooth;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
@@ -9,9 +10,9 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import org.briarproject.bramble.api.io.TimeoutMonitor;
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.plugin.PluginException;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@@ -30,7 +31,6 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.Nullable;
@@ -59,16 +59,16 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
class AndroidBluetoothPlugin
extends BluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
private static final Logger LOG =
getLogger(AndroidBluetoothPlugin.class.getName());
private static final int MAX_DISCOVERY_MS = 10_000;
private final ScheduledExecutorService scheduler;
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final Application app;
private final Clock clock;
private volatile boolean wasEnabledByUs = false;
@@ -78,16 +78,22 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor, Executor ioExecutor,
SecureRandom secureRandom, ScheduledExecutorService scheduler,
AndroidExecutor androidExecutor, Context appContext, Clock clock,
PluginCallback callback, int maxLatency, int maxIdleTime,
int pollingInterval) {
super(connectionLimiter, timeoutMonitor, ioExecutor, secureRandom,
callback, maxLatency, maxIdleTime, pollingInterval);
this.scheduler = scheduler;
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);
this.androidExecutor = androidExecutor;
this.appContext = appContext;
this.app = app;
this.clock = clock;
}
@@ -99,13 +105,13 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED);
receiver = new BluetoothStateReceiver();
appContext.registerReceiver(receiver, filter);
app.registerReceiver(receiver, filter);
}
@Override
public void stop() {
super.stop();
if (receiver != null) appContext.unregisterReceiver(receiver);
if (receiver != null) app.unregisterReceiver(receiver);
}
@Override
@@ -162,7 +168,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
@Override
@Nullable
String getBluetoothAddress() {
String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
String address = AndroidUtils.getBluetoothAddress(app, adapter);
return address.isEmpty() ? null : address;
}
@@ -180,13 +186,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
@Override
DuplexTransportConnection acceptConnection(BluetoothServerSocket ss)
throws IOException {
return wrapSocket(ss.accept());
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s)
throws IOException {
return new AndroidBluetoothTransportConnection(this, connectionLimiter,
timeoutMonitor, appContext, scheduler, s);
return connectionFactory.wrapSocket(this, ss.accept());
}
@Override
@@ -203,7 +203,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
try {
s = d.createInsecureRfcommSocketToServiceRecord(u);
s.connect();
return wrapSocket(s);
return connectionFactory.wrapSocket(this, s);
} catch (IOException e) {
IoUtils.tryToClose(s, LOG, WARNING);
throw e;
@@ -238,7 +238,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
filter.addAction(ACTION_DISCOVERY_STARTED);
filter.addAction(ACTION_DISCOVERY_FINISHED);
filter.addAction(ACTION_FOUND);
appContext.registerReceiver(receiver, filter);
app.registerReceiver(receiver, filter);
try {
if (adapter.startDiscovery()) {
long now = clock.currentTimeMillis();
@@ -275,7 +275,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
} finally {
LOG.info("Cancelling discovery");
adapter.cancelDiscovery();
appContext.unregisterReceiver(receiver);
app.unregisterReceiver(receiver);
}
// Shuffle the addresses so we don't always try the same one first
shuffle(addresses);

View File

@@ -1,57 +1,72 @@
package org.briarproject.bramble.plugin.bluetooth;
import android.content.Context;
import android.app.Application;
import android.bluetooth.BluetoothSocket;
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;
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.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 java.util.concurrent.ScheduledExecutorService;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
@Immutable
@NotNullByDefault
public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = (int) SECONDS.toMillis(30);
private static final int MAX_IDLE_TIME = (int) SECONDS.toMillis(30);
private static final int POLLING_INTERVAL = (int) MINUTES.toMillis(2);
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;
private final ScheduledExecutorService scheduler;
private final Executor ioExecutor, wakefulIoExecutor;
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final AndroidWakeLockManager wakeLockManager;
private final Application app;
private final SecureRandom secureRandom;
private final EventBus eventBus;
private final Clock clock;
private final TimeoutMonitor timeoutMonitor;
private final BackoffFactory backoffFactory;
public AndroidBluetoothPluginFactory(Executor ioExecutor,
ScheduledExecutorService scheduler,
AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, EventBus eventBus, Clock clock,
TimeoutMonitor timeoutMonitor) {
@Inject
public AndroidBluetoothPluginFactory(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor,
AndroidExecutor androidExecutor,
AndroidWakeLockManager wakeLockManager,
Application app,
SecureRandom secureRandom,
EventBus eventBus,
Clock clock,
TimeoutMonitor timeoutMonitor,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.wakefulIoExecutor = wakefulIoExecutor;
this.androidExecutor = androidExecutor;
this.appContext = appContext;
this.wakeLockManager = wakeLockManager;
this.app = app;
this.secureRandom = secureRandom;
this.eventBus = eventBus;
this.clock = clock;
this.timeoutMonitor = timeoutMonitor;
this.backoffFactory = backoffFactory;
}
@Override
@@ -68,10 +83,15 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(PluginCallback callback) {
BluetoothConnectionLimiter connectionLimiter =
new BluetoothConnectionLimiterImpl(eventBus);
BluetoothConnectionFactory<BluetoothSocket> connectionFactory =
new AndroidBluetoothConnectionFactory(connectionLimiter,
wakeLockManager, timeoutMonitor);
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, timeoutMonitor, ioExecutor, secureRandom,
scheduler, androidExecutor, appContext, clock, callback,
MAX_LATENCY, MAX_IDLE_TIME, POLLING_INTERVAL);
connectionLimiter, connectionFactory, ioExecutor,
wakefulIoExecutor, secureRandom, androidExecutor, app,
clock, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin);
return plugin;
}

View File

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

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.plugin.tcp;
import android.annotation.TargetApi;
import android.content.Context;
import android.app.Application;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.event.Event;
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;
@@ -41,6 +42,7 @@ 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;
@@ -60,20 +62,22 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
private volatile SocketFactory socketFactory;
AndroidLanTcpPlugin(Executor ioExecutor, Context appContext,
PluginCallback callback, int maxLatency, int maxIdleTime,
int pollingInterval, int connectionTimeout) {
super(ioExecutor, callback, maxLatency, maxIdleTime, pollingInterval,
connectionTimeout);
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);
// Don't execute more than one connection status check at a time
connectionStatusExecutor =
new PoliteExecutor("AndroidLanTcpPlugin", ioExecutor, 1);
ConnectivityManager connectivityManager = (ConnectivityManager)
appContext.getSystemService(CONNECTIVITY_SERVICE);
if (connectivityManager == null) throw new AssertionError();
this.connectivityManager = connectivityManager;
wifiManager = (WifiManager) appContext.getApplicationContext()
.getSystemService(WIFI_SERVICE);
connectivityManager = (ConnectivityManager)
requireNonNull(app.getSystemService(CONNECTIVITY_SERVICE));
wifiManager = (WifiManager) app.getSystemService(WIFI_SERVICE);
socketFactory = SocketFactory.getDefault();
}
@@ -273,11 +277,11 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
// make outgoing connections on API 21+ if another network
// has internet access
socketFactory = SocketFactory.getDefault();
if (s == INACTIVE) bind();
bind();
} else {
LOG.info("Connected to wifi");
socketFactory = getSocketFactory();
if (s == INACTIVE) bind();
bind();
}
});
}

View File

@@ -1,40 +1,52 @@
package org.briarproject.bramble.plugin.tcp;
import android.content.Context;
import android.app.Application;
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;
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 java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@Immutable
@NotNullByDefault
public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = (int) SECONDS.toMillis(30);
private static final int MAX_IDLE_TIME = (int) SECONDS.toMillis(30);
private static final int POLLING_INTERVAL = (int) MINUTES.toMillis(1);
private static final int CONNECTION_TIMEOUT = (int) SECONDS.toMillis(3);
private static final int MAX_LATENCY = 30_000; // 30 seconds
private static final int MAX_IDLE_TIME = 30_000; // 30 seconds
private static final int CONNECTION_TIMEOUT = 3_000; // 3 seconds
private static final int MIN_POLLING_INTERVAL = 60_000; // 1 minute
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor;
private final Executor ioExecutor, wakefulIoExecutor;
private final EventBus eventBus;
private final Context appContext;
private final BackoffFactory backoffFactory;
private final Application app;
public AndroidLanTcpPluginFactory(Executor ioExecutor, EventBus eventBus,
Context appContext) {
@Inject
public AndroidLanTcpPluginFactory(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor,
EventBus eventBus,
BackoffFactory backoffFactory,
Application app) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.eventBus = eventBus;
this.appContext = appContext;
this.backoffFactory = backoffFactory;
this.app = app;
}
@Override
@@ -49,9 +61,11 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
@Override
public DuplexPlugin createPlugin(PluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor,
appContext, callback, MAX_LATENCY, MAX_IDLE_TIME,
POLLING_INTERVAL, CONNECTION_TIMEOUT);
wakefulIoExecutor, app, backoff, callback,
MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -1,61 +1,60 @@
package org.briarproject.bramble.plugin.tor;
import android.content.Context;
import android.app.Application;
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;
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.RenewableWakeLock;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory;
import static android.content.Context.MODE_PRIVATE;
import static android.content.Context.POWER_SERVICE;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.briarproject.bramble.util.AndroidUtils.getWakeLockTag;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class AndroidTorPlugin extends TorPlugin {
private final Context appContext;
private final RenewableWakeLock wakeLock;
private final Application app;
private final AndroidWakeLock wakeLock;
AndroidTorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
Context appContext, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory,
Clock clock, ResourceProvider resourceProvider,
AndroidTorPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
Application app,
NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager,
AndroidWakeLockManager wakeLockManager,
Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, int initialPollingInterval,
int stablePollingInterval) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
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, initialPollingInterval, stablePollingInterval,
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(appContext), 1, MINUTES);
maxIdleTime, torDirectory);
this.app = app;
wakeLock = wakeLockManager.createWakeLock("TorPlugin");
}
@Override
@@ -66,8 +65,8 @@ class AndroidTorPlugin extends TorPlugin {
@Override
protected long getLastUpdateTime() {
try {
PackageManager pm = appContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(appContext.getPackageName(), 0);
PackageManager pm = app.getPackageManager();
PackageInfo pi = pm.getPackageInfo(app.getPackageName(), 0);
return pi.lastUpdateTime;
} catch (NameNotFoundException e) {
throw new AssertionError(e);

View File

@@ -1,86 +1,91 @@
package org.briarproject.bramble.plugin.tor;
import android.content.Context;
import android.app.Application;
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;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Logger.getLogger;
@Immutable
@NotNullByDefault
public class AndroidTorPluginFactory implements DuplexPluginFactory {
private static final Logger LOG =
getLogger(AndroidTorPluginFactory.class.getName());
Logger.getLogger(AndroidTorPluginFactory.class.getName());
private static final int MAX_LATENCY = (int) SECONDS.toMillis(30);
private static final int MAX_IDLE_TIME = (int) SECONDS.toMillis(30);
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;
/**
* How often to poll before our hidden service becomes reachable.
*/
private static final int INITIAL_POLLING_INTERVAL =
(int) MINUTES.toMillis(1);
/**
* How often to poll when our hidden service is reachable. Our contacts
* will poll when they come online, so our polling is just a fallback in
* case of repeated connection failures.
*/
private static final int STABLE_POLLING_INTERVAL =
(int) MINUTES.toMillis(15);
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final Context appContext;
private final Executor ioExecutor, wakefulIoExecutor;
private final Application app;
private final NetworkManager networkManager;
private final LocationUtils locationUtils;
private final EventBus eventBus;
private final SocketFactory torSocketFactory;
private final BackoffFactory backoffFactory;
private final ResourceProvider resourceProvider;
private final CircumventionProvider circumventionProvider;
private final BatteryManager batteryManager;
private final AndroidWakeLockManager wakeLockManager;
private final Clock clock;
private final File torDirectory;
public AndroidTorPluginFactory(Executor ioExecutor,
ScheduledExecutorService scheduler, Context appContext,
NetworkManager networkManager, LocationUtils locationUtils,
EventBus eventBus, SocketFactory torSocketFactory,
@Inject
public AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor,
Application app,
NetworkManager networkManager,
LocationUtils locationUtils,
EventBus eventBus,
SocketFactory torSocketFactory,
BackoffFactory backoffFactory,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Clock clock) {
BatteryManager batteryManager,
AndroidWakeLockManager wakeLockManager,
Clock clock,
@TorDirectory File torDirectory) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.appContext = appContext;
this.wakefulIoExecutor = wakefulIoExecutor;
this.app = app;
this.networkManager = networkManager;
this.locationUtils = locationUtils;
this.eventBus = eventBus;
this.torSocketFactory = torSocketFactory;
this.backoffFactory = backoffFactory;
this.resourceProvider = resourceProvider;
this.circumventionProvider = circumventionProvider;
this.batteryManager = batteryManager;
this.wakeLockManager = wakeLockManager;
this.clock = clock;
this.torDirectory = torDirectory;
}
@Override
@@ -120,13 +125,15 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
// Use position-independent executable
architecture += "_pie";
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, scheduler,
appContext, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
torRendezvousCrypto, callback, architecture, MAX_LATENCY,
MAX_IDLE_TIME, INITIAL_POLLING_INTERVAL,
STABLE_POLLING_INTERVAL);
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor,
wakefulIoExecutor, app, networkManager, locationUtils,
torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, wakeLockManager,
backoff, torRendezvousCrypto, callback, architecture,
MAX_LATENCY, MAX_IDLE_TIME, torDirectory);
eventBus.addListener(plugin);
return plugin;
}

View File

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

View File

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

View File

@@ -1,12 +1,17 @@
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;
@@ -16,6 +21,23 @@ 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(
@@ -47,4 +69,11 @@ public class AndroidSystemModule {
ResourceProvider provideResourceProvider(AndroidResourceProvider provider) {
return provider;
}
@Provides
@Singleton
AndroidWakeLockManager provideWakeLockManager(
AndroidWakeLockManagerImpl wakeLockManager) {
return wakeLockManager;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Interface for strengthening a password-based key, for example by using a
* key stored in a key management service or hardware security module.
*
* TODO: Remove after a reasonable migration period unless we can work around
* Android keymaster bugs. Added 2020-02-24
*/
@NotNullByDefault
public interface KeyStrengthener {

View File

@@ -7,6 +7,10 @@ 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.

View File

@@ -5,6 +5,7 @@ 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;
@@ -65,6 +66,7 @@ public interface LifecycleManager {
* Opens the {@link DatabaseComponent} using the given key and starts any
* registered {@link Service Services}.
*/
@Wakeful
StartResult startServices(SecretKey dbKey);
/**
@@ -72,6 +74,7 @@ public interface LifecycleManager {
* registered {@link ExecutorService ExecutorServices}, and closes the
* {@link DatabaseComponent}.
*/
@Wakeful
void stopServices();
/**
@@ -104,6 +107,7 @@ public interface LifecycleManager {
*
* @param txn A read-write transaction
*/
@Wakeful
void onDatabaseOpened(Transaction txn) throws DbException;
}
}

View File

@@ -1,16 +1,20 @@
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;
}

View File

@@ -0,0 +1,22 @@
package org.briarproject.bramble.api.plugin;
/**
* Calculates polling intervals for transport plugins that use backoff.
*/
public interface Backoff {
/**
* Returns the current polling interval.
*/
int getPollingInterval();
/**
* Increments the backoff counter.
*/
void increment();
/**
* Resets the backoff counter.
*/
void reset();
}

View File

@@ -0,0 +1,7 @@
package org.briarproject.bramble.api.plugin;
public interface BackoffFactory {
Backoff createBackoff(int minInterval, int maxInterval,
double base);
}

View File

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

View File

@@ -4,6 +4,7 @@ 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;
@@ -70,11 +71,13 @@ public interface Plugin {
/**
* Starts the plugin.
*/
@Wakeful
void start() throws PluginException;
/**
* Stops the plugin.
*/
@Wakeful
void stop() throws PluginException;
/**
@@ -106,6 +109,7 @@ 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);
}

View File

@@ -8,6 +8,8 @@ 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.
@@ -25,6 +27,11 @@ 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
*/

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.api.system;
package org.briarproject.bramble.api.plugin;
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -11,15 +12,11 @@ import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 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!
* Annotation for injecting the {@link File directory} where the Tor plugin
* should store its state.
*/
@Qualifier
@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
public @interface Scheduler {
}
public @interface TorDirectory {
}

View File

@@ -8,6 +8,7 @@ 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;
@@ -21,6 +22,7 @@ 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);

View File

@@ -5,6 +5,7 @@ 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;
@@ -18,6 +19,7 @@ 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);
@@ -25,6 +27,7 @@ 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);
}

View File

@@ -12,6 +12,11 @@ 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.

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,6 @@ 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;
@@ -31,8 +30,6 @@ public interface BrambleCoreEagerSingletons {
void inject(RendezvousModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
void inject(ValidationModule.EagerSingletons init);
@@ -50,7 +47,6 @@ 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());

View File

@@ -21,7 +21,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.SystemModule;
import org.briarproject.bramble.system.ClockModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
@@ -29,6 +29,7 @@ import dagger.Module;
@Module(includes = {
ClientModule.class,
ClockModule.class,
ConnectionModule.class,
ContactModule.class,
CryptoModule.class,
@@ -48,7 +49,6 @@ import dagger.Module;
RendezvousModule.class,
SettingsModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class,
ValidationModule.class,
VersioningModule.class

View File

@@ -179,8 +179,9 @@ class AccountManagerImpl implements AccountManager {
@GuardedBy("stateChangeLock")
private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
byte[] plaintext = key.getBytes();
byte[] ciphertext = crypto.encryptWithPassword(plaintext, password,
databaseConfig.getKeyStrengthener());
// Don't use a key strengthener as the Android keymaster isn't reliable
byte[] ciphertext =
crypto.encryptWithPassword(plaintext, password, null);
return storeEncryptedDatabaseKey(toHexString(ciphertext));
}
@@ -197,13 +198,13 @@ class AccountManagerImpl implements AccountManager {
@Override
public void signIn(String password) throws DecryptionException {
synchronized (stateChangeLock) {
databaseKey = loadAndDecryptDatabaseKey(password);
databaseKey = loadAndDecryptDatabaseKey(password, false);
}
}
@GuardedBy("stateChangeLock")
private SecretKey loadAndDecryptDatabaseKey(String password)
throws DecryptionException {
private SecretKey loadAndDecryptDatabaseKey(String password,
boolean changing) throws DecryptionException {
String hex = loadEncryptedDatabaseKey();
if (hex == null) {
LOG.warning("Failed to load encrypted database key");
@@ -214,11 +215,11 @@ class AccountManagerImpl implements AccountManager {
byte[] plaintext = crypto.decryptWithPassword(ciphertext, password,
keyStrengthener);
SecretKey key = new SecretKey(plaintext);
// If the DB key was encrypted with a weak key and a key strengthener
// is now available, re-encrypt the DB key with a strengthened key
if (keyStrengthener != null &&
!crypto.isEncryptedWithStrengthenedKey(ciphertext)) {
LOG.info("Re-encrypting database key with strengthened key");
// If the DB key was encrypted with a hardware-backed key, re-encrypt
// it without the hardware-backed key so keymaster bugs don't delete
// the user's account
if (!changing && crypto.isEncryptedWithStrengthenedKey(ciphertext)) {
LOG.info("Re-encrypting database key without strengthened key");
encryptAndStoreDatabaseKey(key, password);
}
return key;
@@ -228,7 +229,7 @@ class AccountManagerImpl implements AccountManager {
public void changePassword(String oldPassword, String newPassword)
throws DecryptionException {
synchronized (stateChangeLock) {
SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
SecretKey key = loadAndDecryptDatabaseKey(oldPassword, true);
encryptAndStoreDatabaseKey(key, newPassword);
}
}

View File

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

View File

@@ -0,0 +1,18 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class BackoffFactoryImpl implements BackoffFactory {
@Override
public Backoff createBackoff(int minInterval, int maxInterval,
double base) {
return new BackoffImpl(minInterval, maxInterval, base);
}
}

View File

@@ -0,0 +1,42 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.concurrent.ThreadSafe;
@ThreadSafe
@NotNullByDefault
class BackoffImpl implements Backoff {
private final int minInterval, maxInterval;
private final double base;
private final AtomicInteger backoff;
BackoffImpl(int minInterval, int maxInterval, double base) {
this.minInterval = minInterval;
this.maxInterval = maxInterval;
this.base = base;
backoff = new AtomicInteger(0);
}
@Override
public int getPollingInterval() {
double multiplier = Math.pow(base, backoff.get());
// Large or infinite values will be rounded to Integer.MAX_VALUE
int interval = (int) (minInterval * multiplier);
return Math.min(interval, maxInterval);
}
@Override
public void increment() {
backoff.incrementAndGet();
}
@Override
public void reset() {
backoff.set(0);
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
@@ -28,6 +29,7 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.util.ArrayList;
import java.util.Collection;
@@ -44,6 +46,7 @@ import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.Collections.emptyList;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@@ -62,7 +65,7 @@ class PluginManagerImpl implements PluginManager, Service {
private static final Logger LOG =
getLogger(PluginManagerImpl.class.getName());
private final Executor ioExecutor;
private final Executor ioExecutor, wakefulIoExecutor;
private final EventBus eventBus;
private final PluginConfig pluginConfig;
private final ConnectionManager connectionManager;
@@ -75,11 +78,15 @@ class PluginManagerImpl implements PluginManager, Service {
private final AtomicBoolean used = new AtomicBoolean(false);
@Inject
PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus,
PluginConfig pluginConfig, ConnectionManager connectionManager,
PluginManagerImpl(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor,
EventBus eventBus,
PluginConfig pluginConfig,
ConnectionManager connectionManager,
SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.eventBus = eventBus;
this.pluginConfig = pluginConfig;
this.connectionManager = connectionManager;
@@ -107,7 +114,7 @@ class PluginManagerImpl implements PluginManager, Service {
simplexPlugins.add(s);
CountDownLatch startLatch = new CountDownLatch(1);
startLatches.put(t, startLatch);
ioExecutor.execute(new PluginStarter(s, startLatch));
wakefulIoExecutor.execute(new PluginStarter(s, startLatch));
}
}
// Instantiate the duplex plugins and start them asynchronously
@@ -123,7 +130,7 @@ class PluginManagerImpl implements PluginManager, Service {
duplexPlugins.add(d);
CountDownLatch startLatch = new CountDownLatch(1);
startLatches.put(t, startLatch);
ioExecutor.execute(new PluginStarter(d, startLatch));
wakefulIoExecutor.execute(new PluginStarter(d, startLatch));
}
}
}
@@ -135,12 +142,16 @@ class PluginManagerImpl implements PluginManager, Service {
LOG.info("Stopping simplex plugins");
for (SimplexPlugin s : simplexPlugins) {
CountDownLatch startLatch = startLatches.get(s.getId());
// Don't need the wakeful executor here as we wait for the plugin
// to stop before returning
ioExecutor.execute(new PluginStopper(s, startLatch, stopLatch));
}
// Stop the duplex plugins
LOG.info("Stopping duplex plugins");
for (DuplexPlugin d : duplexPlugins) {
CountDownLatch startLatch = startLatches.get(d.getId());
// Don't need the wakeful executor here as we wait for the plugin
// to stop before returning
ioExecutor.execute(new PluginStopper(d, startLatch, stopLatch));
}
// Wait for all the plugins to stop
@@ -203,7 +214,7 @@ class PluginManagerImpl implements PluginManager, Service {
}
}
private class PluginStarter implements Runnable {
private static class PluginStarter implements Runnable {
private final Plugin plugin;
private final CountDownLatch startLatch;
@@ -233,7 +244,7 @@ class PluginManagerImpl implements PluginManager, Service {
}
}
private class PluginStopper implements Runnable {
private static class PluginStopper implements Runnable {
private final Plugin plugin;
private final CountDownLatch startLatch, stopLatch;
@@ -303,6 +314,18 @@ class PluginManagerImpl implements PluginManager, Service {
}
}
@Override
public Collection<TransportProperties> getRemoteProperties() {
try {
Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(id);
return remote.values();
} catch (DbException e) {
logException(LOG, WARNING, e);
return emptyList();
}
}
@Override
public void mergeSettings(Settings s) {
PluginManagerImpl.this.mergeSettings(s, id.getString());

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginManager;
@@ -21,6 +22,11 @@ public class PluginModule {
Poller poller;
}
@Provides
BackoffFactory provideBackoffFactory() {
return new BackoffFactoryImpl();
}
@Provides
@Singleton
PluginManager providePluginManager(LifecycleManager lifecycleManager,

View File

@@ -19,13 +19,17 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
import org.briarproject.bramble.api.system.Wakeful;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.security.SecureRandom;
import java.util.ArrayList;
@@ -34,8 +38,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
@@ -56,8 +58,8 @@ class PollerImpl implements Poller, EventListener {
private static final Logger LOG = getLogger(PollerImpl.class.getName());
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final Executor ioExecutor, wakefulIoExecutor;
private final TaskScheduler scheduler;
private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final PluginManager pluginManager;
@@ -70,12 +72,16 @@ class PollerImpl implements Poller, EventListener {
@Inject
PollerImpl(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
@WakefulIoExecutor Executor wakefulIoExecutor,
TaskScheduler scheduler,
ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager,
ConnectionRegistry connectionRegistry,
PluginManager pluginManager,
TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock) {
SecureRandom random,
Clock clock) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.scheduler = scheduler;
this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
@@ -95,10 +101,16 @@ class PollerImpl implements Poller, EventListener {
connectToContact(c.getContactId());
} else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
// If an outgoing connection failed, try to reconnect
if (!c.isIncoming() && c.isException()) {
connectToContact(c.getContactId(), c.getTransportId());
}
} else if (e instanceof ConnectionOpenedEvent) {
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
} else if (e instanceof TransportActiveEvent) {
TransportActiveEvent t = (TransportActiveEvent) e;
// Poll the newly activated transport
@@ -126,7 +138,7 @@ class PollerImpl implements Poller, EventListener {
}
private void connectToContact(ContactId c, SimplexPlugin p) {
ioExecutor.execute(() -> {
wakefulIoExecutor.execute(() -> {
TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return;
try {
@@ -142,7 +154,7 @@ class PollerImpl implements Poller, EventListener {
}
private void connectToContact(ContactId c, DuplexPlugin p) {
ioExecutor.execute(() -> {
wakefulIoExecutor.execute(() -> {
TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return;
try {
@@ -157,6 +169,12 @@ class PollerImpl implements Poller, EventListener {
});
}
private void reschedule(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p != null && p.shouldPoll())
schedule(p, p.getPollingInterval(), false);
}
private void pollNow(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
// Randomise next polling interval
@@ -173,11 +191,11 @@ class PollerImpl implements Poller, EventListener {
if (scheduled == null || due < scheduled.task.due) {
// If a later task exists, cancel it. If it's already started
// it will abort safely when it finds it's been replaced
if (scheduled != null) scheduled.future.cancel(false);
if (scheduled != null) scheduled.cancellable.cancel();
PollTask task = new PollTask(p, due, randomiseNext);
Future future = scheduler.schedule(() ->
ioExecutor.execute(task), delay, MILLISECONDS);
tasks.put(t, new ScheduledPollTask(task, future));
Cancellable cancellable = scheduler.schedule(task, ioExecutor,
delay, MILLISECONDS);
tasks.put(t, new ScheduledPollTask(task, cancellable));
}
} finally {
lock.unlock();
@@ -188,7 +206,7 @@ class PollerImpl implements Poller, EventListener {
lock.lock();
try {
ScheduledPollTask scheduled = tasks.remove(t);
if (scheduled != null) scheduled.future.cancel(false);
if (scheduled != null) scheduled.cancellable.cancel();
} finally {
lock.unlock();
}
@@ -219,11 +237,11 @@ class PollerImpl implements Poller, EventListener {
private class ScheduledPollTask {
private final PollTask task;
private final Future future;
private final Cancellable cancellable;
private ScheduledPollTask(PollTask task, Future future) {
private ScheduledPollTask(PollTask task, Cancellable cancellable) {
this.task = task;
this.future = future;
this.cancellable = cancellable;
}
}
@@ -241,6 +259,7 @@ class PollerImpl implements Poller, EventListener {
@Override
@IoExecutor
@Wakeful
public void run() {
lock.lock();
try {

View File

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

View File

@@ -1,11 +1,11 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Multiset;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginException;
@@ -24,6 +25,7 @@ import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
import org.briarproject.bramble.api.plugin.event.DisableBluetoothEvent;
import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.event.RemoteTransportPropertiesUpdatedEvent;
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
import org.briarproject.bramble.api.settings.Settings;
@@ -45,8 +47,12 @@ 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.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_ADDRESS_IS_REFLECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_EVER_CONNECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_ADDRESS_IS_REFLECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_EVER_CONNECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
@@ -54,6 +60,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.REFLECTED_PROPERTY_PREFIX;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@@ -62,19 +69,21 @@ import static org.briarproject.bramble.util.StringUtils.macToString;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
private static final Logger LOG =
getLogger(BluetoothPlugin.class.getName());
final BluetoothConnectionLimiter connectionLimiter;
final TimeoutMonitor timeoutMonitor;
final BluetoothConnectionFactory<S> connectionFactory;
private final Executor ioExecutor;
private final Executor ioExecutor, wakefulIoExecutor;
private final SecureRandom secureRandom;
private final Backoff backoff;
private final PluginCallback callback;
private final int maxLatency, maxIdleTime, pollingInterval;
private final int maxLatency, maxIdleTime;
private final AtomicBoolean used = new AtomicBoolean(false);
private final AtomicBoolean everConnected = new AtomicBoolean(false);
protected final PluginState state = new PluginState();
@@ -113,17 +122,23 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection discoverAndConnect(String uuid);
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor, Executor ioExecutor,
SecureRandom secureRandom, PluginCallback callback, int maxLatency, int maxIdleTime,
int pollingInterval) {
BluetoothConnectionFactory<S> connectionFactory,
Executor ioExecutor,
Executor wakefulIoExecutor,
SecureRandom secureRandom,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime) {
this.connectionLimiter = connectionLimiter;
this.timeoutMonitor = timeoutMonitor;
this.connectionFactory = connectionFactory;
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.secureRandom = secureRandom;
this.backoff = backoff;
this.callback = callback;
this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime;
this.pollingInterval = pollingInterval;
}
void onAdapterEnabled() {
@@ -165,6 +180,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
Settings settings = callback.getSettings();
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE,
DEFAULT_PREF_PLUGIN_ENABLE);
everConnected.set(settings.getBoolean(PREF_EVER_CONNECTED,
DEFAULT_PREF_EVER_CONNECTED));
state.setStarted(enabledByUser);
try {
initialiseAdapter();
@@ -194,6 +211,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
tryToClose(ss);
return;
}
backoff.reset();
acceptContactConnections(ss);
});
}
@@ -202,25 +220,68 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
TransportProperties p = callback.getLocalProperties();
String address = p.get(PROP_ADDRESS);
String uuid = p.get(PROP_UUID);
Settings s = callback.getSettings();
boolean isReflected = s.getBoolean(PREF_ADDRESS_IS_REFLECTED,
DEFAULT_PREF_ADDRESS_IS_REFLECTED);
boolean changed = false;
if (address == null) {
if (address == null || isReflected) {
address = getBluetoothAddress();
if (LOG.isLoggable(INFO))
if (LOG.isLoggable(INFO)) {
LOG.info("Local address " + scrubMacAddress(address));
if (!isNullOrEmpty(address)) {
p.put(PROP_ADDRESS, address);
}
if (address == null) {
if (everConnected.get()) {
address = getReflectedAddress();
if (LOG.isLoggable(INFO)) {
LOG.info("Reflected address " +
scrubMacAddress(address));
}
if (address != null) {
changed = true;
isReflected = true;
}
}
} else {
changed = true;
isReflected = false;
}
}
if (uuid == null) {
byte[] random = new byte[UUID_BYTES];
secureRandom.nextBytes(random);
uuid = UUID.nameUUIDFromBytes(random).toString();
p.put(PROP_UUID, uuid);
changed = true;
}
contactConnectionsUuid = uuid;
if (changed) callback.mergeLocalProperties(p);
if (changed) {
p = new TransportProperties();
// If we previously used a reflected address and there's no longer
// a reflected address with enough votes to be used, we'll continue
// to use the old reflected address until there's a new winner
if (address != null) p.put(PROP_ADDRESS, address);
p.put(PROP_UUID, uuid);
callback.mergeLocalProperties(p);
s = new Settings();
s.putBoolean(PREF_ADDRESS_IS_REFLECTED, isReflected);
callback.mergeSettings(s);
}
}
@Nullable
private String getReflectedAddress() {
// Count the number of votes for each reflected address
String key = REFLECTED_PROPERTY_PREFIX + PROP_ADDRESS;
Multiset<String> votes = new Multiset<>();
for (TransportProperties p : callback.getRemoteProperties()) {
String address = p.get(key);
if (address != null && isValidAddress(address)) votes.add(address);
}
// If an address gets more than half of the votes, accept it
int total = votes.getTotal();
for (String address : votes.keySet()) {
if (votes.getCount(address) * 2 > total) return address;
}
return null;
}
private void acceptContactConnections(SS ss) {
@@ -236,10 +297,24 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
}
LOG.info("Connection received");
connectionLimiter.connectionOpened(conn);
backoff.reset();
setEverConnected();
callback.handleConnection(conn);
}
}
private void setEverConnected() {
if (!everConnected.getAndSet(true)) {
ioExecutor.execute(() -> {
Settings s = new Settings();
s.putBoolean(PREF_EVER_CONNECTED, true);
callback.mergeSettings(s);
// Contacts may already have sent a reflected address
updateProperties();
});
}
}
@Override
public void stop() {
SS ss = state.setStopped();
@@ -264,13 +339,14 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override
public int getPollingInterval() {
return pollingInterval;
return backoff.getPollingInterval();
}
@Override
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
properties) {
if (getState() != ACTIVE) return;
backoff.increment();
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
connect(p.getFirst(), p.getSecond());
}
@@ -281,9 +357,13 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (isNullOrEmpty(address)) return;
String uuid = p.get(PROP_UUID);
if (isNullOrEmpty(uuid)) return;
ioExecutor.execute(() -> {
wakefulIoExecutor.execute(() -> {
DuplexTransportConnection d = createConnection(p);
if (d != null) h.handleConnection(d);
if (d != null) {
backoff.reset();
setEverConnected();
h.handleConnection(d);
}
});
}
@@ -384,7 +464,10 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
LOG.info("Connecting to key agreement UUID " + uuid);
conn = connect(address, uuid);
}
if (conn != null) connectionLimiter.connectionOpened(conn);
if (conn != null) {
connectionLimiter.connectionOpened(conn);
setEverConnected();
}
return conn;
}
@@ -421,6 +504,12 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
ioExecutor.execute(connectionLimiter::keyAgreementStarted);
} else if (e instanceof KeyAgreementStoppedListeningEvent) {
ioExecutor.execute(connectionLimiter::keyAgreementEnded);
} else if (e instanceof RemoteTransportPropertiesUpdatedEvent) {
RemoteTransportPropertiesUpdatedEvent r =
(RemoteTransportPropertiesUpdatedEvent) e;
if (r.getTransportId().equals(ID)) {
ioExecutor.execute(this::updateProperties);
}
}
}

View File

@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
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.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@@ -87,10 +88,15 @@ class LanTcpPlugin extends TcpPlugin {
}
}
LanTcpPlugin(Executor ioExecutor, PluginCallback callback, int maxLatency,
int maxIdleTime, int pollingInterval, int connectionTimeout) {
super(ioExecutor, callback, maxLatency, maxIdleTime, pollingInterval,
connectionTimeout);
LanTcpPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime,
int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,
maxIdleTime, connectionTimeout);
}
@Override
@@ -348,7 +354,7 @@ class LanTcpPlugin extends TcpPlugin {
tryToClose(ss, LOG, WARNING);
}
}
if (ss == null) {
if (ss == null || !ss.isBound()) {
LOG.info("Could not bind server socket for key agreement");
return null;
}

View File

@@ -1,35 +1,47 @@
package org.briarproject.bramble.plugin.tcp;
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;
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 java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@Immutable
@NotNullByDefault
public class LanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = (int) SECONDS.toMillis(30);
private static final int MAX_IDLE_TIME = (int) SECONDS.toMillis(30);
private static final int POLLING_INTERVAL = (int) MINUTES.toMillis(1);
private static final int CONNECTION_TIMEOUT = (int) SECONDS.toMillis(3);
private static final int MAX_LATENCY = 30_000; // 30 seconds
private static final int MAX_IDLE_TIME = 30_000; // 30 seconds
private static final int CONNECTION_TIMEOUT = 3_000; // 3 seconds
private static final int MIN_POLLING_INTERVAL = 60_000; // 1 minute
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor;
private final Executor ioExecutor, wakefulIoExecutor;
private final EventBus eventBus;
private final BackoffFactory backoffFactory;
public LanTcpPluginFactory(Executor ioExecutor, EventBus eventBus) {
@Inject
public LanTcpPluginFactory(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor,
EventBus eventBus,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.eventBus = eventBus;
this.backoffFactory = backoffFactory;
}
@Override
@@ -44,8 +56,10 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
@Override
public DuplexPlugin createPlugin(PluginCallback callback) {
LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, callback,
MAX_LATENCY, MAX_IDLE_TIME, POLLING_INTERVAL,
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, wakefulIoExecutor,
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME,
CONNECTION_TIMEOUT);
eventBus.addListener(plugin);
return plugin;

View File

@@ -10,6 +10,7 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
@@ -65,9 +66,10 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
private static final Pattern DOTTED_QUAD =
Pattern.compile("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$");
protected final Executor ioExecutor, bindExecutor;
protected final Executor ioExecutor, wakefulIoExecutor, bindExecutor;
protected final Backoff backoff;
protected final PluginCallback callback;
protected final int maxLatency, maxIdleTime, pollingInterval;
protected final int maxLatency, maxIdleTime;
protected final int connectionTimeout, socketTimeout;
protected final AtomicBoolean used = new AtomicBoolean(false);
protected final PluginState state = new PluginState();
@@ -105,13 +107,19 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
*/
protected abstract boolean isEnabledByDefault();
TcpPlugin(Executor ioExecutor, PluginCallback callback, int maxLatency,
int maxIdleTime, int pollingInterval, int connectionTimeout) {
TcpPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime,
int connectionTimeout) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.backoff = backoff;
this.callback = callback;
this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime;
this.pollingInterval = pollingInterval;
this.connectionTimeout = connectionTimeout;
if (maxIdleTime > Integer.MAX_VALUE / 2)
socketTimeout = Integer.MAX_VALUE;
@@ -141,15 +149,21 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
protected void bind() {
bindExecutor.execute(() -> {
if (getState() != INACTIVE) return;
State s = getState();
if (s != ACTIVE && s != INACTIVE) return;
bind(true);
bind(false);
});
}
private void bind(boolean ipv4) {
ServerSocket old = state.getServerSocket(ipv4);
ServerSocket ss = null;
for (InetSocketAddress addr : getLocalSocketAddresses(ipv4)) {
if (old != null && addr.equals(old.getLocalSocketAddress())) {
LOG.info("Server socket already bound");
return;
}
try {
ss = new ServerSocket();
ss.bind(addr);
@@ -160,7 +174,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
tryToClose(ss, LOG, WARNING);
}
}
if (ss == null) {
if (ss == null || !ss.isBound()) {
LOG.info("Could not bind server socket");
return;
}
@@ -169,6 +183,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
tryToClose(ss, LOG, WARNING);
return;
}
backoff.reset();
InetSocketAddress local =
(InetSocketAddress) ss.getLocalSocketAddress();
setLocalSocketAddress(local, ipv4);
@@ -201,6 +216,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
LOG.info("Connection from " +
scrubSocketAddress(s.getRemoteSocketAddress()));
}
backoff.reset();
callback.handleConnection(new TcpTransportConnection(this, s));
}
}
@@ -227,22 +243,26 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
@Override
public int getPollingInterval() {
return pollingInterval;
return backoff.getPollingInterval();
}
@Override
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
properties) {
if (getState() != ACTIVE) return;
backoff.increment();
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
connect(p.getFirst(), p.getSecond());
}
}
private void connect(TransportProperties p, ConnectionHandler h) {
ioExecutor.execute(() -> {
wakefulIoExecutor.execute(() -> {
DuplexTransportConnection d = createConnection(p);
if (d != null) h.handleConnection(d);
if (d != null) {
backoff.reset();
h.handleConnection(d);
}
});
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin.tcp;
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.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
@@ -29,11 +30,16 @@ class WanTcpPlugin extends TcpPlugin {
private volatile MappingResult mappingResult;
WanTcpPlugin(Executor ioExecutor, PortMapper portMapper,
PluginCallback callback, int maxLatency, int maxIdleTime,
int pollingInterval, int connectionTimeout) {
super(ioExecutor, callback, maxLatency, maxIdleTime, pollingInterval,
connectionTimeout);
WanTcpPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
Backoff backoff,
PortMapper portMapper,
PluginCallback callback,
int maxLatency,
int maxIdleTime,
int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,
maxIdleTime, connectionTimeout);
this.portMapper = portMapper;
}

View File

@@ -1,38 +1,50 @@
package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.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.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 java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.plugin.WanTcpConstants.ID;
@Immutable
@NotNullByDefault
public class WanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = (int) SECONDS.toMillis(30);
private static final int MAX_IDLE_TIME = (int) SECONDS.toMillis(30);
private static final int POLLING_INTERVAL = (int) MINUTES.toMillis(1);
private static final int CONNECTION_TIMEOUT = (int) SECONDS.toMillis(30);
private static final int MAX_LATENCY = 30_000; // 30 seconds
private static final int MAX_IDLE_TIME = 30_000; // 30 seconds
private static final int CONNECTION_TIMEOUT = 30_000; // 30 seconds
private static final int MIN_POLLING_INTERVAL = 60_000; // 1 minute
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor;
private final Executor ioExecutor, wakefulIoExecutor;
private final EventBus eventBus;
private final BackoffFactory backoffFactory;
private final ShutdownManager shutdownManager;
public WanTcpPluginFactory(Executor ioExecutor, EventBus eventBus,
@Inject
public WanTcpPluginFactory(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor,
EventBus eventBus,
BackoffFactory backoffFactory,
ShutdownManager shutdownManager) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.eventBus = eventBus;
this.backoffFactory = backoffFactory;
this.shutdownManager = shutdownManager;
}
@@ -48,9 +60,12 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
@Override
public DuplexPlugin createPlugin(PluginCallback callback) {
WanTcpPlugin plugin = new WanTcpPlugin(ioExecutor,
new PortMapperImpl(shutdownManager), callback, MAX_LATENCY,
MAX_IDLE_TIME, POLLING_INTERVAL, CONNECTION_TIMEOUT);
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
PortMapper portMapper = new PortMapperImpl(shutdownManager);
WanTcpPlugin plugin = new WanTcpPlugin(ioExecutor, wakefulIoExecutor,
backoff, portMapper, callback, MAX_LATENCY, MAX_IDLE_TIME,
CONNECTION_TIMEOUT);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -17,6 +17,7 @@ import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginException;
@@ -45,11 +46,9 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@@ -110,59 +109,29 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final Logger LOG = getLogger(TorPlugin.class.getName());
/**
* Controller events we want to receive.
*/
private static final String[] EVENTS = {
"CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR"
};
/**
* Command-line argument to set our process as Tor's owning controller
* so Tor exits when our process dies.
*/
private static final String OWNER = "__OwningControllerProcess";
/**
* How long to wait for the authentication cookie file to be created.
*/
private static final int COOKIE_TIMEOUT_MS = 3000;
/**
* How often to check whether the authentication cookie file has been
* created.
*/
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
/**
* Regex for matching v2 hidden service names.
*/
private static final Pattern ONION_V2 = Pattern.compile("[a-z2-7]{16}");
/**
* Regex for matching v3 hidden service names.
*/
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
/**
* How many copies of our descriptor must be uploaded before we consider
* our hidden service to be reachable and switch to less frequent polling.
*/
private static final int MIN_DESCRIPTORS_UPLOADED = 5;
private final Executor ioExecutor, connectionStatusExecutor;
private final Executor ioExecutor, wakefulIoExecutor;
private final Executor connectionStatusExecutor;
private final NetworkManager networkManager;
private final LocationUtils locationUtils;
private final SocketFactory torSocketFactory;
private final Clock clock;
private final BatteryManager batteryManager;
private final Backoff backoff;
private final TorRendezvousCrypto torRendezvousCrypto;
private final PluginCallback callback;
private final String architecture;
private final CircumventionProvider circumventionProvider;
private final ResourceProvider resourceProvider;
private final int maxLatency, maxIdleTime, socketTimeout;
private final int initialPollingInterval, stablePollingInterval;
private final File torDirectory, torFile, geoIpFile, obfs4File, configFile;
private final File doneFile, cookieFile;
private final AtomicBoolean used = new AtomicBoolean(false);
@@ -177,16 +146,24 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
protected abstract long getLastUpdateTime();
TorPlugin(Executor ioExecutor, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory,
Clock clock, ResourceProvider resourceProvider,
TorPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager,
Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, int initialPollingInterval,
int stablePollingInterval, File torDirectory) {
PluginCallback callback,
String architecture,
int maxLatency,
int maxIdleTime,
File torDirectory) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.networkManager = networkManager;
this.locationUtils = locationUtils;
this.torSocketFactory = torSocketFactory;
@@ -194,6 +171,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
this.resourceProvider = resourceProvider;
this.circumventionProvider = circumventionProvider;
this.batteryManager = batteryManager;
this.backoff = backoff;
this.torRendezvousCrypto = torRendezvousCrypto;
this.callback = callback;
this.architecture = architecture;
@@ -202,8 +180,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (maxIdleTime > Integer.MAX_VALUE / 2)
socketTimeout = Integer.MAX_VALUE;
else socketTimeout = maxIdleTime * 2;
this.initialPollingInterval = initialPollingInterval;
this.stablePollingInterval = stablePollingInterval;
this.torDirectory = torDirectory;
torFile = new File(torDirectory, "tor");
geoIpFile = new File(torDirectory, "geoip");
@@ -468,6 +444,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
callback.mergeSettings(s);
// Create a hidden service if necessary
ioExecutor.execute(() -> publishHiddenService(localPort));
backoff.reset();
// Accept incoming hidden service connections from Tor
acceptContactConnections(ss);
});
@@ -485,7 +462,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
// in the migration period since first publishing it
if (!isNullOrEmpty(privKey2)) {
long now = clock.currentTimeMillis();
long then = v3Created == null ? now : Long.valueOf(v3Created);
long then = v3Created == null ? now : Long.parseLong(v3Created);
if (now - then >= V3_MIGRATION_PERIOD_MS) retireV2HiddenService();
else publishV2HiddenService(port, privKey2);
}
@@ -547,15 +524,15 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
LOG.warning("Tor did not return a private key");
return;
}
// Publish the hidden service's onion hostname in transport properties
String onion3 = response.get(HS_ADDRESS);
if (LOG.isLoggable(INFO)) {
LOG.info("V3 hidden service " + scrubOnion(onion3));
}
TransportProperties p = new TransportProperties();
p.put(PROP_ONION_V3, onion3);
callback.mergeLocalProperties(p);
if (privKey == null) {
// Publish the hidden service's onion hostname in transport props
TransportProperties p = new TransportProperties();
p.put(PROP_ONION_V3, onion3);
callback.mergeLocalProperties(p);
// Save the hidden service's private key for next time
Settings s = new Settings();
s.put(HS_PRIVATE_KEY_V3, response.get(HS_PRIVKEY));
@@ -577,6 +554,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return;
}
LOG.info("Connection received");
backoff.reset();
callback.handleConnection(new TorTransportConnection(this, s));
}
}
@@ -638,28 +616,26 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public int getPollingInterval() {
if (state.isDescriptorPublished()) {
LOG.info("Using stable polling interval");
return stablePollingInterval;
} else {
LOG.info("Using initial polling interval");
return initialPollingInterval;
}
return backoff.getPollingInterval();
}
@Override
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
properties) {
if (getState() != ACTIVE) return;
backoff.increment();
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
connect(p.getFirst(), p.getSecond());
}
}
private void connect(TransportProperties p, ConnectionHandler h) {
ioExecutor.execute(() -> {
wakefulIoExecutor.execute(() -> {
DuplexTransportConnection d = createConnection(p);
if (d != null) h.handleConnection(d);
if (d != null) {
backoff.reset();
h.handleConnection(d);
}
});
}
@@ -789,8 +765,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public void circuitStatus(String status, String id, String path) {
if (status.equals("BUILT") && state.getAndSetCircuitBuilt()) {
if (status.equals("BUILT") &&
state.getAndSetCircuitBuilt()) {
LOG.info("First circuit built");
backoff.reset();
}
}
@@ -800,8 +778,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public void orConnStatus(String status, String orName) {
if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status);
state.setOrConnectionStatus(orName, status);
if (LOG.isLoggable(INFO))
LOG.info("OR connection " + status + " " + orName);
if (status.equals("CLOSED") || status.equals("FAILED")) {
// Check whether we've lost connectivity
updateConnectionStatus(networkManager.getNetworkStatus(),
@@ -822,18 +800,20 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) {
state.setBootstrapped();
backoff.reset();
}
}
@Override
public void unrecognized(String type, String msg) {
if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) {
String[] words = msg.split(" ");
if (words.length > 1 && ONION_V3.matcher(words[1]).matches()) {
LOG.info("V3 descriptor uploaded");
state.descriptorUploaded();
} else {
LOG.info("V2 descriptor uploaded");
if (LOG.isLoggable(INFO)) {
String[] words = msg.split(" ");
if (words.length > 1 && ONION_V3.matcher(words[1]).matches()) {
LOG.info("V3 descriptor uploaded");
} else {
LOG.info("V2 descriptor uploaded");
}
}
}
}
@@ -989,12 +969,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Nullable
private ServerSocket serverSocket = null;
@GuardedBy("this")
private final Set<String> orConnections = new HashSet<>();
@GuardedBy("this")
private int descriptorsUploaded = 0;
synchronized void setStarted() {
started = true;
callback.pluginStateChanged(getState());
@@ -1028,10 +1002,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
synchronized void enableNetwork(boolean enable) {
networkInitialised = true;
networkEnabled = enable;
if (!enable) {
circuitBuilt = false;
descriptorsUploaded = 0;
}
if (!enable) circuitBuilt = false;
callback.pluginStateChanged(getState());
}
@@ -1041,34 +1012,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
callback.pluginStateChanged(getState());
}
synchronized void setOrConnectionStatus(String orName, String status) {
if (status.equals("CONNECTED")) {
if (!orConnections.add(orName)) {
LOG.warning("Duplicate OR connection");
}
} else {
orConnections.remove(orName);
if (orConnections.isEmpty()) descriptorsUploaded = 0;
}
if (LOG.isLoggable(INFO)) {
LOG.info(orConnections.size() + " OR connections");
}
callback.pluginStateChanged(getState());
}
// Doesn't affect getState()
synchronized void descriptorUploaded() {
if (networkEnabled && !orConnections.isEmpty()) {
descriptorsUploaded++;
} else {
LOG.warning("Descriptor was uploaded with no OR connection");
}
}
synchronized boolean isDescriptorPublished() {
return descriptorsUploaded >= MIN_DESCRIPTORS_UPLOADED;
}
// Doesn't affect getState()
synchronized boolean setServerSocket(ServerSocket ss) {
if (stopped || serverSocket != null) return false;
@@ -1088,8 +1031,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (reasonsDisabled != 0) return DISABLED;
if (!networkInitialised) return ENABLING;
if (!networkEnabled) return INACTIVE;
return bootstrapped && circuitBuilt && !orConnections.isEmpty()
? ACTIVE : ENABLING;
return bootstrapped && circuitBuilt ? ACTIVE : ENABLING;
}
synchronized int getReasonsDisabled() {

View File

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

View File

@@ -41,7 +41,8 @@ import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedE
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousPollEvent;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.api.system.Wakeful;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
@@ -52,7 +53,6 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@@ -80,7 +80,7 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
private static final Logger LOG =
getLogger(RendezvousPollerImpl.class.getName());
private final ScheduledExecutorService scheduler;
private final TaskScheduler scheduler;
private final DatabaseComponent db;
private final IdentityManager identityManager;
private final TransportCrypto transportCrypto;
@@ -105,7 +105,7 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
@Inject
RendezvousPollerImpl(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
TaskScheduler scheduler,
DatabaseComponent db,
IdentityManager identityManager,
TransportCrypto transportCrypto,
@@ -144,8 +144,8 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
} catch (DbException e) {
throw new ServiceException(e);
}
scheduler.scheduleAtFixedRate(this::poll, POLLING_INTERVAL_MS,
POLLING_INTERVAL_MS, MILLISECONDS);
scheduler.scheduleWithFixedDelay(this::poll, worker,
POLLING_INTERVAL_MS, POLLING_INTERVAL_MS, MILLISECONDS);
}
@EventExecutor
@@ -205,12 +205,11 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
return plugin.createRendezvousEndpoint(k, cs.alice, h);
}
@Scheduler
// Worker
@Wakeful
private void poll() {
worker.execute(() -> {
removeExpiredPendingContacts();
for (PluginState ps : pluginStates.values()) poll(ps);
});
removeExpiredPendingContacts();
for (PluginState ps : pluginStates.values()) poll(ps);
}
// Worker
@@ -238,6 +237,7 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
}
// Worker
@Wakeful
private void poll(PluginState ps) {
if (ps.endpoints.isEmpty()) return;
TransportId t = ps.plugin.getId();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -134,7 +134,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
keyStrengthener);
will(returnValue(key.getBytes()));
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey);
will(returnValue(true));
will(returnValue(false));
}});
storeDatabaseKey(keyFile, encryptedKeyHex);
@@ -160,9 +160,8 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
keyStrengthener);
will(returnValue(key.getBytes()));
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey);
will(returnValue(false));
oneOf(crypto).encryptWithPassword(key.getBytes(), password,
keyStrengthener);
will(returnValue(true));
oneOf(crypto).encryptWithPassword(key.getBytes(), password, null);
will(returnValue(newEncryptedKey));
}});
@@ -262,8 +261,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
oneOf(identityManager).registerIdentity(identity);
oneOf(crypto).generateSecretKey();
will(returnValue(key));
oneOf(crypto).encryptWithPassword(key.getBytes(), password,
keyStrengthener);
oneOf(crypto).encryptWithPassword(key.getBytes(), password, null);
will(returnValue(encryptedKey));
}});
@@ -323,10 +321,8 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
oneOf(crypto).decryptWithPassword(encryptedKey, password,
keyStrengthener);
will(returnValue(key.getBytes()));
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey);
will(returnValue(true));
oneOf(crypto).encryptWithPassword(key.getBytes(), newPassword,
keyStrengthener);
null);
will(returnValue(newEncryptedKey));
}});

View File

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

View File

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

View File

@@ -0,0 +1,63 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class BackoffImplTest extends BrambleTestCase {
private static final int MIN_INTERVAL = 60 * 1000;
private static final int MAX_INTERVAL = 60 * 60 * 1000;
private static final double BASE = 1.2;
@Test
public void testPollingIntervalStartsAtMinimum() {
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
assertEquals(MIN_INTERVAL, b.getPollingInterval());
}
@Test
public void testIncrementIncreasesPollingInterval() {
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
b.increment();
assertTrue(b.getPollingInterval() > MIN_INTERVAL);
}
@Test
public void testResetResetsPollingInterval() {
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
b.increment();
b.increment();
b.reset();
assertEquals(MIN_INTERVAL, b.getPollingInterval());
}
@Test
public void testBaseAffectsBackoffSpeed() {
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
b.increment();
int interval = b.getPollingInterval();
BackoffImpl b1 = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE * 2);
b1.increment();
int interval1 = b1.getPollingInterval();
assertTrue(interval < interval1);
}
@Test
public void testIntervalDoesNotExceedMaxInterval() {
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
for (int i = 0; i < 100; i++) b.increment();
assertEquals(MAX_INTERVAL, b.getPollingInterval());
}
@Test
public void testIntervalDoesNotExceedMaxIntervalWithInfiniteMultiplier() {
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL,
Double.POSITIVE_INFINITY);
b.increment();
assertEquals(MAX_INTERVAL, b.getPollingInterval());
}
}

View File

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

View File

@@ -12,12 +12,15 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.RunAction;
@@ -29,8 +32,6 @@ import org.junit.Test;
import java.security.SecureRandom;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
@@ -44,8 +45,7 @@ import static org.briarproject.bramble.test.TestUtils.getTransportId;
public class PollerImplTest extends BrambleMockTestCase {
private final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
private final TaskScheduler scheduler = context.mock(TaskScheduler.class);
private final ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
private final ConnectionRegistry connectionRegistry =
@@ -55,10 +55,11 @@ public class PollerImplTest extends BrambleMockTestCase {
private final TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class);
private final Clock clock = context.mock(Clock.class);
private final ScheduledFuture future = context.mock(ScheduledFuture.class);
private final Cancellable cancellable = context.mock(Cancellable.class);
private final SecureRandom random;
private final Executor ioExecutor = new ImmediateExecutor();
private final Executor wakefulIoExecutor = new ImmediateExecutor();
private final TransportId transportId = getTransportId();
private final ContactId contactId = getContactId();
private final TransportProperties properties = new TransportProperties();
@@ -74,9 +75,9 @@ public class PollerImplTest extends BrambleMockTestCase {
@Before
public void setUp() {
poller = new PollerImpl(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
poller = new PollerImpl(ioExecutor, wakefulIoExecutor, scheduler,
connectionManager, connectionRegistry, pluginManager,
transportPropertyManager, random, clock);
}
@Test
@@ -156,20 +157,22 @@ public class PollerImplTest extends BrambleMockTestCase {
}
@Test
public void testDoesNotReconnectOnOutgoingConnectionClosed() {
public void testRescheduleOnOutgoingConnectionClosed() {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
}});
expectReschedule(plugin);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
false, false));
}
@Test
public void testReconnectsOnOutgoingConnectionFailed() throws Exception {
public void testRescheduleAndReconnectOnOutgoingConnectionFailed()
throws Exception {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
DuplexTransportConnection duplexConnection =
context.mock(DuplexTransportConnection.class);
@@ -178,6 +181,7 @@ public class PollerImplTest extends BrambleMockTestCase {
allowing(plugin).getId();
will(returnValue(transportId));
}});
expectReschedule(plugin);
expectReconnect(plugin, duplexConnection);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
@@ -185,31 +189,151 @@ public class PollerImplTest extends BrambleMockTestCase {
}
@Test
public void testDoesNotReconnectOnIncomingConnectionClosed() {
public void testRescheduleOnIncomingConnectionClosed() {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
}});
expectReschedule(plugin);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
true, false));
}
@Test
public void testDoesNotReconnectOnIncomingConnectionFailed() {
public void testRescheduleOnIncomingConnectionFailed() {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
}});
expectReschedule(plugin);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
true, false));
}
@Test
public void testRescheduleOnConnectionOpened() {
Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) pollingInterval),
with(MILLISECONDS));
will(returnValue(cancellable));
}});
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false));
}
@Test
public void testRescheduleDoesNotReplaceEarlierTask() {
Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
// First event
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) pollingInterval),
with(MILLISECONDS));
will(returnValue(cancellable));
// Second event
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Don't replace the previously scheduled task, due earlier
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now + 1));
}});
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false));
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false));
}
@Test
public void testRescheduleReplacesLaterTask() {
Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
// First event
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) pollingInterval),
with(MILLISECONDS));
will(returnValue(cancellable));
// Second event
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Replace the previously scheduled task, due later
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval - 2));
oneOf(clock).currentTimeMillis();
will(returnValue(now + 1));
oneOf(cancellable).cancel();
oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) pollingInterval - 2),
with(MILLISECONDS));
}});
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false));
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false));
}
@Test
public void testPollsOnTransportActivated() throws Exception {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
@@ -226,9 +350,9 @@ public class PollerImplTest extends BrambleMockTestCase {
// Schedule a polling task immediately
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L),
with(MILLISECONDS));
will(returnValue(future));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with(0L), with(MILLISECONDS));
will(returnValue(cancellable));
will(new RunAction());
// Running the polling task schedules the next polling task
oneOf(plugin).getPollingInterval();
@@ -238,8 +362,9 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
will(returnValue(future));
with(ioExecutor), with((long) (pollingInterval * 0.5)),
with(MILLISECONDS));
will(returnValue(cancellable));
// Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties)));
@@ -269,9 +394,9 @@ public class PollerImplTest extends BrambleMockTestCase {
// Schedule a polling task immediately
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L),
with(MILLISECONDS));
will(returnValue(future));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with(0L), with(MILLISECONDS));
will(returnValue(cancellable));
will(new RunAction());
// Running the polling task schedules the next polling task
oneOf(plugin).getPollingInterval();
@@ -281,8 +406,9 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
will(returnValue(future));
with(ioExecutor), with((long) (pollingInterval * 0.5)),
with(MILLISECONDS));
will(returnValue(cancellable));
// Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties)));
@@ -310,17 +436,37 @@ public class PollerImplTest extends BrambleMockTestCase {
// Schedule a polling task immediately
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L),
with(MILLISECONDS));
will(returnValue(future));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with(0L), with(MILLISECONDS));
will(returnValue(cancellable));
// The plugin is deactivated before the task runs - cancel the task
oneOf(future).cancel(false);
oneOf(cancellable).cancel();
}});
poller.eventOccurred(new TransportActiveEvent(transportId));
poller.eventOccurred(new TransportInactiveEvent(transportId));
}
private void expectReschedule(Plugin plugin) {
context.checking(new Expectations() {{
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with(ioExecutor), with((long) pollingInterval),
with(MILLISECONDS));
will(returnValue(cancellable));
}});
}
private void expectReconnect(DuplexPlugin plugin,
DuplexTransportConnection duplexConnection) throws Exception {
context.checking(new Expectations() {{

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
@@ -21,11 +22,13 @@ import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import static java.net.NetworkInterface.getNetworkInterfaces;
import static java.util.Collections.emptyList;
import static java.util.Collections.list;
import static java.util.concurrent.Executors.newCachedThreadPool;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -41,6 +44,7 @@ import static org.junit.Assume.assumeTrue;
public class LanTcpPluginTest extends BrambleTestCase {
private final Backoff backoff = new TestBackoff();
private final ExecutorService ioExecutor = newCachedThreadPool();
private Callback callback = null;
@@ -49,7 +53,8 @@ public class LanTcpPluginTest extends BrambleTestCase {
@Before
public void setUp() {
callback = new Callback();
plugin = new LanTcpPlugin(ioExecutor, callback, 0, 0, 0, 1000) {
plugin = new LanTcpPlugin(ioExecutor, ioExecutor, backoff, callback,
0, 0, 1000) {
@Override
protected boolean canConnectToOwnAddress() {
return true;
@@ -318,6 +323,11 @@ public class LanTcpPluginTest extends BrambleTestCase {
return local;
}
@Override
public Collection<TransportProperties> getRemoteProperties() {
return emptyList();
}
@Override
public void mergeSettings(Settings s) {
}
@@ -345,4 +355,20 @@ public class LanTcpPluginTest extends BrambleTestCase {
public void handleWriter(TransportConnectionWriter w) {
}
}
private static class TestBackoff implements Backoff {
@Override
public int getPollingInterval() {
return 60 * 1000;
}
@Override
public void increment() {
}
@Override
public void reset() {
}
}
}

View File

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

View File

@@ -26,6 +26,7 @@ import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedE
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousPollEvent;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.CaptureArgumentAction;
import org.briarproject.bramble.test.DbExpectations;
@@ -37,7 +38,6 @@ import org.junit.Test;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import static java.util.Collections.emptyList;
@@ -60,8 +60,7 @@ import static org.briarproject.bramble.test.TestUtils.getTransportProperties;
public class RendezvousPollerImplTest extends BrambleMockTestCase {
private final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
private final TaskScheduler scheduler = context.mock(TaskScheduler.class);
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
@@ -123,9 +122,9 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == OFFLINE)));
// Capture the poll task
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
with(MILLISECONDS));
oneOf(scheduler).scheduleWithFixedDelay(with(any(Runnable.class)),
with(any(Executor.class)), with(POLLING_INTERVAL_MS),
with(POLLING_INTERVAL_MS), with(MILLISECONDS));
will(new CaptureArgumentAction<>(capturePollTask, Runnable.class,
0));
}});
@@ -159,9 +158,9 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == FAILED)));
// Schedule the poll task
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
with(MILLISECONDS));
oneOf(scheduler).scheduleWithFixedDelay(with(any(Runnable.class)),
with(any(Executor.class)), with(POLLING_INTERVAL_MS),
with(POLLING_INTERVAL_MS), with(MILLISECONDS));
}});
rendezvousPoller.startService();
@@ -468,9 +467,9 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
oneOf(db).getPendingContacts(txn);
will(returnValue(emptyList()));
// Capture the poll task
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
with(MILLISECONDS));
oneOf(scheduler).scheduleWithFixedDelay(with(any(Runnable.class)),
with(any(Executor.class)), with(POLLING_INTERVAL_MS),
with(POLLING_INTERVAL_MS), with(MILLISECONDS));
will(new CaptureArgumentAction<>(capturePollTask, Runnable.class,
0));
}});
@@ -545,9 +544,9 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == OFFLINE)));
// Capture the poll task
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
with(MILLISECONDS));
oneOf(scheduler).scheduleWithFixedDelay(with(any(Runnable.class)),
with(any(Executor.class)), with(POLLING_INTERVAL_MS),
with(POLLING_INTERVAL_MS), with(MILLISECONDS));
will(new CaptureArgumentAction<>(capturePollTask, Runnable.class,
0));
}});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@@ -25,7 +25,8 @@ import static org.briarproject.bramble.util.StringUtils.isValidMac;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
class JavaBluetoothPlugin
extends BluetoothPlugin<StreamConnection, StreamConnectionNotifier> {
private static final Logger LOG =
getLogger(JavaBluetoothPlugin.class.getName());
@@ -34,11 +35,17 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
private volatile LocalDevice localDevice = null;
JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager,
TimeoutMonitor timeoutMonitor, Executor ioExecutor,
SecureRandom secureRandom, PluginCallback callback, int maxLatency,
int maxIdleTime, int pollingInterval) {
super(connectionManager, timeoutMonitor, ioExecutor, secureRandom,
callback, maxLatency, maxIdleTime, pollingInterval);
BluetoothConnectionFactory<StreamConnection> connectionFactory,
Executor ioExecutor,
Executor wakefulIoExecutor,
SecureRandom secureRandom,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime) {
super(connectionManager, connectionFactory, ioExecutor,
wakefulIoExecutor, secureRandom, backoff, callback,
maxLatency, maxIdleTime);
}
@Override
@@ -95,7 +102,7 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
@Override
DuplexTransportConnection acceptConnection(StreamConnectionNotifier ss)
throws IOException {
return wrapSocket(ss.acceptAndOpen());
return connectionFactory.wrapSocket(this, ss.acceptAndOpen());
}
@Override
@@ -107,7 +114,8 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
DuplexTransportConnection connectTo(String address, String uuid)
throws IOException {
String url = makeUrl(address, uuid);
return wrapSocket((StreamConnection) Connector.open(url));
StreamConnection s = (StreamConnection) Connector.open(url);
return connectionFactory.wrapSocket(this, s);
}
@Override
@@ -119,10 +127,4 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
private String makeUrl(String address, String uuid) {
return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
}
private DuplexTransportConnection wrapSocket(StreamConnection s)
throws IOException {
return new JavaBluetoothTransportConnection(this, connectionLimiter,
timeoutMonitor, s);
}
}

View File

@@ -2,41 +2,54 @@ package org.briarproject.bramble.plugin.bluetooth;
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;
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.security.SecureRandom;
import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.microedition.io.StreamConnection;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
@Immutable
@NotNullByDefault
public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = (int) SECONDS.toMillis(30);
private static final int MAX_IDLE_TIME = (int) SECONDS.toMillis(30);
private static final int POLLING_INTERVAL = (int) MINUTES.toMillis(2);
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;
private final Executor ioExecutor, wakefulIoExecutor;
private final SecureRandom secureRandom;
private final EventBus eventBus;
private final TimeoutMonitor timeoutMonitor;
private final BackoffFactory backoffFactory;
public JavaBluetoothPluginFactory(Executor ioExecutor,
SecureRandom secureRandom, EventBus eventBus,
TimeoutMonitor timeoutMonitor) {
@Inject
public JavaBluetoothPluginFactory(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor,
SecureRandom secureRandom,
EventBus eventBus,
TimeoutMonitor timeoutMonitor,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.secureRandom = secureRandom;
this.eventBus = eventBus;
this.timeoutMonitor = timeoutMonitor;
this.backoffFactory = backoffFactory;
}
@Override
@@ -53,9 +66,14 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(PluginCallback callback) {
BluetoothConnectionLimiter connectionLimiter =
new BluetoothConnectionLimiterImpl(eventBus);
BluetoothConnectionFactory<StreamConnection> connectionFactory =
new JavaBluetoothConnectionFactory(connectionLimiter,
timeoutMonitor);
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
timeoutMonitor, ioExecutor, secureRandom, callback, MAX_LATENCY,
MAX_IDLE_TIME, POLLING_INTERVAL);
connectionFactory, ioExecutor, wakefulIoExecutor, secureRandom,
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin);
return plugin;
}

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.api.battery.BatteryManager;
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.PluginCallback;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
@@ -19,20 +20,27 @@ import javax.net.SocketFactory;
@NotNullByDefault
abstract class JavaTorPlugin extends TorPlugin {
JavaTorPlugin(Executor ioExecutor, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory,
Clock clock, ResourceProvider resourceProvider,
JavaTorPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager,
Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, int initialPollingInterval,
int stablePollingInterval, File torDirectory) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
torRendezvousCrypto, callback, architecture, maxLatency,
maxIdleTime, initialPollingInterval, stablePollingInterval,
torDirectory);
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);
}
@Override

View File

@@ -6,6 +6,7 @@ import com.sun.jna.Native;
import org.briarproject.bramble.api.battery.BatteryManager;
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.PluginCallback;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
@@ -19,20 +20,27 @@ import javax.net.SocketFactory;
@NotNullByDefault
class UnixTorPlugin extends JavaTorPlugin {
UnixTorPlugin(Executor ioExecutor, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory,
Clock clock, ResourceProvider resourceProvider,
UnixTorPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager,
Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, int initialPollingInterval,
int stablePollingInterval, File torDirectory) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
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, initialPollingInterval,
stablePollingInterval, torDirectory);
maxLatency, maxIdleTime, torDirectory);
}
@Override

View File

@@ -2,10 +2,14 @@ package org.briarproject.bramble.plugin.tor;
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;
@@ -18,10 +22,9 @@ import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.net.SocketFactory;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.OsUtils.isLinux;
@@ -32,45 +35,44 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
private static final Logger LOG =
getLogger(UnixTorPluginFactory.class.getName());
private static final int MAX_LATENCY = (int) SECONDS.toMillis(30);
private static final int MAX_IDLE_TIME = (int) SECONDS.toMillis(30);
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;
/**
* How often to poll before our hidden service becomes reachable.
*/
private static final int INITIAL_POLLING_INTERVAL =
(int) MINUTES.toMillis(1);
/**
* How often to poll when our hidden service is reachable. Our contacts
* will poll when they come online, so our polling is just a fallback in
* case of repeated connection failures.
*/
private static final int STABLE_POLLING_INTERVAL =
(int) MINUTES.toMillis(15);
private final Executor ioExecutor;
private final Executor ioExecutor, wakefulIoExecutor;
private final NetworkManager networkManager;
private final LocationUtils locationUtils;
private final EventBus eventBus;
private final SocketFactory torSocketFactory;
private final BackoffFactory backoffFactory;
private final ResourceProvider resourceProvider;
private final CircumventionProvider circumventionProvider;
private final BatteryManager batteryManager;
private final Clock clock;
private final File torDirectory;
public UnixTorPluginFactory(Executor ioExecutor,
NetworkManager networkManager, LocationUtils locationUtils,
EventBus eventBus, SocketFactory torSocketFactory,
@Inject
public UnixTorPluginFactory(@IoExecutor Executor ioExecutor,
@IoExecutor Executor wakefulIoExecutor,
NetworkManager networkManager,
LocationUtils locationUtils,
EventBus eventBus,
SocketFactory torSocketFactory,
BackoffFactory backoffFactory,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Clock clock, File torDirectory) {
BatteryManager batteryManager,
Clock clock,
@TorDirectory File torDirectory) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.networkManager = networkManager;
this.locationUtils = locationUtils;
this.eventBus = eventBus;
this.torSocketFactory = torSocketFactory;
this.backoffFactory = backoffFactory;
this.resourceProvider = resourceProvider;
this.circumventionProvider = circumventionProvider;
this.batteryManager = batteryManager;
@@ -103,13 +105,14 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
return null;
}
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, networkManager,
locationUtils, torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, torRendezvousCrypto,
callback, architecture, MAX_LATENCY, MAX_IDLE_TIME,
INITIAL_POLLING_INTERVAL, STABLE_POLLING_INTERVAL,
torDirectory);
UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, wakefulIoExecutor,
networkManager, locationUtils, torSocketFactory, clock,
resourceProvider, circumventionProvider, batteryManager,
backoff, torRendezvousCrypto, callback, architecture,
MAX_LATENCY, MAX_IDLE_TIME, torDirectory);
eventBus.addListener(plugin);
return plugin;
}

View File

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

View File

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

View File

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

View File

@@ -22,8 +22,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 10207
versionName "1.2.7"
versionCode 10209
versionName "1.2.9"
applicationId "org.briarproject.briar.android"
buildConfigField "String", "GitHash",
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""

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