Compare commits

...

111 Commits

Author SHA1 Message Date
akwizgran
327e12e9f8 Don't allow multiple messages to be queued in memory for validation at startup. 2020-09-24 15:42:18 +01:00
Torsten Grote
4e5f2e31df Merge branch 'deterministic-briar-headless-jar' into 'master'
Make briar-headless.jar deterministic

See merge request briar/briar!1282
2020-09-17 20:25:12 +00:00
akwizgran
518c0370c8 Make briar-headless.jar deterministic. 2020-09-17 16:13:01 +01:00
akwizgran
7ef2fb5f0c Update Dutch translation. 2020-09-17 14:55:11 +01:00
akwizgran
1210b27bd1 Update translations. 2020-09-17 14:48:10 +01:00
Torsten Grote
cdf1a4abcd Merge branch 'update-feed-manager-integration-test-expectations' into 'master'
Update FeedManagerIntegrationTest expectations

See merge request briar/briar!1281
2020-09-10 15:05:39 +00:00
akwizgran
b18ef7e72d Update FeedManagerIntegrationTest expectations.
The "Schneier on Security" RSS feed no longer has a description.
2020-09-10 15:56:26 +01:00
Torsten Grote
48d907dda5 Merge branch '185-transports-activity' into 'master'
Add connections screen with information about transports

Closes #185

See merge request briar/briar!1277
2020-09-04 12:27:52 +00:00
akwizgran
3e5b7f451a Merge branch '1716-duplicate-unlock-screen' into 'master'
Don't show duplicate unlock screen on API 29+

Closes #1716

See merge request briar/briar!1280
2020-09-04 12:07:46 +00:00
akwizgran
95cccd1d15 Don't show duplicate unlock screen on API 29+. 2020-09-04 12:37:00 +01:00
Torsten Grote
0a33c77393 Merge branch 'cancel-rendezvous-polling' into 'master'
Only run the rendezvous polling task when we have pending contacts

See merge request briar/briar!1276
2020-09-01 11:53:16 +00:00
Torsten Grote
80caa7634a Merge branch 'do-not-enable-or-disable-bluetooth-automatically' into 'master'
Don't enable or disable the Bluetooth adapter automatically

Closes #1348

See merge request briar/briar!1278
2020-08-14 17:17:22 +00:00
akwizgran
2a8778d3cc Don't enable or disable the Bluetooth adapter automatically. 2020-08-14 16:18:02 +01:00
akwizgran
2cf146a104 Initialise Bluetooth state when view model is created. 2020-08-14 16:13:29 +01:00
akwizgran
a1e3c81bda Remove unused drawable. 2020-08-14 15:45:34 +01:00
akwizgran
bbcb183c24 Use a single click target that covers all transport indicators. 2020-08-14 15:25:52 +01:00
akwizgran
7fcb3394ca Add optional summary text to transport cards. 2020-08-14 15:25:51 +01:00
akwizgran
4310e4d1af Add help button to transports activity. 2020-08-14 15:25:51 +01:00
akwizgran
82e85bdb39 Remove redundant separator. 2020-08-14 15:25:51 +01:00
akwizgran
5ba0728abc Add onboarding for transports activity. 2020-08-14 15:25:51 +01:00
akwizgran
46bdb3589c Use Briar card style (sets background colour for dark theme). 2020-08-14 15:25:51 +01:00
akwizgran
392bc0d339 Use resource for title of transports activity. 2020-08-14 15:25:51 +01:00
akwizgran
02cf6bfcaa Use constants for default settings. 2020-08-14 15:25:51 +01:00
akwizgran
08a8a0b281 Show reason why Tor is disabled. 2020-08-14 15:25:51 +01:00
akwizgran
b189a38f62 Only show plugin status when it's relevant. 2020-08-14 15:25:50 +01:00
akwizgran
57b0641e5f Update network status. 2020-08-14 15:25:50 +01:00
akwizgran
5b5d513316 Shorter explanations. 2020-08-14 15:25:50 +01:00
akwizgran
6684fb2e1b Add settings button to toolbar. 2020-08-14 15:25:50 +01:00
akwizgran
73c6a29ede Add transports activity. 2020-08-14 15:25:50 +01:00
akwizgran
a8fe0a01ac Only run the rendezvous polling task when we have pending contacts. 2020-08-14 14:49:04 +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
c1d0936a1e Log HS version when descriptor is uploaded. 2020-06-30 14:18:55 +01:00
akwizgran
717be0178a Allow local transport properties to be removed by setting empty values. 2020-06-30 14:05:46 +01:00
akwizgran
34677eb3a7 Migrate to v3 hidden services. 2020-06-30 12:22:16 +01:00
172 changed files with 4368 additions and 1441 deletions

View File

@@ -1,8 +1,5 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings>
<JavaCodeStyleSettings> <JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="1" /> <option name="ANNOTATION_PARAMETER_WRAP" value="1" />
<option name="IMPORT_LAYOUT_TABLE"> <option name="IMPORT_LAYOUT_TABLE">

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 { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 28
versionCode 10208 versionCode 10209
versionName "1.2.8" versionName "1.2.9"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

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

View File

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

View File

@@ -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 android.net.NetworkInfo;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.network.NetworkStatus; import org.briarproject.bramble.api.network.NetworkStatus;
import org.briarproject.bramble.api.network.event.NetworkStatusEvent; import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.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.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@@ -50,20 +51,22 @@ class AndroidNetworkManager implements NetworkManager, Service {
private static final String WIFI_AP_STATE_CHANGED_ACTION = private static final String WIFI_AP_STATE_CHANGED_ACTION =
"android.net.wifi.WIFI_AP_STATE_CHANGED"; "android.net.wifi.WIFI_AP_STATE_CHANGED";
private final ScheduledExecutorService scheduler; private final TaskScheduler scheduler;
private final EventBus eventBus; private final EventBus eventBus;
private final Executor eventExecutor;
private final Context appContext; private final Context appContext;
private final AtomicReference<Future<?>> connectivityCheck = private final AtomicReference<Cancellable> connectivityCheck =
new AtomicReference<>(); new AtomicReference<>();
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private volatile BroadcastReceiver networkStateReceiver = null; private volatile BroadcastReceiver networkStateReceiver = null;
@Inject @Inject
AndroidNetworkManager(@Scheduler ScheduledExecutorService scheduler, AndroidNetworkManager(TaskScheduler scheduler, EventBus eventBus,
EventBus eventBus, Application app) { @EventExecutor Executor eventExecutor, Application app) {
this.scheduler = scheduler; this.scheduler = scheduler;
this.eventBus = eventBus; this.eventBus = eventBus;
this.eventExecutor = eventExecutor;
this.appContext = app.getApplicationContext(); this.appContext = app.getApplicationContext();
} }
@@ -104,11 +107,12 @@ class AndroidNetworkManager implements NetworkManager, Service {
} }
private void scheduleConnectionStatusUpdate(int delay, TimeUnit unit) { private void scheduleConnectionStatusUpdate(int delay, TimeUnit unit) {
Future<?> newConnectivityCheck = Cancellable newConnectivityCheck =
scheduler.schedule(this::updateConnectionStatus, delay, unit); scheduler.schedule(this::updateConnectionStatus, eventExecutor,
Future<?> oldConnectivityCheck = delay, unit);
Cancellable oldConnectivityCheck =
connectivityCheck.getAndSet(newConnectivityCheck); connectivityCheck.getAndSet(newConnectivityCheck);
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(false); if (oldConnectivityCheck != null) oldConnectivityCheck.cancel();
} }
private class NetworkStateReceiver extends BroadcastReceiver { 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; package org.briarproject.bramble.plugin.bluetooth;
import android.app.Application;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothServerSocket;
@@ -9,7 +10,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -31,7 +31,6 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -60,35 +59,40 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> { class AndroidBluetoothPlugin
extends BluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
private static final Logger LOG = private static final Logger LOG =
getLogger(AndroidBluetoothPlugin.class.getName()); getLogger(AndroidBluetoothPlugin.class.getName());
private static final int MAX_DISCOVERY_MS = 10_000; private static final int MAX_DISCOVERY_MS = 10_000;
private final ScheduledExecutorService scheduler;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final Context appContext; private final Application app;
private final Clock clock; private final Clock clock;
private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null; private volatile BluetoothStateReceiver receiver = null;
// Non-null if the plugin started successfully // Non-null if the plugin started successfully
private volatile BluetoothAdapter adapter = null; private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor, Executor ioExecutor, BluetoothConnectionFactory<BluetoothSocket> connectionFactory,
SecureRandom secureRandom, ScheduledExecutorService scheduler, Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext, Clock clock, Executor wakefulIoExecutor,
Backoff backoff, PluginCallback callback, int maxLatency, SecureRandom secureRandom,
AndroidExecutor androidExecutor,
Application app,
Clock clock,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime) { int maxIdleTime) {
super(connectionLimiter, timeoutMonitor, ioExecutor, secureRandom, super(connectionLimiter, connectionFactory, ioExecutor,
backoff, callback, maxLatency, maxIdleTime); wakefulIoExecutor, secureRandom, backoff, callback,
this.scheduler = scheduler; maxLatency, maxIdleTime);
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.appContext = appContext; this.app = app;
this.clock = clock; this.clock = clock;
} }
@@ -100,13 +104,13 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
filter.addAction(ACTION_STATE_CHANGED); filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED); filter.addAction(ACTION_SCAN_MODE_CHANGED);
receiver = new BluetoothStateReceiver(); receiver = new BluetoothStateReceiver();
appContext.registerReceiver(receiver, filter); app.registerReceiver(receiver, filter);
} }
@Override @Override
public void stop() { public void stop() {
super.stop(); super.stop();
if (receiver != null) appContext.unregisterReceiver(receiver); if (receiver != null) app.unregisterReceiver(receiver);
} }
@Override @Override
@@ -128,42 +132,10 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
return adapter != null && adapter.isEnabled(); return adapter != null && adapter.isEnabled();
} }
@Override
void enableAdapter() {
if (adapter != null && !adapter.isEnabled()) {
if (adapter.enable()) {
LOG.info("Enabling Bluetooth");
wasEnabledByUs = true;
} else {
LOG.info("Could not enable Bluetooth");
}
}
}
@Override
void disableAdapterIfEnabledByUs() {
if (isAdapterEnabled() && wasEnabledByUs) {
if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth");
wasEnabledByUs = false;
}
}
@Override
void setEnabledByUs() {
wasEnabledByUs = true;
}
@Override
void onAdapterDisabled() {
super.onAdapterDisabled();
wasEnabledByUs = false;
}
@Override @Override
@Nullable @Nullable
String getBluetoothAddress() { String getBluetoothAddress() {
String address = AndroidUtils.getBluetoothAddress(appContext, adapter); String address = AndroidUtils.getBluetoothAddress(app, adapter);
return address.isEmpty() ? null : address; return address.isEmpty() ? null : address;
} }
@@ -181,13 +153,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
@Override @Override
DuplexTransportConnection acceptConnection(BluetoothServerSocket ss) DuplexTransportConnection acceptConnection(BluetoothServerSocket ss)
throws IOException { throws IOException {
return wrapSocket(ss.accept()); return connectionFactory.wrapSocket(this, ss.accept());
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s)
throws IOException {
return new AndroidBluetoothTransportConnection(this, connectionLimiter,
timeoutMonitor, appContext, scheduler, s);
} }
@Override @Override
@@ -204,7 +170,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
try { try {
s = d.createInsecureRfcommSocketToServiceRecord(u); s = d.createInsecureRfcommSocketToServiceRecord(u);
s.connect(); s.connect();
return wrapSocket(s); return connectionFactory.wrapSocket(this, s);
} catch (IOException e) { } catch (IOException e) {
IoUtils.tryToClose(s, LOG, WARNING); IoUtils.tryToClose(s, LOG, WARNING);
throw e; throw e;
@@ -239,7 +205,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
filter.addAction(ACTION_DISCOVERY_STARTED); filter.addAction(ACTION_DISCOVERY_STARTED);
filter.addAction(ACTION_DISCOVERY_FINISHED); filter.addAction(ACTION_DISCOVERY_FINISHED);
filter.addAction(ACTION_FOUND); filter.addAction(ACTION_FOUND);
appContext.registerReceiver(receiver, filter); app.registerReceiver(receiver, filter);
try { try {
if (adapter.startDiscovery()) { if (adapter.startDiscovery()) {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
@@ -276,7 +242,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
} finally { } finally {
LOG.info("Cancelling discovery"); LOG.info("Cancelling discovery");
adapter.cancelDiscovery(); adapter.cancelDiscovery();
appContext.unregisterReceiver(receiver); app.unregisterReceiver(receiver);
} }
// Shuffle the addresses so we don't always try the same one first // Shuffle the addresses so we don't always try the same one first
shuffle(addresses); shuffle(addresses);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,28 +1,33 @@
package org.briarproject.bramble.plugin.tor; 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.battery.BatteryManager;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.TorDirectory;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.AndroidUtils;
import java.io.File;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.net.SocketFactory; import javax.net.SocketFactory;
@Immutable @Immutable
@@ -38,9 +43,8 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor; private final Executor ioExecutor, wakefulIoExecutor;
private final ScheduledExecutorService scheduler; private final Application app;
private final Context appContext;
private final NetworkManager networkManager; private final NetworkManager networkManager;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final EventBus eventBus; private final EventBus eventBus;
@@ -49,18 +53,28 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
private final ResourceProvider resourceProvider; private final ResourceProvider resourceProvider;
private final CircumventionProvider circumventionProvider; private final CircumventionProvider circumventionProvider;
private final BatteryManager batteryManager; private final BatteryManager batteryManager;
private final AndroidWakeLockManager wakeLockManager;
private final Clock clock; private final Clock clock;
private final File torDirectory;
public AndroidTorPluginFactory(Executor ioExecutor, @Inject
ScheduledExecutorService scheduler, Context appContext, public AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
NetworkManager networkManager, LocationUtils locationUtils, @WakefulIoExecutor Executor wakefulIoExecutor,
EventBus eventBus, SocketFactory torSocketFactory, Application app,
BackoffFactory backoffFactory, ResourceProvider resourceProvider, NetworkManager networkManager,
LocationUtils locationUtils,
EventBus eventBus,
SocketFactory torSocketFactory,
BackoffFactory backoffFactory,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Clock clock) { BatteryManager batteryManager,
AndroidWakeLockManager wakeLockManager,
Clock clock,
@TorDirectory File torDirectory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler; this.wakefulIoExecutor = wakefulIoExecutor;
this.appContext = appContext; this.app = app;
this.networkManager = networkManager; this.networkManager = networkManager;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.eventBus = eventBus; this.eventBus = eventBus;
@@ -69,7 +83,9 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
this.resourceProvider = resourceProvider; this.resourceProvider = resourceProvider;
this.circumventionProvider = circumventionProvider; this.circumventionProvider = circumventionProvider;
this.batteryManager = batteryManager; this.batteryManager = batteryManager;
this.wakeLockManager = wakeLockManager;
this.clock = clock; this.clock = clock;
this.torDirectory = torDirectory;
} }
@Override @Override
@@ -112,11 +128,12 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl(); TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, scheduler, AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor,
appContext, networkManager, locationUtils, torSocketFactory, wakefulIoExecutor, app, networkManager, locationUtils,
clock, resourceProvider, circumventionProvider, batteryManager, torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, wakeLockManager,
backoff, torRendezvousCrypto, callback, architecture, backoff, torRendezvousCrypto, callback, architecture,
MAX_LATENCY, MAX_IDLE_TIME); MAX_LATENCY, MAX_IDLE_TIME, torDirectory);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return 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; package org.briarproject.bramble.system;
import org.briarproject.bramble.api.event.EventExecutor; import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -16,6 +21,23 @@ import dagger.Provides;
@Module @Module
public class AndroidSystemModule { public class AndroidSystemModule {
private final ScheduledExecutorService scheduledExecutorService;
public AndroidSystemModule() {
// Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy =
new ScheduledThreadPoolExecutor.DiscardPolicy();
scheduledExecutorService = new ScheduledThreadPoolExecutor(1, policy);
}
@Provides
@Singleton
ScheduledExecutorService provideScheduledExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(scheduledExecutorService);
return scheduledExecutorService;
}
@Provides @Provides
@Singleton @Singleton
SecureRandomProvider provideSecureRandomProvider( SecureRandomProvider provideSecureRandomProvider(
@@ -47,4 +69,11 @@ public class AndroidSystemModule {
ResourceProvider provideResourceProvider(AndroidResourceProvider provider) { ResourceProvider provideResourceProvider(AndroidResourceProvider provider) {
return provider; return provider;
} }
@Provides
@Singleton
AndroidWakeLockManager provideWakeLockManager(
AndroidWakeLockManagerImpl wakeLockManager) {
return wakeLockManager;
}
} }

View File

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

View File

@@ -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

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,8 @@ import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import java.util.Collection;
/** /**
* An interface through which a transport plugin interacts with the rest of * An interface through which a transport plugin interacts with the rest of
* the application. * the application.
@@ -25,6 +27,11 @@ public interface PluginCallback extends ConnectionHandler {
*/ */
TransportProperties getLocalProperties(); TransportProperties getLocalProperties();
/**
* Returns the plugin's remote transport properties.
*/
Collection<TransportProperties> getRemoteProperties();
/** /**
* Merges the given settings with the plugin's settings * Merges the given settings with the plugin's settings
*/ */

View File

@@ -1,5 +1,7 @@
package org.briarproject.bramble.api.plugin; package org.briarproject.bramble.api.plugin;
import static java.util.concurrent.TimeUnit.DAYS;
public interface TorConstants { public interface TorConstants {
TransportId ID = new TransportId("org.briarproject.bramble.tor"); TransportId ID = new TransportId("org.briarproject.bramble.tor");
@@ -19,6 +21,14 @@ public interface TorConstants {
String PREF_TOR_PORT = "port"; String PREF_TOR_PORT = "port";
String PREF_TOR_MOBILE = "useMobileData"; String PREF_TOR_MOBILE = "useMobileData";
String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging"; String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging";
String HS_PRIVATE_KEY_V2 = "onionPrivKey";
String HS_PRIVATE_KEY_V3 = "onionPrivKey3";
String HS_V3_CREATED = "onionPrivKey3Created";
/**
* How long to publish a v3 hidden service before retiring the v2 service.
*/
long V3_MIGRATION_PERIOD_MS = DAYS.toMillis(180);
// Values for PREF_TOR_NETWORK // Values for PREF_TOR_NETWORK
int PREF_TOR_NETWORK_AUTOMATIC = 0; int PREF_TOR_NETWORK_AUTOMATIC = 0;

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.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@@ -11,15 +12,11 @@ import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Annotation for injecting a scheduled executor service * Annotation for injecting the {@link File directory} where the Tor plugin
* that can be used to schedule the execution of tasks. * should store its state.
* <p>
* The service should <b>only</b> be used for running tasks on other executors
* at scheduled times.
* No significant work should be run by the service itself!
*/ */
@Qualifier @Qualifier
@Target({FIELD, METHOD, PARAMETER}) @Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface 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.properties.TransportProperties;
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
import org.briarproject.bramble.api.system.Wakeful;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -21,6 +22,7 @@ public interface DuplexPlugin extends Plugin {
* Attempts to create and return a connection using the given transport * Attempts to create and return a connection using the given transport
* properties. Returns null if a connection cannot be created. * properties. Returns null if a connection cannot be created.
*/ */
@Wakeful
@Nullable @Nullable
DuplexTransportConnection createConnection(TransportProperties p); DuplexTransportConnection createConnection(TransportProperties p);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,10 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.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.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
@@ -35,8 +38,6 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -57,8 +58,8 @@ class PollerImpl implements Poller, EventListener {
private static final Logger LOG = getLogger(PollerImpl.class.getName()); private static final Logger LOG = getLogger(PollerImpl.class.getName());
private final Executor ioExecutor; private final Executor ioExecutor, wakefulIoExecutor;
private final ScheduledExecutorService scheduler; private final TaskScheduler scheduler;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry; private final ConnectionRegistry connectionRegistry;
private final PluginManager pluginManager; private final PluginManager pluginManager;
@@ -71,12 +72,16 @@ class PollerImpl implements Poller, EventListener {
@Inject @Inject
PollerImpl(@IoExecutor Executor ioExecutor, PollerImpl(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler, @WakefulIoExecutor Executor wakefulIoExecutor,
TaskScheduler scheduler,
ConnectionManager connectionManager, ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager, ConnectionRegistry connectionRegistry,
PluginManager pluginManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock) { SecureRandom random,
Clock clock) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.scheduler = scheduler; this.scheduler = scheduler;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry; this.connectionRegistry = connectionRegistry;
@@ -133,7 +138,7 @@ class PollerImpl implements Poller, EventListener {
} }
private void connectToContact(ContactId c, SimplexPlugin p) { private void connectToContact(ContactId c, SimplexPlugin p) {
ioExecutor.execute(() -> { wakefulIoExecutor.execute(() -> {
TransportId t = p.getId(); TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return; if (connectionRegistry.isConnected(c, t)) return;
try { try {
@@ -149,7 +154,7 @@ class PollerImpl implements Poller, EventListener {
} }
private void connectToContact(ContactId c, DuplexPlugin p) { private void connectToContact(ContactId c, DuplexPlugin p) {
ioExecutor.execute(() -> { wakefulIoExecutor.execute(() -> {
TransportId t = p.getId(); TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return; if (connectionRegistry.isConnected(c, t)) return;
try { try {
@@ -186,11 +191,11 @@ class PollerImpl implements Poller, EventListener {
if (scheduled == null || due < scheduled.task.due) { if (scheduled == null || due < scheduled.task.due) {
// If a later task exists, cancel it. If it's already started // If a later task exists, cancel it. If it's already started
// it will abort safely when it finds it's been replaced // it will abort safely when it finds it's been replaced
if (scheduled != null) scheduled.future.cancel(false); if (scheduled != null) scheduled.cancellable.cancel();
PollTask task = new PollTask(p, due, randomiseNext); PollTask task = new PollTask(p, due, randomiseNext);
Future future = scheduler.schedule(() -> Cancellable cancellable = scheduler.schedule(task, ioExecutor,
ioExecutor.execute(task), delay, MILLISECONDS); delay, MILLISECONDS);
tasks.put(t, new ScheduledPollTask(task, future)); tasks.put(t, new ScheduledPollTask(task, cancellable));
} }
} finally { } finally {
lock.unlock(); lock.unlock();
@@ -201,7 +206,7 @@ class PollerImpl implements Poller, EventListener {
lock.lock(); lock.lock();
try { try {
ScheduledPollTask scheduled = tasks.remove(t); ScheduledPollTask scheduled = tasks.remove(t);
if (scheduled != null) scheduled.future.cancel(false); if (scheduled != null) scheduled.cancellable.cancel();
} finally { } finally {
lock.unlock(); lock.unlock();
} }
@@ -232,11 +237,11 @@ class PollerImpl implements Poller, EventListener {
private class ScheduledPollTask { private class ScheduledPollTask {
private final PollTask task; 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.task = task;
this.future = future; this.cancellable = cancellable;
} }
} }
@@ -254,6 +259,7 @@ class PollerImpl implements Poller, EventListener {
@Override @Override
@IoExecutor @IoExecutor
@Wakeful
public void run() { public void run() {
lock.lock(); lock.lock();
try { 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; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Multiset;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection; import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
@@ -21,10 +21,8 @@ import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
import org.briarproject.bramble.api.plugin.event.DisableBluetoothEvent;
import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.event.RemoteTransportPropertiesUpdatedEvent;
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
@@ -46,8 +44,12 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_ADDRESS_IS_REFLECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_EVER_CONNECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_PLUGIN_ENABLE; import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_ADDRESS_IS_REFLECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_EVER_CONNECTED;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
@@ -55,6 +57,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.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.REFLECTED_PROPERTY_PREFIX;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@@ -63,20 +66,21 @@ import static org.briarproject.bramble.util.StringUtils.macToString;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener { abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
private static final Logger LOG = private static final Logger LOG =
getLogger(BluetoothPlugin.class.getName()); getLogger(BluetoothPlugin.class.getName());
final BluetoothConnectionLimiter connectionLimiter; final BluetoothConnectionLimiter connectionLimiter;
final TimeoutMonitor timeoutMonitor; final BluetoothConnectionFactory<S> connectionFactory;
private final Executor ioExecutor; private final Executor ioExecutor, wakefulIoExecutor;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final Backoff backoff; private final Backoff backoff;
private final PluginCallback callback; private final PluginCallback callback;
private final int maxLatency, maxIdleTime; private final int maxLatency, maxIdleTime;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private final AtomicBoolean everConnected = new AtomicBoolean(false);
protected final PluginState state = new PluginState(); protected final PluginState state = new PluginState();
@@ -86,12 +90,6 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract boolean isAdapterEnabled(); abstract boolean isAdapterEnabled();
abstract void enableAdapter();
abstract void disableAdapterIfEnabledByUs();
abstract void setEnabledByUs();
/** /**
* Returns the local Bluetooth address, or null if no valid address can * Returns the local Bluetooth address, or null if no valid address can
* be found. * be found.
@@ -115,12 +113,18 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection discoverAndConnect(String uuid); abstract DuplexTransportConnection discoverAndConnect(String uuid);
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor, Executor ioExecutor, BluetoothConnectionFactory<S> connectionFactory,
SecureRandom secureRandom, Backoff backoff, Executor ioExecutor,
PluginCallback callback, int maxLatency, int maxIdleTime) { Executor wakefulIoExecutor,
SecureRandom secureRandom,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime) {
this.connectionLimiter = connectionLimiter; this.connectionLimiter = connectionLimiter;
this.timeoutMonitor = timeoutMonitor; this.connectionFactory = connectionFactory;
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.backoff = backoff; this.backoff = backoff;
this.callback = callback; this.callback = callback;
@@ -167,6 +171,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
Settings settings = callback.getSettings(); Settings settings = callback.getSettings();
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE,
DEFAULT_PREF_PLUGIN_ENABLE); DEFAULT_PREF_PLUGIN_ENABLE);
everConnected.set(settings.getBoolean(PREF_EVER_CONNECTED,
DEFAULT_PREF_EVER_CONNECTED));
state.setStarted(enabledByUser); state.setStarted(enabledByUser);
try { try {
initialiseAdapter(); initialiseAdapter();
@@ -174,10 +180,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
throw new PluginException(e); throw new PluginException(e);
} }
updateProperties(); updateProperties();
if (enabledByUser) { if (enabledByUser && isAdapterEnabled()) bind();
if (isAdapterEnabled()) bind();
else enableAdapter();
}
} }
private void bind() { private void bind() {
@@ -205,25 +208,68 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
TransportProperties p = callback.getLocalProperties(); TransportProperties p = callback.getLocalProperties();
String address = p.get(PROP_ADDRESS); String address = p.get(PROP_ADDRESS);
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);
Settings s = callback.getSettings();
boolean isReflected = s.getBoolean(PREF_ADDRESS_IS_REFLECTED,
DEFAULT_PREF_ADDRESS_IS_REFLECTED);
boolean changed = false; boolean changed = false;
if (address == null) { if (address == null || isReflected) {
address = getBluetoothAddress(); address = getBluetoothAddress();
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO)) {
LOG.info("Local address " + scrubMacAddress(address)); LOG.info("Local address " + scrubMacAddress(address));
if (!isNullOrEmpty(address)) { }
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; changed = true;
isReflected = false;
} }
} }
if (uuid == null) { if (uuid == null) {
byte[] random = new byte[UUID_BYTES]; byte[] random = new byte[UUID_BYTES];
secureRandom.nextBytes(random); secureRandom.nextBytes(random);
uuid = UUID.nameUUIDFromBytes(random).toString(); uuid = UUID.nameUUIDFromBytes(random).toString();
p.put(PROP_UUID, uuid);
changed = true; changed = true;
} }
contactConnectionsUuid = uuid; contactConnectionsUuid = uuid;
if (changed) 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) { private void acceptContactConnections(SS ss) {
@@ -240,15 +286,27 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
LOG.info("Connection received"); LOG.info("Connection received");
connectionLimiter.connectionOpened(conn); connectionLimiter.connectionOpened(conn);
backoff.reset(); backoff.reset();
setEverConnected();
callback.handleConnection(conn); callback.handleConnection(conn);
} }
} }
private void setEverConnected() {
if (!everConnected.getAndSet(true)) {
ioExecutor.execute(() -> {
Settings s = new Settings();
s.putBoolean(PREF_EVER_CONNECTED, true);
callback.mergeSettings(s);
// Contacts may already have sent a reflected address
updateProperties();
});
}
}
@Override @Override
public void stop() { public void stop() {
SS ss = state.setStopped(); SS ss = state.setStopped();
tryToClose(ss); tryToClose(ss);
disableAdapterIfEnabledByUs();
} }
@Override @Override
@@ -286,10 +344,11 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (isNullOrEmpty(address)) return; if (isNullOrEmpty(address)) return;
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);
if (isNullOrEmpty(uuid)) return; if (isNullOrEmpty(uuid)) return;
ioExecutor.execute(() -> { wakefulIoExecutor.execute(() -> {
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
setEverConnected();
h.handleConnection(d); h.handleConnection(d);
} }
}); });
@@ -392,7 +451,10 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
LOG.info("Connecting to key agreement UUID " + uuid); LOG.info("Connecting to key agreement UUID " + uuid);
conn = connect(address, uuid); conn = connect(address, uuid);
} }
if (conn != null) connectionLimiter.connectionOpened(conn); if (conn != null) {
connectionLimiter.connectionOpened(conn);
setEverConnected();
}
return conn; return conn;
} }
@@ -415,13 +477,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof EnableBluetoothEvent) { if (e instanceof SettingsUpdatedEvent) {
ioExecutor.execute(this::enableAdapter);
} else if (e instanceof DisableBluetoothEvent) {
ioExecutor.execute(this::disableAdapterIfEnabledByUs);
} else if (e instanceof BluetoothEnabledEvent) {
setEnabledByUs();
} else if (e instanceof SettingsUpdatedEvent) {
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e; SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
if (s.getNamespace().equals(ID.getString())) if (s.getNamespace().equals(ID.getString()))
ioExecutor.execute(() -> onSettingsUpdated(s.getSettings())); ioExecutor.execute(() -> onSettingsUpdated(s.getSettings()));
@@ -429,6 +485,12 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
ioExecutor.execute(connectionLimiter::keyAgreementStarted); ioExecutor.execute(connectionLimiter::keyAgreementStarted);
} else if (e instanceof KeyAgreementStoppedListeningEvent) { } else if (e instanceof KeyAgreementStoppedListeningEvent) {
ioExecutor.execute(connectionLimiter::keyAgreementEnded); ioExecutor.execute(connectionLimiter::keyAgreementEnded);
} else if (e instanceof RemoteTransportPropertiesUpdatedEvent) {
RemoteTransportPropertiesUpdatedEvent r =
(RemoteTransportPropertiesUpdatedEvent) e;
if (r.getTransportId().equals(ID)) {
ioExecutor.execute(this::updateProperties);
}
} }
} }
@@ -441,11 +503,13 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (ss != null) { if (ss != null) {
LOG.info("Disabled by user, closing server socket"); LOG.info("Disabled by user, closing server socket");
tryToClose(ss); tryToClose(ss);
disableAdapterIfEnabledByUs();
} else if (s == INACTIVE) { } else if (s == INACTIVE) {
LOG.info("Enabled by user, opening server socket"); if (isAdapterEnabled()) {
if (isAdapterEnabled()) bind(); LOG.info("Enabled by user, opening server socket");
else enableAdapter(); bind();
} else {
LOG.info("Enabled by user but adapter is disabled");
}
} }
} }

View File

@@ -88,10 +88,15 @@ class LanTcpPlugin extends TcpPlugin {
} }
} }
LanTcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback, LanTcpPlugin(Executor ioExecutor,
int maxLatency, int maxIdleTime, int connectionTimeout) { Executor wakefulIoExecutor,
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime, Backoff backoff,
connectionTimeout); PluginCallback callback,
int maxLatency,
int maxIdleTime,
int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,
maxIdleTime, connectionTimeout);
} }
@Override @Override

View File

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

View File

@@ -66,7 +66,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
private static final Pattern DOTTED_QUAD = private static final Pattern DOTTED_QUAD =
Pattern.compile("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"); Pattern.compile("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$");
protected final Executor ioExecutor, bindExecutor; protected final Executor ioExecutor, wakefulIoExecutor, bindExecutor;
protected final Backoff backoff; protected final Backoff backoff;
protected final PluginCallback callback; protected final PluginCallback callback;
protected final int maxLatency, maxIdleTime; protected final int maxLatency, maxIdleTime;
@@ -107,9 +107,15 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
*/ */
protected abstract boolean isEnabledByDefault(); protected abstract boolean isEnabledByDefault();
TcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback, TcpPlugin(Executor ioExecutor,
int maxLatency, int maxIdleTime, int connectionTimeout) { Executor wakefulIoExecutor,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime,
int connectionTimeout) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.backoff = backoff; this.backoff = backoff;
this.callback = callback; this.callback = callback;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
@@ -143,15 +149,21 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
protected void bind() { protected void bind() {
bindExecutor.execute(() -> { bindExecutor.execute(() -> {
if (getState() != INACTIVE) return; State s = getState();
if (s != ACTIVE && s != INACTIVE) return;
bind(true); bind(true);
bind(false); bind(false);
}); });
} }
private void bind(boolean ipv4) { private void bind(boolean ipv4) {
ServerSocket old = state.getServerSocket(ipv4);
ServerSocket ss = null; ServerSocket ss = null;
for (InetSocketAddress addr : getLocalSocketAddresses(ipv4)) { for (InetSocketAddress addr : getLocalSocketAddresses(ipv4)) {
if (old != null && addr.equals(old.getLocalSocketAddress())) {
LOG.info("Server socket already bound");
return;
}
try { try {
ss = new ServerSocket(); ss = new ServerSocket();
ss.bind(addr); ss.bind(addr);
@@ -245,7 +257,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
} }
private void connect(TransportProperties p, ConnectionHandler h) { private void connect(TransportProperties p, ConnectionHandler h) {
ioExecutor.execute(() -> { wakefulIoExecutor.execute(() -> {
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();

View File

@@ -30,11 +30,16 @@ class WanTcpPlugin extends TcpPlugin {
private volatile MappingResult mappingResult; private volatile MappingResult mappingResult;
WanTcpPlugin(Executor ioExecutor, Backoff backoff, PortMapper portMapper, WanTcpPlugin(Executor ioExecutor,
PluginCallback callback, int maxLatency, int maxIdleTime, Executor wakefulIoExecutor,
Backoff backoff,
PortMapper portMapper,
PluginCallback callback,
int maxLatency,
int maxIdleTime,
int connectionTimeout) { int connectionTimeout) {
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime, super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,
connectionTimeout); maxIdleTime, connectionTimeout);
this.portMapper = portMapper; this.portMapper = portMapper;
} }

View File

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

View File

@@ -79,6 +79,9 @@ import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_PLUG
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING; import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_PRIVATE_KEY_V2;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_PRIVATE_KEY_V3;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_V3_CREATED;
import static org.briarproject.bramble.api.plugin.TorConstants.ID; import static org.briarproject.bramble.api.plugin.TorConstants.ID;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
@@ -92,6 +95,7 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
import static org.briarproject.bramble.api.plugin.TorConstants.V3_MIGRATION_PERIOD_MS;
import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES; import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES;
import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.IoUtils.tryToClose;
@@ -114,7 +118,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final Pattern ONION_V2 = Pattern.compile("[a-z2-7]{16}"); private static final Pattern ONION_V2 = Pattern.compile("[a-z2-7]{16}");
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}"); private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
private final Executor ioExecutor, connectionStatusExecutor; private final Executor ioExecutor, wakefulIoExecutor;
private final Executor connectionStatusExecutor;
private final NetworkManager networkManager; private final NetworkManager networkManager;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final SocketFactory torSocketFactory; private final SocketFactory torSocketFactory;
@@ -141,15 +146,24 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
protected abstract long getLastUpdateTime(); protected abstract long getLastUpdateTime();
TorPlugin(Executor ioExecutor, NetworkManager networkManager, TorPlugin(Executor ioExecutor,
LocationUtils locationUtils, SocketFactory torSocketFactory, Executor wakefulIoExecutor,
Clock clock, ResourceProvider resourceProvider, NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff, BatteryManager batteryManager,
Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency, PluginCallback callback,
int maxIdleTime, File torDirectory) { String architecture,
int maxLatency,
int maxIdleTime,
File torDirectory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.networkManager = networkManager; this.networkManager = networkManager;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.torSocketFactory = torSocketFactory; this.torSocketFactory = torSocketFactory;
@@ -438,15 +452,66 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private void publishHiddenService(String port) { private void publishHiddenService(String port) {
if (!state.isTorRunning()) return; if (!state.isTorRunning()) return;
LOG.info("Creating hidden service"); // TODO: Remove support for v2 hidden services after a reasonable
String privKey = settings.get(HS_PRIVKEY); // migration period (migration started 2020-06-30)
String privKey2 = settings.get(HS_PRIVATE_KEY_V2);
String privKey3 = settings.get(HS_PRIVATE_KEY_V3);
String v3Created = settings.get(HS_V3_CREATED);
// Publish a v2 hidden service if we've already created one, and
// either we've never published a v3 hidden service or we're still
// in the migration period since first publishing it
if (!isNullOrEmpty(privKey2)) {
long now = clock.currentTimeMillis();
long then = v3Created == null ? now : Long.parseLong(v3Created);
if (now - then >= V3_MIGRATION_PERIOD_MS) retireV2HiddenService();
else publishV2HiddenService(port, privKey2);
}
publishV3HiddenService(port, privKey3);
}
private void publishV2HiddenService(String port, String privKey) {
LOG.info("Creating v2 hidden service");
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
Map<String, String> response;
try {
response = controlConnection.addOnion(privKey, portLines);
} catch (IOException e) {
logException(LOG, WARNING, e);
return;
}
if (!response.containsKey(HS_ADDRESS)) {
LOG.warning("Tor did not return a hidden service address");
return;
}
String onion2 = response.get(HS_ADDRESS);
if (LOG.isLoggable(INFO)) {
LOG.info("V2 hidden service " + scrubOnion(onion2));
}
// The hostname has already been published and the private key stored
}
private void retireV2HiddenService() {
LOG.info("Retiring v2 hidden service");
TransportProperties p = new TransportProperties();
p.put(PROP_ONION_V2, "");
callback.mergeLocalProperties(p);
Settings s = new Settings();
s.put(HS_PRIVATE_KEY_V2, "");
callback.mergeSettings(s);
}
private void publishV3HiddenService(String port, @Nullable String privKey) {
LOG.info("Creating v3 hidden service");
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port); Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
Map<String, String> response; Map<String, String> response;
try { try {
// Use the control connection to set up the hidden service // Use the control connection to set up the hidden service
if (privKey == null) if (privKey == null) {
response = controlConnection.addOnion(portLines); response = controlConnection.addOnion("NEW:ED25519-V3",
else response = controlConnection.addOnion(privKey, portLines); portLines, null);
} else {
response = controlConnection.addOnion(privKey, portLines);
}
} catch (IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
return; return;
@@ -459,17 +524,19 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
LOG.warning("Tor did not return a private key"); LOG.warning("Tor did not return a private key");
return; return;
} }
// Publish the hidden service's onion hostname in transport properties String onion3 = response.get(HS_ADDRESS);
String onion2 = response.get(HS_ADDRESS); if (LOG.isLoggable(INFO)) {
if (LOG.isLoggable(INFO)) LOG.info("V3 hidden service " + scrubOnion(onion3));
LOG.info("Hidden service " + scrubOnion(onion2)); }
TransportProperties p = new TransportProperties();
p.put(PROP_ONION_V2, onion2);
callback.mergeLocalProperties(p);
if (privKey == null) { if (privKey == null) {
// Publish the hidden service's onion hostname in transport props
TransportProperties p = new TransportProperties();
p.put(PROP_ONION_V3, onion3);
callback.mergeLocalProperties(p);
// Save the hidden service's private key for next time // Save the hidden service's private key for next time
Settings s = new Settings(); Settings s = new Settings();
s.put(HS_PRIVKEY, response.get(HS_PRIVKEY)); s.put(HS_PRIVATE_KEY_V3, response.get(HS_PRIVKEY));
s.put(HS_V3_CREATED, String.valueOf(clock.currentTimeMillis()));
callback.mergeSettings(s); callback.mergeSettings(s);
} }
} }
@@ -563,7 +630,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void connect(TransportProperties p, ConnectionHandler h) { private void connect(TransportProperties p, ConnectionHandler h) {
ioExecutor.execute(() -> { wakefulIoExecutor.execute(() -> {
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
@@ -575,12 +642,15 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public DuplexTransportConnection createConnection(TransportProperties p) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (getState() != ACTIVE) return null; if (getState() != ACTIVE) return null;
String bestOnion = null; // TODO: Remove support for v2 hidden services after a reasonable
// migration period (migration started 2020-06-30)
String bestOnion = null, version = null;
String onion2 = p.get(PROP_ONION_V2); String onion2 = p.get(PROP_ONION_V2);
String onion3 = p.get(PROP_ONION_V3); String onion3 = p.get(PROP_ONION_V3);
if (!isNullOrEmpty(onion2)) { if (!isNullOrEmpty(onion2)) {
if (ONION_V2.matcher(onion2).matches()) { if (ONION_V2.matcher(onion2).matches()) {
bestOnion = onion2; bestOnion = onion2;
version = "v2";
} else { } else {
// Don't scrub the address so we can find the problem // Don't scrub the address so we can find the problem
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -590,6 +660,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!isNullOrEmpty(onion3)) { if (!isNullOrEmpty(onion3)) {
if (ONION_V3.matcher(onion3).matches()) { if (ONION_V3.matcher(onion3).matches()) {
bestOnion = onion3; bestOnion = onion3;
version = "v3";
} else { } else {
// Don't scrub the address so we can find the problem // Don't scrub the address so we can find the problem
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -599,17 +670,21 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (bestOnion == null) return null; if (bestOnion == null) return null;
Socket s = null; Socket s = null;
try { try {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO)) {
LOG.info("Connecting to " + scrubOnion(bestOnion)); LOG.info("Connecting to " + version + " "
+ scrubOnion(bestOnion));
}
s = torSocketFactory.createSocket(bestOnion + ".onion", 80); s = torSocketFactory.createSocket(bestOnion + ".onion", 80);
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO)) {
LOG.info("Connected to " + scrubOnion(bestOnion)); LOG.info("Connected to " + version + " "
+ scrubOnion(bestOnion));
}
return new TorTransportConnection(this, s); return new TorTransportConnection(this, s);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Could not connect to " + scrubOnion(bestOnion) LOG.info("Could not connect to " + version + " "
+ ": " + e.toString()); + scrubOnion(bestOnion) + ": " + e.toString());
} }
tryToClose(s, LOG, WARNING); tryToClose(s, LOG, WARNING);
return null; return null;
@@ -731,8 +806,16 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public void unrecognized(String type, String msg) { public void unrecognized(String type, String msg) {
if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) {
LOG.info("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");
}
}
}
} }
@Override @Override

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

View File

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

View File

@@ -35,6 +35,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
@@ -124,8 +125,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
Group g = db.getGroup(txn, m.getGroupId()); Group g = db.getGroup(txn, m.getGroupId());
return new Pair<>(m, g); return new Pair<>(m, g);
}); });
validateMessageAsync(mg.getFirst(), mg.getSecond()); validateMessageAsync(mg.getFirst(), mg.getSecond(), unvalidated);
validateNextMessageAsync(unvalidated);
} catch (NoSuchMessageException e) { } catch (NoSuchMessageException e) {
LOG.info("Message removed before validation"); LOG.info("Message removed before validation");
validateNextMessageAsync(unvalidated); validateNextMessageAsync(unvalidated);
@@ -213,12 +213,14 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
} }
private void validateMessageAsync(Message m, Group g) { private void validateMessageAsync(Message m, Group g,
validationExecutor.execute(() -> validateMessage(m, g)); @Nullable Queue<MessageId> unvalidated) {
validationExecutor.execute(() -> validateMessage(m, g, unvalidated));
} }
@ValidationExecutor @ValidationExecutor
private void validateMessage(Message m, Group g) { private void validateMessage(Message m, Group g,
@Nullable Queue<MessageId> unvalidated) {
ClientMajorVersion cv = ClientMajorVersion cv =
new ClientMajorVersion(g.getClientId(), g.getMajorVersion()); new ClientMajorVersion(g.getClientId(), g.getMajorVersion());
MessageValidator v = validators.get(cv); MessageValidator v = validators.get(cv);
@@ -234,6 +236,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
Queue<MessageId> invalidate = new LinkedList<>(); Queue<MessageId> invalidate = new LinkedList<>();
invalidate.add(m.getId()); invalidate.add(m.getId());
invalidateNextMessageAsync(invalidate); invalidateNextMessageAsync(invalidate);
} finally {
if (unvalidated != null) validateNextMessageAsync(unvalidated);
} }
} }
} }
@@ -440,7 +444,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
try { try {
Group g = db.transactionWithResult(true, txn -> Group g = db.transactionWithResult(true, txn ->
db.getGroup(txn, m.getGroupId())); db.getGroup(txn, m.getGroupId()));
validateMessageAsync(m, g); validateMessageAsync(m, g, null);
} catch (NoSuchGroupException e) { } catch (NoSuchGroupException e) {
LOG.info("Group removed before validation"); LOG.info("Group removed before validation");
} catch (DbException e) { } catch (DbException e) {

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

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

View File

@@ -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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.sync; package org.briarproject.bramble.sync;
import org.briarproject.bramble.BrambleCoreEagerSingletons; import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule; import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
@@ -13,7 +13,8 @@ import dagger.Component;
BrambleCoreIntegrationTestModule.class, BrambleCoreIntegrationTestModule.class,
BrambleCoreModule.class BrambleCoreModule.class
}) })
interface SyncIntegrationTestComponent extends BrambleCoreEagerSingletons { interface SyncIntegrationTestComponent extends
BrambleCoreIntegrationTestEagerSingletons {
void inject(SyncIntegrationTest testCase); 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.api.FeatureFlags;
import org.briarproject.bramble.battery.DefaultBatteryManagerModule; import org.briarproject.bramble.battery.DefaultBatteryManagerModule;
import org.briarproject.bramble.event.DefaultEventExecutorModule; import org.briarproject.bramble.event.DefaultEventExecutorModule;
import org.briarproject.bramble.system.DefaultTaskSchedulerModule;
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@@ -10,6 +12,8 @@ import dagger.Provides;
@Module(includes = { @Module(includes = {
DefaultBatteryManagerModule.class, DefaultBatteryManagerModule.class,
DefaultEventExecutorModule.class, DefaultEventExecutorModule.class,
DefaultTaskSchedulerModule.class,
DefaultWakefulIoExecutorModule.class,
TestDatabaseConfigModule.class, TestDatabaseConfigModule.class,
TestPluginConfigModule.class, TestPluginConfigModule.class,
TestSecureRandomModule.class TestSecureRandomModule.class

View File

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

View File

@@ -1,28 +1,20 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.BluetoothConstants; import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory;
import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory; import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory;
import org.briarproject.bramble.plugin.modem.ModemPluginFactory; import org.briarproject.bramble.plugin.modem.ModemPluginFactory;
import org.briarproject.bramble.plugin.tcp.LanTcpPluginFactory; import org.briarproject.bramble.plugin.tcp.LanTcpPluginFactory;
import org.briarproject.bramble.plugin.tcp.WanTcpPluginFactory; import org.briarproject.bramble.plugin.tcp.WanTcpPluginFactory;
import java.security.SecureRandom;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@@ -36,27 +28,15 @@ import static java.util.Collections.singletonMap;
public class DesktopPluginModule extends PluginModule { public class DesktopPluginModule extends PluginModule {
@Provides @Provides
PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor, PluginConfig getPluginConfig(JavaBluetoothPluginFactory bluetooth,
SecureRandom random, BackoffFactory backoffFactory, ModemPluginFactory modem, LanTcpPluginFactory lan,
ReliabilityLayerFactory reliabilityFactory, WanTcpPluginFactory wan) {
ShutdownManager shutdownManager, EventBus eventBus,
TimeoutMonitor timeoutMonitor) {
DuplexPluginFactory bluetooth = new JavaBluetoothPluginFactory(
ioExecutor, random, eventBus, timeoutMonitor, backoffFactory);
DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
reliabilityFactory);
DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor, eventBus,
backoffFactory);
DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor, eventBus,
backoffFactory, shutdownManager);
Collection<DuplexPluginFactory> duplex =
asList(bluetooth, modem, lan, wan);
@NotNullByDefault @NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() { PluginConfig pluginConfig = new PluginConfig() {
@Override @Override
public Collection<DuplexPluginFactory> getDuplexFactories() { public Collection<DuplexPluginFactory> getDuplexFactories() {
return duplex; return asList(bluetooth, modem, lan, wan);
} }
@Override @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,6 +1,5 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -26,7 +25,8 @@ import static org.briarproject.bramble.util.StringUtils.isValidMac;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> { class JavaBluetoothPlugin
extends BluetoothPlugin<StreamConnection, StreamConnectionNotifier> {
private static final Logger LOG = private static final Logger LOG =
getLogger(JavaBluetoothPlugin.class.getName()); getLogger(JavaBluetoothPlugin.class.getName());
@@ -35,11 +35,17 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
private volatile LocalDevice localDevice = null; private volatile LocalDevice localDevice = null;
JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager, JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager,
TimeoutMonitor timeoutMonitor, Executor ioExecutor, BluetoothConnectionFactory<StreamConnection> connectionFactory,
SecureRandom secureRandom, Backoff backoff, Executor ioExecutor,
PluginCallback callback, int maxLatency, int maxIdleTime) { Executor wakefulIoExecutor,
super(connectionManager, timeoutMonitor, ioExecutor, secureRandom, SecureRandom secureRandom,
backoff, callback, maxLatency, maxIdleTime); Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime) {
super(connectionManager, connectionFactory, ioExecutor,
wakefulIoExecutor, secureRandom, backoff, callback,
maxLatency, maxIdleTime);
} }
@Override @Override
@@ -56,22 +62,6 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
return localDevice != null && LocalDevice.isPowerOn(); return localDevice != null && LocalDevice.isPowerOn();
} }
@Override
void enableAdapter() {
// Nothing we can do on this platform
LOG.info("Could not enable Bluetooth");
}
@Override
void disableAdapterIfEnabledByUs() {
// We didn't enable it so we don't need to disable it
}
@Override
void setEnabledByUs() {
// Irrelevant on this platform
}
@Nullable @Nullable
@Override @Override
String getBluetoothAddress() { String getBluetoothAddress() {
@@ -96,7 +86,7 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
@Override @Override
DuplexTransportConnection acceptConnection(StreamConnectionNotifier ss) DuplexTransportConnection acceptConnection(StreamConnectionNotifier ss)
throws IOException { throws IOException {
return wrapSocket(ss.acceptAndOpen()); return connectionFactory.wrapSocket(this, ss.acceptAndOpen());
} }
@Override @Override
@@ -108,7 +98,8 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
DuplexTransportConnection connectTo(String address, String uuid) DuplexTransportConnection connectTo(String address, String uuid)
throws IOException { throws IOException {
String url = makeUrl(address, uuid); String url = makeUrl(address, uuid);
return wrapSocket((StreamConnection) Connector.open(url)); StreamConnection s = (StreamConnection) Connector.open(url);
return connectionFactory.wrapSocket(this, s);
} }
@Override @Override
@@ -120,10 +111,4 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
private String makeUrl(String address, String uuid) { private String makeUrl(String address, String uuid) {
return "btspp://" + address + ":" + uuid + ";name=RFCOMM"; return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
} }
private DuplexTransportConnection wrapSocket(StreamConnection s)
throws IOException {
return new JavaBluetoothTransportConnection(this, connectionLimiter,
timeoutMonitor, s);
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,57 +1,57 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.briarproject.briar" xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android" package="org.briarproject.briar">
xmlns:tools="http://schemas.android.com/tools">
<uses-feature <uses-feature
android:name="android.hardware.bluetooth" android:name="android.hardware.bluetooth"
android:required="false"/> android:required="false" />
<uses-feature <uses-feature
android:name="android.hardware.camera" android:name="android.hardware.camera"
android:required="false"/> android:required="false" />
<uses-feature <uses-feature
android:name="android.hardware.touchscreen" android:name="android.hardware.touchscreen"
android:required="false"/> android:required="false" />
<uses-feature android:name="android.software.leanback" <uses-feature
android:required="false" /> android:name="android.software.leanback"
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!--suppress DeprecatedClassUsageInspection --> <!--suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.USE_FINGERPRINT"/> <uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> <uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission-sdk-23 android:name="android.permission.USE_BIOMETRIC"/> <uses-permission-sdk-23 android:name="android.permission.USE_BIOMETRIC" />
<uses-permission-sdk-23 android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission-sdk-23 android:name="android.permission.FOREGROUND_SERVICE" />
<application <application
android:name="org.briarproject.briar.android.BriarApplicationImpl" android:name="org.briarproject.briar.android.BriarApplicationImpl"
android:allowBackup="false" android:allowBackup="false"
android:banner="@mipmap/tv_banner"
android:icon="@mipmap/ic_launcher_round" android:icon="@mipmap/ic_launcher_round"
android:label="@string/app_name" android:label="@string/app_name"
android:logo="@mipmap/ic_launcher_round" android:logo="@mipmap/ic_launcher_round"
android:banner="@mipmap/tv_banner"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/BriarTheme" android:theme="@style/BriarTheme"
tools:ignore="GoogleAppIndexingWarning" tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"
tools:targetApi="16"> tools:targetApi="16">
<receiver <receiver
android:name="org.briarproject.briar.android.login.SignInReminderReceiver" android:name="org.briarproject.briar.android.login.SignInReminderReceiver"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/> <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
@@ -59,14 +59,13 @@
android:name="org.briarproject.briar.android.BriarService" android:name="org.briarproject.briar.android.BriarService"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="org.briarproject.briar.android.BriarService"/> <action android:name="org.briarproject.briar.android.BriarService" />
</intent-filter> </intent-filter>
</service> </service>
<service <service
android:name="org.briarproject.briar.android.NotificationCleanupService" android:name="org.briarproject.briar.android.NotificationCleanupService"
android:exported="false"> android:exported="false"></service>
</service>
<activity <activity
android:name="org.briarproject.briar.android.reporting.DevReportActivity" android:name="org.briarproject.briar.android.reporting.DevReportActivity"
@@ -76,32 +75,29 @@
android:label="@string/crash_report_title" android:label="@string/crash_report_title"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden"></activity>
</activity>
<activity <activity
android:name="org.briarproject.briar.android.splash.ExpiredActivity" android:name="org.briarproject.briar.android.splash.ExpiredActivity"
android:label="@string/app_name"> android:label="@string/app_name"></activity>
</activity>
<activity <activity
android:name="org.briarproject.briar.android.login.StartupActivity" android:name="org.briarproject.briar.android.login.StartupActivity"
android:label="@string/app_name"> android:label="@string/app_name"></activity>
</activity>
<activity <activity
android:name="org.briarproject.briar.android.account.SetupActivity" android:name="org.briarproject.briar.android.account.SetupActivity"
android:label="@string/setup_title" android:label="@string/setup_title"
android:windowSoftInputMode="adjustResize|stateAlwaysVisible"> android:windowSoftInputMode="adjustResize|stateAlwaysVisible"></activity>
</activity>
<activity <activity
android:name="org.briarproject.briar.android.splash.SplashScreenActivity" android:name="org.briarproject.briar.android.splash.SplashScreenActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/BriarTheme.NoActionBar"> android:theme="@style/BriarTheme.NoActionBar">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" /> <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
@@ -111,17 +107,17 @@
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/BriarTheme.NoActionBar"> android:theme="@style/BriarTheme.NoActionBar">
<intent-filter android:label="@string/add_contact_remotely_title_case"> <intent-filter android:label="@string/add_contact_remotely_title_case">
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE"/> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="briar"/> <data android:scheme="briar" />
</intent-filter> </intent-filter>
<intent-filter android:label="@string/add_contact_remotely_title_case"> <intent-filter android:label="@string/add_contact_remotely_title_case">
<action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain"/> <data android:mimeType="text/plain" />
</intent-filter> </intent-filter>
</activity> </activity>
@@ -133,7 +129,7 @@
android:windowSoftInputMode="adjustResize|stateUnchanged"> android:windowSoftInputMode="adjustResize|stateUnchanged">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -142,7 +138,7 @@
android:theme="@style/BriarTheme.ActionBarOverlay"> android:theme="@style/BriarTheme.ActionBarOverlay">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.conversation.ConversationActivity"/> android:value="org.briarproject.briar.android.conversation.ConversationActivity" />
</activity> </activity>
<activity <activity
@@ -152,7 +148,7 @@
android:windowSoftInputMode="adjustResize|stateAlwaysVisible"> android:windowSoftInputMode="adjustResize|stateAlwaysVisible">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -163,7 +159,7 @@
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -172,7 +168,7 @@
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -181,7 +177,7 @@
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"> android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"/> android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity" />
</activity> </activity>
<activity <activity
@@ -190,7 +186,7 @@
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"> android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"/> android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity" />
</activity> </activity>
<activity <activity
@@ -200,7 +196,7 @@
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"/> android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity" />
</activity> </activity>
<activity <activity
@@ -209,7 +205,7 @@
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -218,7 +214,7 @@
android:parentActivityName="org.briarproject.briar.android.conversation.ConversationActivity"> android:parentActivityName="org.briarproject.briar.android.conversation.ConversationActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.conversation.ConversationActivity"/> android:value="org.briarproject.briar.android.conversation.ConversationActivity" />
</activity> </activity>
<activity <activity
@@ -228,7 +224,7 @@
android:windowSoftInputMode="adjustResize|stateAlwaysVisible"> android:windowSoftInputMode="adjustResize|stateAlwaysVisible">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -239,7 +235,7 @@
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -249,7 +245,7 @@
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.forum.ForumActivity"/> android:value="org.briarproject.briar.android.forum.ForumActivity" />
</activity> </activity>
<activity <activity
@@ -259,7 +255,7 @@
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"/> android:value="org.briarproject.briar.android.blog.BlogActivity" />
</activity> </activity>
<activity <activity
@@ -268,8 +264,7 @@
android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity"> android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.forum.ForumActivity" android:value="org.briarproject.briar.android.forum.ForumActivity" />
/>
</activity> </activity>
<activity <activity
@@ -278,7 +273,7 @@
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"> android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"/> android:value="org.briarproject.briar.android.blog.BlogActivity" />
</activity> </activity>
<activity <activity
@@ -287,7 +282,7 @@
android:theme="@style/BriarTheme.NoActionBar"> android:theme="@style/BriarTheme.NoActionBar">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -297,7 +292,7 @@
android:windowSoftInputMode="adjustResize|stateAlwaysVisible"> android:windowSoftInputMode="adjustResize|stateAlwaysVisible">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"/> android:value="org.briarproject.briar.android.blog.BlogActivity" />
</activity> </activity>
<activity <activity
@@ -307,7 +302,7 @@
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"/> android:value="org.briarproject.briar.android.blog.BlogActivity" />
</activity> </activity>
<activity <activity
@@ -317,7 +312,7 @@
android:windowSoftInputMode="adjustResize|stateAlwaysVisible"> android:windowSoftInputMode="adjustResize|stateAlwaysVisible">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -326,7 +321,7 @@
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -336,7 +331,7 @@
android:theme="@style/BriarTheme.NoActionBar"> android:theme="@style/BriarTheme.NoActionBar">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity> </activity>
<activity <activity
@@ -346,13 +341,12 @@
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.conversation.ConversationActivity"/> android:value="org.briarproject.briar.android.conversation.ConversationActivity" />
</activity> </activity>
<activity <activity
android:name="org.briarproject.briar.android.StartupFailureActivity" android:name="org.briarproject.briar.android.StartupFailureActivity"
android:label="@string/startup_failed_activity_title"> android:label="@string/startup_failed_activity_title"></activity>
</activity>
<activity <activity
android:name="org.briarproject.briar.android.settings.SettingsActivity" android:name="org.briarproject.briar.android.settings.SettingsActivity"
@@ -361,13 +355,22 @@
android:permission="android.permission.READ_NETWORK_USAGE_HISTORY"> android:permission="android.permission.READ_NETWORK_USAGE_HISTORY">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/> <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name="org.briarproject.briar.android.navdrawer.TransportsActivity"
android:label="@string/network_settings_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
</activity>
<activity <activity
android:name="org.briarproject.briar.android.login.ChangePasswordActivity" android:name="org.briarproject.briar.android.login.ChangePasswordActivity"
android:label="@string/change_password" android:label="@string/change_password"
@@ -375,7 +378,7 @@
android:windowSoftInputMode="adjustResize|stateAlwaysVisible"> android:windowSoftInputMode="adjustResize|stateAlwaysVisible">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.settings.SettingsActivity"/> android:value="org.briarproject.briar.android.settings.SettingsActivity" />
</activity> </activity>
<activity <activity
@@ -384,7 +387,7 @@
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity"> android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.settings.SettingsActivity"/> android:value="org.briarproject.briar.android.settings.SettingsActivity" />
</activity> </activity>
<activity <activity
@@ -393,7 +396,7 @@
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity"> android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.settings.SettingsActivity"/> android:value="org.briarproject.briar.android.settings.SettingsActivity" />
</activity> </activity>
<activity <activity
@@ -402,37 +405,35 @@
android:theme="@style/TranslucentTheme"> android:theme="@style/TranslucentTheme">
<!-- this can never have launchMode singleTask or singleInstance! --> <!-- this can never have launchMode singleTask or singleInstance! -->
<intent-filter> <intent-filter>
<action android:name="info.guardianproject.panic.action.TRIGGER"/> <action android:name="info.guardianproject.panic.action.TRIGGER" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name="org.briarproject.briar.android.logout.ExitActivity" android:name="org.briarproject.briar.android.logout.ExitActivity"
android:theme="@android:style/Theme.NoDisplay"> android:theme="@android:style/Theme.NoDisplay"></activity>
</activity>
<activity <activity
android:name=".android.logout.HideUiActivity" android:name=".android.logout.HideUiActivity"
android:theme="@android:style/Theme.NoDisplay"> android:theme="@android:style/Theme.NoDisplay"></activity>
</activity>
<activity <activity
android:name=".android.account.UnlockActivity" android:name=".android.account.UnlockActivity"
android:label="@string/lock_unlock" android:label="@string/lock_unlock"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/BriarTheme.NoActionBar"/> android:theme="@style/BriarTheme.NoActionBar" />
<activity <activity
android:name=".android.contact.add.remote.AddContactActivity" android:name=".android.contact.add.remote.AddContactActivity"
android:label="@string/add_contact_remotely_title_case" android:label="@string/add_contact_remotely_title_case"
android:theme="@style/BriarTheme" android:theme="@style/BriarTheme"
android:windowSoftInputMode="adjustResize|stateHidden"/> android:windowSoftInputMode="adjustResize|stateHidden" />
<activity <activity
android:name=".android.contact.add.remote.PendingContactListActivity" android:name=".android.contact.add.remote.PendingContactListActivity"
android:label="@string/pending_contact_requests" android:label="@string/pending_contact_requests"
android:theme="@style/BriarTheme"/> android:theme="@style/BriarTheme" />
</application> </application>
</manifest> </manifest>

View File

@@ -2,6 +2,7 @@ package org.briarproject.briar.android;
import org.briarproject.bramble.BrambleAndroidEagerSingletons; import org.briarproject.bramble.BrambleAndroidEagerSingletons;
import org.briarproject.bramble.BrambleAndroidModule; import org.briarproject.bramble.BrambleAndroidModule;
import org.briarproject.bramble.BrambleAppComponent;
import org.briarproject.bramble.BrambleCoreEagerSingletons; import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.account.BriarAccountModule; import org.briarproject.bramble.account.BriarAccountModule;
@@ -23,6 +24,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.plugin.tor.CircumventionProvider; import org.briarproject.bramble.plugin.tor.CircumventionProvider;
@@ -73,7 +75,7 @@ import dagger.Component;
}) })
public interface AndroidComponent public interface AndroidComponent
extends BrambleCoreEagerSingletons, BrambleAndroidEagerSingletons, extends BrambleCoreEagerSingletons, BrambleAndroidEagerSingletons,
BriarCoreEagerSingletons, AndroidEagerSingletons { BriarCoreEagerSingletons, AndroidEagerSingletons, BrambleAppComponent {
// Exposed objects // Exposed objects
@CryptoExecutor @CryptoExecutor
@@ -165,6 +167,8 @@ public interface AndroidComponent
FeatureFlags featureFlags(); FeatureFlags featureFlags();
AndroidWakeLockManager wakeLockManager();
void inject(SignInReminderReceiver briarService); void inject(SignInReminderReceiver briarService);
void inject(BriarService briarService); void inject(BriarService briarService);

View File

@@ -1,41 +1,30 @@
package org.briarproject.briar.android; package org.briarproject.briar.android;
import android.app.Application; import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.StrictMode; import android.os.StrictMode;
import com.vanniktech.emoji.RecentEmoji; import com.vanniktech.emoji.RecentEmoji;
import org.briarproject.bramble.api.FeatureFlags; import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyStrengthener; import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.BluetoothConstants; import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TorDirectory;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevConfig; import org.briarproject.bramble.api.reporting.DevConfig;
import org.briarproject.bramble.api.system.AndroidExecutor;
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.Scheduler;
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory; import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory; import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
import org.briarproject.bramble.plugin.tor.AndroidTorPluginFactory; import org.briarproject.bramble.plugin.tor.AndroidTorPluginFactory;
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.android.account.LockManagerImpl; import org.briarproject.briar.android.account.LockManagerImpl;
@@ -50,16 +39,12 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.io.File; import java.io.File;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.net.SocketFactory;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@@ -124,33 +109,21 @@ public class AppModule {
} }
@Provides @Provides
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor, @Singleton
@Scheduler ScheduledExecutorService scheduler, @TorDirectory
AndroidExecutor androidExecutor, SecureRandom random, File provideTorDirectory(Application app) {
SocketFactory torSocketFactory, BackoffFactory backoffFactory, return app.getDir("tor", MODE_PRIVATE);
Application app, NetworkManager networkManager, }
LocationUtils locationUtils, EventBus eventBus,
ResourceProvider resourceProvider, @Provides
CircumventionProvider circumventionProvider, PluginConfig providePluginConfig(AndroidBluetoothPluginFactory bluetooth,
BatteryManager batteryManager, Clock clock, AndroidTorPluginFactory tor, AndroidLanTcpPluginFactory lan) {
TimeoutMonitor timeoutMonitor) {
Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth = new AndroidBluetoothPluginFactory(
ioExecutor, scheduler, androidExecutor, appContext, random,
eventBus, clock, timeoutMonitor, backoffFactory);
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
scheduler, appContext, networkManager, locationUtils, eventBus,
torSocketFactory, backoffFactory, resourceProvider,
circumventionProvider, batteryManager, clock);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
eventBus, backoffFactory, appContext);
Collection<DuplexPluginFactory> duplex = asList(bluetooth, tor, lan);
@NotNullByDefault @NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() { PluginConfig pluginConfig = new PluginConfig() {
@Override @Override
public Collection<DuplexPluginFactory> getDuplexFactories() { public Collection<DuplexPluginFactory> getDuplexFactories() {
return duplex; return asList(bluetooth, tor, lan);
} }
@Override @Override

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.android;
import android.app.Activity; import android.app.Activity;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import org.briarproject.bramble.BrambleApplication;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import java.util.Collection; import java.util.Collection;
@@ -12,7 +13,7 @@ import java.util.logging.LogRecord;
* This exists so that the Application object will not necessarily be cast * This exists so that the Application object will not necessarily be cast
* directly to the Briar application object. * directly to the Briar application object.
*/ */
public interface BriarApplication { public interface BriarApplication extends BrambleApplication {
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class; Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;

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