Compare commits

...

156 Commits

Author SHA1 Message Date
akwizgran
a438050e68 Merge branch '1081-share-app-via-wifi-hotspot' into offline-testing 2021-07-09 10:41:37 +01:00
Torsten Grote
4d0fe24722 Merge branch '1802-sync-via-removable-storage' into offline-testing
# Conflicts:
#	bramble-api/src/main/java/org/briarproject/bramble/api/FeatureFlags.java
#	bramble-core/build.gradle
#	bramble-core/src/test/java/org/briarproject/bramble/test/BrambleCoreIntegrationTestModule.java
#	bramble-core/witness.gradle
#	bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPluginFactory.java
#	briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java
#	briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
#	briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java
#	briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
#	briar-android/src/main/res/values/strings.xml
#	briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt
#	briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt
2021-07-07 15:36:08 -03:00
akwizgran
559b29e8b5 Merge branch '2080-improve-offline-sharing-texts' into '1081-share-app-via-wifi-hotspot'
Improve texts on offline hotspot for a better UX

See merge request briar/briar!1493
2021-07-07 10:59:40 +00:00
Torsten Grote
1b7b285862 Merge remote-tracking branch 'origin/1081-share-app-via-wifi-hotspot' into offline-testing 2021-07-06 17:15:23 -03:00
Torsten Grote
178810241f Merge branch '2031-increase-max-latency' into '1802-sync-via-removable-storage'
Increase max latency of removable drive plugin to 28 days

See merge request briar/briar!1503
2021-07-06 20:06:48 +00:00
Torsten Grote
61c601cb6d Merge branch '2103-check-transport-keys' into '1802-sync-via-removable-storage'
Check whether we have transport keys before trying to send data

See merge request briar/briar!1502
2021-07-06 19:56:24 +00:00
Torsten Grote
2002ad08ca Merge branch '2105-let-contacts-know-drive-not-supported' into '1802-sync-via-removable-storage'
Don't configure the removable drive plugin on API < 19

See merge request briar/briar!1501
2021-07-06 19:53:50 +00:00
akwizgran
d134a67ee9 Increase max latency of removable drive plugin to 28 days. 2021-07-06 17:33:00 +01:00
akwizgran
04cf8e16a9 Check whether we have transport keys before trying to send data. 2021-07-06 16:24:43 +01:00
akwizgran
227d345858 Don't configure the removable drive plugin on API < 19. 2021-07-06 16:13:48 +01:00
Torsten Grote
07ef73ab56 Merge branch '2102-clear-introduction-state' into '1802-sync-via-removable-storage'
Clear keys from session when moving to AWAIT_ACTIVATE state

See merge request briar/briar!1500
2021-07-06 12:26:05 +00:00
Torsten Grote
ea2b1ff4d8 Merge branch '2079-reject-old-timestamps' into '1802-sync-via-removable-storage'
Reject old timestamps when deriving rotation mode keys

See merge request briar/briar!1481
2021-07-06 12:22:42 +00:00
akwizgran
69fac86a0c Clear keys from session when moving to AWAIT_ACTIVATE state. 2021-07-05 18:02:22 +01:00
akwizgran
bd6b6c1cd6 Reject old timestamps when deriving rotation mode keys. 2021-07-05 16:35:32 +01:00
Torsten Grote
be6c868135 Merge branch '2101-dont-increment-attempt-twice' into '1081-share-app-via-wifi-hotspot'
Do not increment the attempt variable twice when requesting group info

See merge request briar/briar!1499
2021-07-05 11:44:51 +00:00
Torsten Grote
041a296666 Merge branch '2090-avoid-double-tap-on-start-sharing-button' into '1081-share-app-via-wifi-hotspot'
Do not allow the user to tap the start sharing button twice quickly

See merge request briar/briar!1495
2021-07-05 11:24:47 +00:00
Torsten Grote
00f03f6587 Merge branch '1981-upgrade-material-tap-target-prompt' into 'master'
Upgrade MaterialTapTargetPrompt library to fix NPE

Closes #1981

See merge request briar/briar!1498
2021-07-05 11:17:39 +00:00
Sebastian Kürten
276eeb1c20 Do not increment the attempt variable twice when requesting group info 2021-07-05 12:48:58 +02:00
akwizgran
c68bd699f1 Upgrade MaterialTapTargetPrompt library to fix NPE. 2021-07-05 10:06:53 +01:00
Sebastian Kürten
d46cfb757e Do not allow the user to tap the start sharing button twice quickly 2021-07-05 10:32:49 +02:00
Torsten Grote
d81c4e7982 Merge branch '2065-transfer-data-ui' into '1802-sync-via-removable-storage'
Implement UI of transfer data feature

See merge request briar/briar!1486
2021-07-02 18:07:57 +00:00
Torsten Grote
8c4d6ed5e4 Remove guidelines for percent based laout width 2021-07-02 14:53:10 -03:00
akwizgran
b21b319cb7 Use guidelines to set image sizes. 2021-07-02 14:53:10 -03:00
Torsten Grote
780f6e97b9 Check if the chosen contact supports removable drive transport
and show message if not
2021-07-02 14:53:10 -03:00
Torsten Grote
1756215183 Combine transfer data graphics to reduce layout complexity
and make scaling work better on smaller screens
2021-07-02 14:53:09 -03:00
Torsten Grote
7e3db6c6df Address review feedback for Transfer Data UI 2021-07-02 14:53:09 -03:00
Torsten Grote
1adf408ade Migrate all image file pickers to ActivityResultLauncher
startActivityForResult is deprecated and the new API is nicer. Also, we can use the same launcher types in various places.
2021-07-02 14:53:09 -03:00
Torsten Grote
d662ae49ee Try to force file chooser to show internal/external storage by default 2021-07-02 14:53:08 -03:00
Torsten Grote
7939c8b213 Calculate percentages for send progress bar 2021-07-02 14:53:08 -03:00
Torsten Grote
1899873da3 Remove manual initial state and oldTask state argument
The latter is now handled via a LiveEvent
2021-07-02 14:53:08 -03:00
Torsten Grote
5beffb21f1 Hide Transfer Data feature behind feature flag 2021-07-02 14:53:05 -03:00
Torsten Grote
0f9afda329 Check if there is data to send and show a message if not 2021-07-02 14:46:08 -03:00
Torsten Grote
c16663b530 Always inform new observers about current state 2021-07-02 14:46:07 -03:00
Torsten Grote
77767b45c9 Re-organize conversations overflow menu 2021-07-02 14:46:07 -03:00
Torsten Grote
79ae8fea8d Transfer Data UI 2021-07-02 14:46:06 -03:00
Daniel Lublin
7e3eb1201a Start of UI for transfer data feature 2021-07-02 14:46:04 -03:00
akwizgran
eb283d81c5 Merge branch '2069-transport-key-agreement-integration-tests' into '1802-sync-via-removable-storage'
Integration tests for transport key agreement client

See merge request briar/briar!1492
2021-07-02 11:11:06 +00:00
Torsten Grote
be3700d364 Remove FIXME in test since we won't fix it this way 2021-06-30 16:57:32 -03:00
Torsten Grote
ccec17f28a Also test that messages arrive and activate keys 2021-06-30 16:49:03 -03:00
Torsten Grote
e8428925ae Add two more tests to TransportKeyAgreementIntegrationTest 2021-06-30 16:49:03 -03:00
Torsten Grote
195123e669 Ensure that private key is not stored anymore 2021-06-30 16:49:02 -03:00
Torsten Grote
abe570e905 Add first integration test for TransportKeyAgreementManager 2021-06-30 16:49:02 -03:00
Torsten Grote
a93b1f18ac Refactor base of BriarIntegrationTest into BrambleIntegrationTest 2021-06-30 16:49:02 -03:00
Torsten Grote
e4bd6fdf95 Put FeatureFlags for tests into a TestFeatureFlagModule 2021-06-30 16:49:01 -03:00
Torsten Grote
793d81bd93 Merge branch '2093-inconsistency-when-navigating-back-to-intro' into '1081-share-app-via-wifi-hotspot'
Don't move to HotspotFragment on rotate when user navigated back to introduction

See merge request briar/briar!1494
2021-06-30 15:16:33 +00:00
Torsten Grote
be637cef65 Merge branch '2091-supported-property' into '1802-sync-via-removable-storage'
Add a transport property to signal support for removable drives

See merge request briar/briar!1496
2021-06-30 14:53:15 +00:00
Sebastian Kürten
9370062e41 Don't move to HotspotFragment on rotate when user navigated back to introduction 2021-06-30 12:07:30 +02:00
akwizgran
8c1f721015 Add method for checking whether contact supports transport. 2021-06-28 16:51:30 +01:00
akwizgran
22ea4ced0d Add transport property to indicate support for removable drives. 2021-06-28 16:51:30 +01:00
Sebastian Kürten
312d31b40e Improve texts on offline hotspot for a better UX 2021-06-28 15:09:40 +02:00
Torsten Grote
b15d42b0cd Merge branch '2087-fix-issue-when-hotspot-activity-and-viewmodel-get-destroyed' into '1081-share-app-via-wifi-hotspot'
Fix bug that occurs when HotspotActivity gets destroyed

See merge request briar/briar!1489
2021-06-25 19:52:56 +00:00
Torsten Grote
9274b8ef4a Merge branch '2084-aborted-introduction-sessions' into '1802-sync-via-removable-storage'
Allow aborted introduction sessions to be retried

See merge request briar/briar!1490
2021-06-23 16:05:31 +00:00
Torsten Grote
7ef4ea51b3 Merge branch '2061-check-clock-sanity-at-startup' into '1802-sync-via-removable-storage'
Check whether system clock is reasonable at startup

See merge request briar/briar!1491
2021-06-23 15:59:52 +00:00
akwizgran
e285f21d1c Check whether system clock is reasonable at startup. 2021-06-23 16:40:42 +01:00
Sebastian Kürten
160cef25af Fix bug that occurs when HotspotActivity gets destroyed
If HotspotActivity gets destroyed, so will be its viewmodel, resulting
in an undefined state when the activity gets created again. While the
fragments will be restored, the view model and hotspot/webserver state
will not. Fix this by resetting the UI to reflect the reset of hotspot
and webserver.
2021-06-23 17:23:59 +02:00
akwizgran
c0293a1327 Merge branch '2070-transport-key-agreement-validator-test' into '1802-sync-via-removable-storage'
Add unit test for transport key agreement validator

See merge request briar/briar!1488
2021-06-23 10:16:52 +00:00
Torsten Grote
035c639aa0 Add TransportKeyAgreementValidatorTest 2021-06-22 17:20:47 -03:00
Torsten Grote
29d31e79c3 Merge branch '2086-fix-margin-on-qr-cardview' into '1081-share-app-via-wifi-hotspot'
Fix background color of cardview for qr code

See merge request briar/briar!1487
2021-06-22 11:49:58 +00:00
Sebastian Kürten
7f7210becd Fix background color of cardview for qr code 2021-06-22 09:46:34 +02:00
akwizgran
ce74fcaab5 Store ID of message that triggered abort. 2021-06-21 16:22:51 +01:00
akwizgran
00407539d3 Bump version numbers for 1.3.5 release. 2021-06-18 11:17:35 +01:00
akwizgran
62014f4d01 Update translations. 2021-06-18 11:16:33 +01:00
Torsten Grote
d4c1e132f7 Merge branch '2077-anything-to-send' into '1802-sync-via-removable-storage'
Add DB method for checking whether there's anything to send

See merge request briar/briar!1485
2021-06-17 13:30:31 +00:00
akwizgran
e7dddda7e5 Merge branch 'tor-0.3.5.15' into 'master'
Upgrade Tor to 0.3.5.15

Closes #2075

See merge request briar/briar!1483
2021-06-17 12:21:01 +00:00
akwizgran
208ff6f3a5 Merge branch '2056-view-pager-crash' into 'master'
Migrate to ViewPager2 to fix weird ViewPager crash

Closes #2056

See merge request briar/briar!1484
2021-06-17 12:07:04 +00:00
akwizgran
4d31465191 Merge branch 'master' into 'tor-0.3.5.15'
# Conflicts:
#   bramble-java/build.gradle
2021-06-17 12:05:34 +00:00
akwizgran
6b976df6a8 Add RemovableDriveManager method. 2021-06-17 13:01:33 +01:00
Torsten Grote
3e4db3b9da Merge branch '2045-flexible-sync' into '1802-sync-via-removable-storage'
Make retransmissions in the sync protocol more flexible

See merge request briar/briar!1482
2021-06-16 17:40:25 +00:00
Torsten Grote
cf987427e0 Migrate to ViewPager2 to fix weird ViewPager crash 2021-06-16 12:44:04 -03:00
akwizgran
0bf59eec20 Add comment explaining second client versioning message. 2021-06-16 16:26:29 +01:00
akwizgran
9f828a2222 Add DB method for checking whether there's anything to send 2021-06-16 16:25:11 +01:00
akwizgran
b11b959fd3 Merge branch 'upgrade-libraries-1.3' into 'master'
Upgrade libraries after 1.3 release

See merge request briar/briar!1455
2021-06-16 13:52:04 +00:00
Torsten Grote
61f660ca1d Upgrade libraries before 1.3 release 2021-06-16 10:40:30 -03:00
Torsten Grote
7be77b8c60 Merge branch '2038-transport-key-agreement-client' into '1802-sync-via-removable-storage'
Add transport key agreement client

See merge request briar/briar!1474
2021-06-16 12:28:00 +00:00
akwizgran
d5853e8403 Add integration test for eager retransmission. 2021-06-16 12:29:49 +01:00
akwizgran
32e9bf01ec Update DB method that gets total size of messages to send. 2021-06-16 12:29:49 +01:00
akwizgran
a5ce400341 Use eager retransmission if the transport is lossy and cheap. 2021-06-16 12:29:49 +01:00
akwizgran
a960bfb2c1 Add tests for eager retransmission. 2021-06-16 12:29:49 +01:00
akwizgran
847650f280 Replace inner classes with lambdas. 2021-06-16 12:29:49 +01:00
akwizgran
77a3199aac Update SimplexOutgoingSession to support sending unacked messages. 2021-06-16 12:29:49 +01:00
akwizgran
9a58b37ce2 Add database methods for sending unacked messages. 2021-06-16 12:29:49 +01:00
akwizgran
6d44521016 Upgrade Tor to 0.3.5.15. 2021-06-16 11:16:10 +01:00
Torsten Grote
608e1eac6b Merge branch '2071-removable-drive-task-refactoring' into '1802-sync-via-removable-storage'
Refactor removable drive task management

See merge request briar/briar!1480
2021-06-15 12:23:27 +00:00
akwizgran
09de768e7e Merge branch '2038-key-manager-methods' into '1802-sync-via-removable-storage'
Key manager changes to support transport key agreement client

See merge request briar/briar!1473
2021-06-15 10:55:31 +00:00
akwizgran
faab80f0ea Hold lock while calling notifyObservers(). 2021-06-15 11:47:10 +01:00
akwizgran
07162cad8b Refactor removable drive tasks. 2021-06-15 11:44:10 +01:00
Torsten Grote
a5a1cdfabb Merge branch '2039-implement-hotspot-error-fragment' into '1081-share-app-via-wifi-hotspot'
Resolve "Implement HotspotErrorFragment"

See merge request briar/briar!1469
2021-06-14 20:23:18 +00:00
Sebastian Kürten
bfcb469d49 Use FragmentContainerView for displaying FallbackFragment 2021-06-14 17:14:50 +02:00
Sebastian Kürten
f8b645d2b1 Improve hotspot error fragment UI
* Use different highlighting for error message
* Improve margins in fragment_hotspot_save_apk.xml
* Address some review feedback
2021-06-14 17:14:35 +02:00
Sebastian Kürten
052eb03c9e Pass error message to feedback activity 2021-06-14 17:13:14 +02:00
Sebastian Kürten
83bf3f4ca7 Create FallbackFragment for alternative apk sharing method 2021-06-14 17:12:02 +02:00
Sebastian Kürten
f9181fa021 Log hotspot errors 2021-06-14 17:10:52 +02:00
Sebastian Kürten
79dae27c24 Wire feedback button to show feedback fragment 2021-06-14 17:10:46 +02:00
akwizgran
7289f5ba8a Merge branch '2059-no-such-group-exception' into 'master'
Check if group still exists before storing MessageId of thread position

Closes #2059

See merge request briar/briar!1477
2021-06-14 14:57:21 +00:00
Torsten Grote
b657c7f347 Ignore NoSuchGroupException when storing MessageId of thread position for removed group 2021-06-10 15:45:31 -03:00
akwizgran
ac0fc21e6e Merge branch '2038-db-methods' into '1802-sync-via-removable-storage'
New DB methods to support transport key agreement client

See merge request briar/briar!1472
2021-06-10 15:28:20 +00:00
akwizgran
dab736ce0e Merge branch '1802-defer-delivery' into '1802-sync-via-removable-storage'
Allow sync clients to defer delivery of messages

See merge request briar/briar!1471
2021-06-10 15:27:08 +00:00
Sebastian Kürten
8ef21637a9 Outline specific error fragment for hotspot 2021-06-09 18:53:59 +02:00
Sebastian Kürten
e3a1fca22e Let HotspotActivity implement BaseFragmentListener 2021-06-09 18:53:54 +02:00
Torsten Grote
ea9a2789ab Move hotspot help ActivityResultLauncher into method 2021-06-09 17:48:17 +02:00
Torsten Grote
cbdbd10cb3 Adapt hotspot buttons to latest design and add a nullability annotation 2021-06-09 17:48:17 +02:00
Torsten Grote
d6f985174a Make HotspotHelpFragment headlines bold 2021-06-09 17:48:17 +02:00
Torsten Grote
d184fbd3fe Handle returned Uri being null 2021-06-09 17:48:17 +02:00
Torsten Grote
ef623370b6 Save the APK as a hotspot fallback 2021-06-09 17:48:17 +02:00
Sebastian Kürten
5ac636d52d Add feature flag for sharing the app via offline hotspot 2021-06-09 17:48:17 +02:00
Sebastian Kürten
f1c71ec5a7 Recommend to undo settings to install apps from unknown sources 2021-06-09 17:48:16 +02:00
Torsten Grote
5cc280be61 Add missing hotspot nullability annotations 2021-06-09 17:48:16 +02:00
Torsten Grote
a5d8faef3c Move savedNetworkConfig into HotspotManager and use constructor injection 2021-06-09 17:48:16 +02:00
Torsten Grote
e22e9dcade Make hotspot SSID and passphrase persistent 2021-06-09 17:48:16 +02:00
Sebastian Kürten
7474ad8606 Use better filename for apk files shared via hotspot 2021-06-09 17:48:16 +02:00
Torsten Grote
1c3d90f7fc Show a snackbar when a peer connected to the hotspot 2021-06-09 17:48:16 +02:00
Torsten Grote
6f8d7167db Don't start hotspot while running and use proper ErrorFragment 2021-06-09 17:48:16 +02:00
Torsten Grote
99da50d37c Port code from Offline hotspot test app 2021-06-09 17:48:15 +02:00
Torsten Grote
15f5c8deee Fix hotspot notification on old APIs 2021-06-09 17:48:15 +02:00
Torsten Grote
7913cd322e Rename tab fragments
and remove redundant NonNull annotations
2021-06-09 17:48:15 +02:00
Torsten Grote
de8ad8f6f9 Show notification while hotspot is active 2021-06-09 17:48:15 +02:00
Torsten Grote
d0bc17e634 Add hotspot troubleshooting info 2021-06-09 17:48:15 +02:00
Torsten Grote
85433611a5 Add offline sharing entry point to Settings/Actions 2021-06-09 17:48:15 +02:00
Torsten Grote
ebd5879761 Let info screens scroll in case of insufficient space 2021-06-09 17:48:15 +02:00
Torsten Grote
b255ab07ae Implement info screens for offline app sharing 2021-06-09 17:48:14 +02:00
Torsten Grote
a86ba50dec Implement intro screen for offline app sharing 2021-06-09 17:48:09 +02:00
Torsten Grote
40865425c5 Merge branch 'fix-padding-of-contact-exchange-error' into 'master'
Fix missing padding for title in contact exchange error fragment

See merge request briar/briar!1478
2021-06-09 11:56:57 +00:00
Sebastian Kürten
f7249f5e84 Fix missing padding for title in contact exchange error fragment 2021-06-09 13:45:32 +02:00
akwizgran
a72e92de24 Timestamp isn't needed for deriving root key. 2021-06-09 10:08:07 +01:00
akwizgran
5dfd9e3546 Make tests more readable. 2021-06-08 17:13:18 +01:00
akwizgran
e05575b956 Add unit tests for addRotationKeys() methods. 2021-06-08 15:51:29 +01:00
akwizgran
c703d90636 Remove unused remote timestamp from session. 2021-06-01 14:50:14 +01:00
akwizgran
e228b9fcbf Add transport key agreement client. 2021-06-01 14:18:02 +01:00
akwizgran
6e6cadd3ad Refactor KeyManager startup so managers are created earlier. 2021-06-01 14:17:12 +01:00
akwizgran
9cc8d44778 Add a key manager method for adding a single set of transport keys. 2021-06-01 11:34:27 +01:00
akwizgran
ee6f571c31 Add a DB method for checking whether transport keys exist. 2021-06-01 11:34:26 +01:00
akwizgran
2ac3bdd3ae Add database method for getting transports with keys. 2021-06-01 11:34:26 +01:00
akwizgran
e35ffe0cf0 Add javadocs for message states. 2021-06-01 11:33:06 +01:00
akwizgran
8a04d8edc4 Allow sync clients to defer delivery of messages. 2021-06-01 11:24:55 +01:00
Torsten Grote
cc04fb05c2 Merge branch '1971-tor-0.3.5.14' into 'master'
Upgrade Tor to 0.3.5.14

Closes #1971

See merge request briar/briar!1470
2021-05-31 12:02:58 +00:00
akwizgran
58eb1d9e24 Upgrade Tor to 0.3.5.14. 2021-05-28 11:45:21 +01:00
akwizgran
76b7e6fecf Bump version numbers for 1.3.4 release. 2021-05-19 10:47:08 +01:00
akwizgran
980940a7cf Update translations. 2021-05-19 10:46:21 +01:00
akwizgran
9a021daae8 Merge branch '2043-blog-scrolling' into 'master'
Automatically scroll to our own blog posts

Closes #2043

See merge request briar/briar!1462
2021-05-19 09:35:10 +00:00
Torsten Grote
cc9904a454 Automatically scroll to our own blog posts 2021-05-18 15:39:22 -03:00
Torsten Grote
b0faab9395 Merge branch 'remove-failed-bridges' into 'master'
Remove three failed bridges

See merge request briar/briar!1460
2021-05-17 17:37:58 +00:00
akwizgran
f1198b47fd Remove three failed bridges. 2021-05-17 16:36:00 +01:00
akwizgran
37f2ab555f Update translations. 2021-05-17 16:26:22 +01:00
akwizgran
61ca2a391b Merge branch 'create-testdata-zero-values' into 'master'
Add button to zero values when creating test data

See merge request briar/briar!1447
2021-05-10 12:05:26 +00:00
akwizgran
88537b9323 Merge branch '2032-connect-via-bt-crash' into 'master'
Remove duplicate code due to merge mistake

Closes #2032

See merge request briar/briar!1456
2021-05-10 11:55:38 +00:00
Daniel Lublin
e6c004e8f6 Add button to zero values when creating test data 2021-05-10 13:46:49 +02:00
akwizgran
dc7290dab7 Remove duplicate code due to merge mistake. 2021-05-10 12:29:28 +01:00
akwizgran
9dff8bd64a Merge branch '2005-connect-via-bt-error' into 'master'
Connect via Bluetooth: Wait before showing an error Toast

See merge request briar/briar!1446
2021-05-06 13:14:32 +00:00
akwizgran
35c2f60129 Merge branch 'master' into '2005-connect-via-bt-error'
# Conflicts:
#   briar-android/src/main/java/org/briarproject/briar/android/conversation/BluetoothConnecter.java
2021-05-06 13:03:46 +00:00
akwizgran
471a2372c8 Merge branch '2027-pause-polling-when-doing-connect-via-bt' into 'master'
Disable polling while doing connect-via-BT

Closes #2027

See merge request briar/briar!1450
2021-05-06 12:59:38 +00:00
Daniel Lublin
fba028db03 Disable polling while doing connect-via-BT 2021-05-06 11:13:35 +02:00
Torsten Grote
c647c52638 Merge branch '2009-xiaomi-setup-screen' into 'master'
Show instructions for locking Briar to the Xiaomi recent apps list

Closes #2009

See merge request briar/briar!1451
2021-05-05 16:39:55 +00:00
akwizgran
cad5edcf86 Move input state methods to UiUtils. 2021-05-05 16:57:25 +01:00
akwizgran
38f70bb6be Add full stop. 2021-05-05 13:02:56 +01:00
akwizgran
33bdc81b3e Show instructions for locking Briar to the Xiaomi recent apps list. 2021-05-05 12:37:23 +01:00
Torsten Grote
552b9ef21a Wait for an incoming connection before showing an error Toast 2021-04-30 16:01:54 -03:00
288 changed files with 10358 additions and 1755 deletions

View File

@@ -15,8 +15,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 30 targetSdkVersion 30
versionCode 10303 versionCode 10305
versionName "1.3.3" versionName "1.3.5"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -42,18 +42,18 @@ configurations {
dependencies { dependencies {
implementation project(path: ':bramble-core', configuration: 'default') implementation project(path: ':bramble-core', configuration: 'default')
tor 'org.briarproject:tor-android:0.3.5.13@zip' tor 'org.briarproject:tor-android:0.3.5.15'
tor 'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a@zip' tor 'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24' annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'javax.annotation:jsr250-api:1.0'
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation 'junit:junit:4.12' testImplementation "junit:junit:$junit_version"
testImplementation "org.jmock:jmock:2.8.2" testImplementation "org.jmock:jmock:$jmock_version"
testImplementation "org.jmock:jmock-junit4:2.8.2" testImplementation "org.jmock:jmock-junit4:$jmock_version"
testImplementation "org.jmock:jmock-legacy:2.8.2" testImplementation "org.jmock:jmock-legacy:$jmock_version"
} }
def torBinariesDir = 'src/main/res/raw' def torBinariesDir = 'src/main/res/raw'

View File

@@ -87,7 +87,7 @@ class AndroidBluetoothPlugin extends
Clock clock, Clock clock,
Backoff backoff, Backoff backoff,
PluginCallback callback, PluginCallback callback,
int maxLatency, long maxLatency,
int maxIdleTime) { int maxIdleTime) {
super(connectionLimiter, connectionFactory, ioExecutor, super(connectionLimiter, connectionFactory, ioExecutor,
wakefulIoExecutor, secureRandom, backoff, callback, wakefulIoExecutor, secureRandom, backoff, callback,

View File

@@ -75,7 +75,7 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return MAX_LATENCY; return MAX_LATENCY;
} }

View File

@@ -4,6 +4,7 @@ import android.app.Application;
import android.net.Uri; import android.net.Uri;
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.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import java.io.IOException; import java.io.IOException;
@@ -21,8 +22,9 @@ class AndroidRemovableDrivePlugin extends RemovableDrivePlugin {
private final Application app; private final Application app;
AndroidRemovableDrivePlugin(Application app, int maxLatency) { AndroidRemovableDrivePlugin(Application app, PluginCallback callback,
super(maxLatency); long maxLatency) {
super(callback, maxLatency);
this.app = app; this.app = app;
} }

View File

@@ -35,13 +35,13 @@ public class AndroidRemovableDrivePluginFactory implements
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return MAX_LATENCY; return MAX_LATENCY;
} }
@Nullable @Nullable
@Override @Override
public SimplexPlugin createPlugin(PluginCallback callback) { public SimplexPlugin createPlugin(PluginCallback callback) {
return new AndroidRemovableDrivePlugin(app, MAX_LATENCY); return new AndroidRemovableDrivePlugin(app, callback, MAX_LATENCY);
} }
} }

View File

@@ -67,7 +67,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
Application app, Application app,
Backoff backoff, Backoff backoff,
PluginCallback callback, PluginCallback callback,
int maxLatency, long maxLatency,
int maxIdleTime, int maxIdleTime,
int connectionTimeout) { int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency, super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,

View File

@@ -55,7 +55,7 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return MAX_LATENCY; return MAX_LATENCY;
} }

View File

@@ -68,7 +68,7 @@ class AndroidTorPlugin extends TorPlugin {
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, PluginCallback callback,
String architecture, String architecture,
int maxLatency, long maxLatency,
int maxIdleTime, int maxIdleTime,
File torDirectory) { File torDirectory) {
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils, super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,

View File

@@ -94,7 +94,7 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return MAX_LATENCY; return MAX_LATENCY;
} }

View File

@@ -10,17 +10,20 @@ import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Scanner;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static java.lang.Runtime.getRuntime;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@@ -118,4 +121,17 @@ public class AndroidUtils {
public static String[] getSupportedImageContentTypes() { public static String[] getSupportedImageContentTypes() {
return new String[] {"image/jpeg", "image/png", "image/gif"}; return new String[] {"image/jpeg", "image/png", "image/gif"};
} }
@Nullable
public static String getSystemProperty(String propName) {
try {
Process p = getRuntime().exec("getprop " + propName);
Scanner s = new Scanner(p.getInputStream());
String line = s.nextLine();
s.close();
return line;
} catch (SecurityException | IOException e) {
return null;
}
}
} }

View File

@@ -1,42 +1,43 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
'com.android.tools.analytics-library:protos:27.1.1:protos-27.1.1.jar:13f77e73762e58ab372d140b3a6be6903aea9775b62dd14fbc62d4cc7069c9a4', 'com.android.tools.analytics-library:protos:27.1.3:protos-27.1.3.jar:0d9e6cff60b318baac250b6f5bb076a8161103338bf2749cdf1db8a5a13a1f12',
'com.android.tools.analytics-library:shared:27.1.1:shared-27.1.1.jar:82930a52001410e97d809930b670f4de3002286975f046b9de5f6b777b06d366', 'com.android.tools.analytics-library:shared:27.1.3:shared-27.1.3.jar:10d2a51d8f89ff4ac849888e5a9c60b10e879c30d78545ec1da4d3df7bd56ae4',
'com.android.tools.analytics-library:tracker:27.1.1:tracker-27.1.1.jar:31bc5a00be0055bac89c9b2f34751883e987cd89e3ac1783720645c164f591d9', 'com.android.tools.analytics-library:tracker:27.1.3:tracker-27.1.3.jar:589b355a2ba796cbc0a2b2295737de6661f078262e5f87cd6f540b8d011e5ebb',
'com.android.tools.build:aapt2-proto:4.1.0-alpha01-6193524:aapt2-proto-4.1.0-alpha01-6193524.jar:17e75523e1e92dd4f222c7368ee41df9e964a508232f591e265d0c499baf9dca', 'com.android.tools.build:aapt2-proto:4.1.0-alpha01-6193524:aapt2-proto-4.1.0-alpha01-6193524.jar:17e75523e1e92dd4f222c7368ee41df9e964a508232f591e265d0c499baf9dca',
'com.android.tools.build:apksig:4.1.1:apksig-4.1.1.jar:e0a69da9e5a03986d608b45bbf954ef0e6a0b3f58c1b8315bd169ec08b279e72', 'com.android.tools.build:apksig:4.1.3:apksig-4.1.3.jar:a851980c678ff7a6785388b9a9e8cc094788ce3c4a985ad2b19c2028fd3c631a',
'com.android.tools.build:apkzlib:4.1.1:apkzlib-4.1.1.jar:ba4b5e419b6be0130eae7f8301c3a551ad3976f487d2e0c6852ebb175ac41127', 'com.android.tools.build:apkzlib:4.1.3:apkzlib-4.1.3.jar:475903065e7e83a8c1ba78d267c97a54dc5a04d768b535093850423d7b11f2c8',
'com.android.tools.build:builder-model:4.1.1:builder-model-4.1.1.jar:e95c99cc298ad67b8deb6ced99c51abc8f59afebedad044b1a10dde14646a4dd', 'com.android.tools.build:builder-model:4.1.3:builder-model-4.1.3.jar:2624a1436c3ab39dd91d3ecf9409a594b0f89ea5cab255f2e9ff11f5ee03d274',
'com.android.tools.build:builder-test-api:4.1.1:builder-test-api-4.1.1.jar:464f596ab261c051c3847406748e843770dea123f6fa5fee8a9390644e709b7a', 'com.android.tools.build:builder-test-api:4.1.3:builder-test-api-4.1.3.jar:3d2af66726b06b53b8d6d497efcee39ff9f77eb2f8d2cce38b31502383a40d2c',
'com.android.tools.build:builder:4.1.1:builder-4.1.1.jar:0f78d4759d2f7b57b95865522ec34596ba419b9982f3b25e3449213f9c98b80d', 'com.android.tools.build:builder:4.1.3:builder-4.1.3.jar:a40426cd6d68f6a722ef4950058c075e4547025e8c2fd78e732ad89f15176f84',
'com.android.tools.build:gradle-api:4.1.1:gradle-api-4.1.1.jar:d42e6b539e4c1353ad3546e75ec8ce11a017b97481023e8ea18577eefe374358', 'com.android.tools.build:gradle-api:4.1.3:gradle-api-4.1.3.jar:11b1fb9de658bdcf9290b1c1517060d0c4d93f2b27975934989ca4ac890bc077',
'com.android.tools.build:manifest-merger:27.1.1:manifest-merger-27.1.1.jar:7a45fa143687859bb2e5a961dcf6ee88094d3853de0cb543dc03dbcb0f4b554b', 'com.android.tools.build:manifest-merger:27.1.3:manifest-merger-27.1.3.jar:ce8d4009b1f1584777a7ffa1da3b0551dc316bc8e08112e442c352af70f46f2d',
'com.android.tools.ddms:ddmlib:27.1.1:ddmlib-27.1.1.jar:da6e4bd834b6a85dae8019039849d8bd96933347dfbf460df74913ddade6e40a', 'com.android.tools.ddms:ddmlib:27.1.3:ddmlib-27.1.3.jar:8f76e8236d2b9eebf26378746dad025c4c7c056a02e133dae4ddef47b283c710',
'com.android.tools.external.com-intellij:intellij-core:27.1.1:intellij-core-27.1.1.jar:2591a7363c4443c59bf9f793730acafce9d6ec3076e2f46716edaf53a41b6fb6', 'com.android.tools.external.com-intellij:intellij-core:27.1.3:intellij-core-27.1.3.jar:652814fa099b4746fb6f10e19718e476952e8b5bac24e17d914f90650ad21808',
'com.android.tools.external.com-intellij:kotlin-compiler:27.1.1:kotlin-compiler-27.1.1.jar:5054ae770ba788f110303c65abd6b1fa28eccf52dee1274510e201b2b81885c8', 'com.android.tools.external.com-intellij:kotlin-compiler:27.1.3:kotlin-compiler-27.1.3.jar:8d7a78d5efd213c5e467e42bd205582aad73ffc77ee5dc18eb1361c9af72f125',
'com.android.tools.external.org-jetbrains:uast:27.1.1:uast-27.1.1.jar:54cd8f6886a9d2f5641659dd5c91f626629672cd48301f7f0bd6aad9bd448714', 'com.android.tools.external.org-jetbrains:uast:27.1.3:uast-27.1.3.jar:aea53944a1ac6a05f12297b55290e8cbecfe54c4166260cfba4405823bfe1c78',
'com.android.tools.layoutlib:layoutlib-api:27.1.1:layoutlib-api-27.1.1.jar:8a9a22e3b309521ea83b724e5a89cfdac6076f52d675c0e17d77b05527bc0f8c', 'com.android.tools.layoutlib:layoutlib-api:27.1.3:layoutlib-api-27.1.3.jar:23875ce0a8429f33a4e86cc358f658faa0ba9c576f5f05760e544b453d67d04b',
'com.android.tools.lint:lint-api:27.1.1:lint-api-27.1.1.jar:c1d8176094cb0478786070d40533efb578ebc53529a82f6ef5bee879bdca418b', 'com.android.tools.lint:lint-api:27.1.3:lint-api-27.1.3.jar:97666be32bcadacd944416ea334a9575ef8f4ad0c8f333151491ff4a7df43e1c',
'com.android.tools.lint:lint-checks:27.1.1:lint-checks-27.1.1.jar:3899c91e00bd059b40c31a9ca00cd0f8303191947608735ae1b657323693fb61', 'com.android.tools.lint:lint-checks:27.1.3:lint-checks-27.1.3.jar:b2d71ae84a31490fe9ff26c706163fe245b2aea98e3eb747214c1085df444708',
'com.android.tools.lint:lint-gradle-api:27.1.1:lint-gradle-api-27.1.1.jar:26aa89d38b9825cc73229daa82a68875801c8b8491f30497ce62aff1f206eb0d', 'com.android.tools.lint:lint-gradle-api:27.1.3:lint-gradle-api-27.1.3.jar:e54131c287a2954e6ed78a3351e5e10e35a1da2f09ac443bf44b705c71b63a4d',
'com.android.tools.lint:lint-gradle:27.1.1:lint-gradle-27.1.1.jar:f7355823ead869f4d28184ba28b7a0c693b507519a2d3705bb9848a0f35b3756', 'com.android.tools.lint:lint-gradle:27.1.3:lint-gradle-27.1.3.jar:6a79e48943649d63665db7b17dbaff7af93e94ab9b15072f1a4d90486294ee9f',
'com.android.tools.lint:lint-model:27.1.1:lint-model-27.1.1.jar:bc23c0c413bdfca59dac2cd56b870d8360d009e9ec0d365e71f774bcf127971d', 'com.android.tools.lint:lint-model:27.1.3:lint-model-27.1.3.jar:acb9e792db7000e38e3c3ca21a9b14f2de6549d7a3fc92a97ffba3d06345e5bf',
'com.android.tools.lint:lint:27.1.1:lint-27.1.1.jar:2f6038a5398a42bd591883c3f5e5894f4ec52ca1c3683bf94fa8553c1700af81', 'com.android.tools.lint:lint:27.1.3:lint-27.1.3.jar:5a2e69d0901a3a476a5b2d5001de755868113145f5f6aa557750cfad5389a44b',
'com.android.tools:annotations:27.1.1:annotations-27.1.1.jar:ff28c504d2acb9fd1a5ffbd97ae85cf59ee18c76927525aad250509bccf2cab1', 'com.android.tools:annotations:27.1.3:annotations-27.1.3.jar:904dd771883496d5dfc86619ab2555968ea4e8a29d7a5f4f7cae6fbf5429f8f5',
'com.android.tools:common:27.1.1:common-27.1.1.jar:63d9a2a9ad6d278db319f3749b9f50bdf5457ef7020074a1bebe124e714b535c', 'com.android.tools:common:27.1.3:common-27.1.3.jar:17ab4728e3ea50f047dd5937f0faf35f2c5416962ed74891057087ddc328bf96',
'com.android.tools:dvlib:27.1.1:dvlib-27.1.1.jar:998a54201fc1cefee5f2399215e95c42b1f64f9e1d8f4452eb8255c68ba5440f', 'com.android.tools:dvlib:27.1.3:dvlib-27.1.3.jar:cead1c0c356cbe43e6855b0330fe09ef4bec2c72e22bdb4c6e7cf7e6b1dfbc37',
'com.android.tools:repository:27.1.1:repository-27.1.1.jar:d25b74ccabf4d876903efb375e9af6fb380d8ae0445bb74bbdcc225c1e37fa1d', 'com.android.tools:repository:27.1.3:repository-27.1.3.jar:99de1a178855b56b8cd91a56296f1e0a9399c445e6acc51f1d2927947cc472cb',
'com.android.tools:sdk-common:27.1.1:sdk-common-27.1.1.jar:4473ae97d0ef7061ee1de61041d5aa97405ae08e44c09cf7bb278b42e4b97c7c', 'com.android.tools:sdk-common:27.1.3:sdk-common-27.1.3.jar:b591e2aa0f1be600795f5c9e2bf81cba9b052bee452fc86c3362b5dd9e427a14',
'com.android.tools:sdklib:27.1.1:sdklib-27.1.1.jar:08e6b83961ac9724b3c1e3d0eff971f13be6701292c77914b8794480f3391250', 'com.android.tools:sdklib:27.1.3:sdklib-27.1.3.jar:ad6c08a45fe2904d05656bdddf9f623fa5c1d16bbd7b8d6a270a0734136ae02e',
'com.android:signflinger:4.1.1:signflinger-4.1.1.jar:0c66825988873ec2d51057fa463f54a8f18fc7326ff4530b9da363b71e97ce60', 'com.android:signflinger:4.1.3:signflinger-4.1.3.jar:f3103b55ccdc8dd9ee2517eb26af93b904d41303726594372d0df59d51156e5c',
'com.android:zipflinger:4.1.1:zipflinger-4.1.1.jar:0a8c3e52ac13dd031236f9fb5ba4408b1d5dcd12325a05440b36da09d8881446', 'com.android:zipflinger:4.1.3:zipflinger-4.1.3.jar:48569896c0497268308a8014c66eb0f2bace2b9e2fc9390f3012823fb86387d5',
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81', 'com.google.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81',
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f', 'com.google.dagger:dagger-compiler:2.33:dagger-compiler-2.33.jar:aa8a0d8370c578fd6999802d0d90b9829377a46d2c1141e11b8f737970e7155e',
'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a', 'com.google.dagger:dagger-producers:2.33:dagger-producers-2.33.jar:5897f0b6eef799c2adfe3ccacc58c0fb374d58acb063c3ebe5366c38a8bce5c8',
'com.google.dagger:dagger-spi:2.24:dagger-spi-2.24.jar:c038445d14dbcb4054e61bf49e05009edf26fce4fdc7ec1a9db544784f68e718', 'com.google.dagger:dagger-spi:2.33:dagger-spi-2.33.jar:e2dcab2221b8afb9556ef0a1c83b0bd5f42552e254322a257330f754cdbbb9d4',
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64', 'com.google.dagger:dagger:2.33:dagger-2.33.jar:d8798c5b8cf6b125234e33af5c6293bb9f2208ce29b57924c35b8c0be7b6bdcb',
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a', 'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a',
'com.google.errorprone:error_prone_annotations:2.3.2:error_prone_annotations-2.3.2.jar:357cd6cfb067c969226c442451502aee13800a24e950fdfde77bcdb4565a668d', 'com.google.errorprone:error_prone_annotations:2.3.2:error_prone_annotations-2.3.2.jar:357cd6cfb067c969226c442451502aee13800a24e950fdfde77bcdb4565a668d',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
@@ -50,7 +51,7 @@ dependencyVerification {
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd', 'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
'com.google.protobuf:protobuf-java:3.10.0:protobuf-java-3.10.0.jar:161d7d61a8cb3970891c299578702fd079646e032329d6c2cabf998d191437c9', 'com.google.protobuf:protobuf-java:3.10.0:protobuf-java-3.10.0.jar:161d7d61a8cb3970891c299578702fd079646e032329d6c2cabf998d191437c9',
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439', 'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90', 'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0', 'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce', 'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
'com.sun.istack:istack-commons-runtime:3.0.7:istack-commons-runtime-3.0.7.jar:6443e10ba2e259fb821d9b6becf10db5316285fc30c53cec9d7b19a3877e7fdf', 'com.sun.istack:istack-commons-runtime:3.0.7:istack-commons-runtime-3.0.7.jar:6443e10ba2e259fb821d9b6becf10db5316285fc30c53cec9d7b19a3877e7fdf',
@@ -62,21 +63,21 @@ dependencyVerification {
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f', 'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'javax.xml.bind:jaxb-api:2.3.1:jaxb-api-2.3.1.jar:88b955a0df57880a26a74708bc34f74dcaf8ebf4e78843a28b50eae945732b06', 'javax.xml.bind:jaxb-api:2.3.1:jaxb-api-2.3.1.jar:88b955a0df57880a26a74708bc34f74dcaf8ebf4e78843a28b50eae945732b06',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd', 'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5', 'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2', 'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6', 'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7', 'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7',
'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd', 'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e', 'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca', 'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349', 'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a:obfs4proxy-android-0.0.12-dev-40245c4a.zip:8ab05a8f8391be2cb5ab2b665c281a06d9e3a756bd0f95a40a36ca927866ea82', 'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a:obfs4proxy-android-0.0.12-dev-40245c4a.zip:8ab05a8f8391be2cb5ab2b665c281a06d9e3a756bd0f95a40a36ca927866ea82',
'org.briarproject:tor-android:0.3.5.13:tor-android-0.3.5.13.zip:e0978db136731dae07774b722970cdae1e462fb5adc82845dd80a7e2d87f356c', 'org.briarproject:tor-android:0.3.5.15:tor-android-0.3.5.15.jar:560c5070166300b396cb2f28d82d9f639ee1fb5479096a3cef67da56d39937ad',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a', 'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
'org.checkerframework:checker-qual:2.8.1:checker-qual-2.8.1.jar:9103499008bcecd4e948da29b17864abb64304e15706444ae209d17ebe0575df', 'org.checkerframework:checker-qual:2.8.1:checker-qual-2.8.1.jar:9103499008bcecd4e948da29b17864abb64304e15706444ae209d17ebe0575df',
@@ -85,26 +86,31 @@ dependencyVerification {
'org.codehaus.mojo:animal-sniffer-annotations:1.18:animal-sniffer-annotations-1.18.jar:47f05852b48ee9baefef80fa3d8cea60efa4753c0013121dd7fe5eef2e5c729d', 'org.codehaus.mojo:animal-sniffer-annotations:1.18:animal-sniffer-annotations-1.18.jar:47f05852b48ee9baefef80fa3d8cea60efa4753c0013121dd7fe5eef2e5c729d',
'org.glassfish.jaxb:jaxb-runtime:2.3.1:jaxb-runtime-2.3.1.jar:45fecfa5c8217ce1f3652ab95179790ec8cc0dec0384bca51cbeb94a293d9f2f', 'org.glassfish.jaxb:jaxb-runtime:2.3.1:jaxb-runtime-2.3.1.jar:45fecfa5c8217ce1f3652ab95179790ec8cc0dec0384bca51cbeb94a293d9f2f',
'org.glassfish.jaxb:txw2:2.3.1:txw2-2.3.1.jar:34975dde1c6920f1a39791142235689bc3cd357e24d05edd8ff93b885bd68d60', 'org.glassfish.jaxb:txw2:2.3.1:txw2-2.3.1.jar:34975dde1c6920f1a39791142235689bc3cd357e24d05edd8ff93b885bd68d60',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', 'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c', 'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
'org.jetbrains.kotlin:kotlin-reflect:1.3.72:kotlin-reflect-1.3.72.jar:a188d9367de1c4ee9479db630985c0597b20709c83161b1430d24edb27e38c40', 'org.jetbrains.kotlin:kotlin-reflect:1.3.72:kotlin-reflect-1.3.72.jar:a188d9367de1c4ee9479db630985c0597b20709c83161b1430d24edb27e38c40',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72:kotlin-stdlib-common-1.3.72.jar:5e7d1552863e480c1628b1cc39ce230ef829f5b7230106215a05acda5172203a', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72:kotlin-stdlib-common-1.3.72.jar:5e7d1552863e480c1628b1cc39ce230ef829f5b7230106215a05acda5172203a',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20:kotlin-stdlib-common-1.4.20.jar:a7112c9b3cefee418286c9c9372f7af992bd1e6e030691d52f60cb36dbec8320',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72:kotlin-stdlib-jdk7-1.3.72.jar:40566c0c08d414b9413ba556ff7f8a0b04b98b9f0f424d122dd2088510efccc4', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72:kotlin-stdlib-jdk7-1.3.72.jar:40566c0c08d414b9413ba556ff7f8a0b04b98b9f0f424d122dd2088510efccc4',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72:kotlin-stdlib-jdk8-1.3.72.jar:133da70cfc07b56094282eac5c59bccd59f167ee2ead22e5282876d8bc10bf95', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72:kotlin-stdlib-jdk8-1.3.72.jar:133da70cfc07b56094282eac5c59bccd59f167ee2ead22e5282876d8bc10bf95',
'org.jetbrains.kotlin:kotlin-stdlib:1.3.72:kotlin-stdlib-1.3.72.jar:3856a7349ebacd6d1be6802b2fed9c4dc2c5a564ea92b6b945ac988243d4b16b', 'org.jetbrains.kotlin:kotlin-stdlib:1.3.72:kotlin-stdlib-1.3.72.jar:3856a7349ebacd6d1be6802b2fed9c4dc2c5a564ea92b6b945ac988243d4b16b',
'org.jetbrains.kotlin:kotlin-stdlib:1.4.20:kotlin-stdlib-1.4.20.jar:b8ab1da5cdc89cb084d41e1f28f20a42bd431538642a5741c52bbfae3fa3e656',
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0:kotlinx-metadata-jvm-0.1.0.jar:9753bb39efef35957c5c15df9a3cb769aabf2cdfa74b47afcb7760e5146be3b5',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7', 'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478', 'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c', 'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b', 'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b',
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760', 'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16', 'org.jmock:jmock-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04',
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
'org.jvnet.staxex:stax-ex:1.8:stax-ex-1.8.jar:95b05d9590af4154c6513b9c5dc1fb2e55b539972ba0a9ef28e9a0c01d83ad77', 'org.jvnet.staxex:stax-ex:1.8:stax-ex-1.8.jar:95b05d9590af4154c6513b9c5dc1fb2e55b539972ba0a9ef28e9a0c01d83ad77',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80', 'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
'org.ow2.asm:asm-analysis:7.0:asm-analysis-7.0.jar:e981f8f650c4d900bb033650b18e122fa6b161eadd5f88978d08751f72ee8474', 'org.ow2.asm:asm-analysis:7.0:asm-analysis-7.0.jar:e981f8f650c4d900bb033650b18e122fa6b161eadd5f88978d08751f72ee8474',
'org.ow2.asm:asm-commons:7.0:asm-commons-7.0.jar:fed348ef05958e3e846a3ac074a12af5f7936ef3d21ce44a62c4fa08a771927d', 'org.ow2.asm:asm-commons:7.0:asm-commons-7.0.jar:fed348ef05958e3e846a3ac074a12af5f7936ef3d21ce44a62c4fa08a771927d',
'org.ow2.asm:asm-tree:7.0:asm-tree-7.0.jar:cfd7a0874f9de36a999c127feeadfbfe6e04d4a71ee954d7af3d853f0be48a6c', 'org.ow2.asm:asm-tree:7.0:asm-tree-7.0.jar:cfd7a0874f9de36a999c127feeadfbfe6e04d4a71ee954d7af3d853f0be48a6c',
'org.ow2.asm:asm-util:7.0:asm-util-7.0.jar:75fbbca440ef463f41c2b0ab1a80abe67e910ac486da60a7863cbcb5bae7e145', 'org.ow2.asm:asm-util:7.0:asm-util-7.0.jar:75fbbca440ef463f41c2b0ab1a80abe67e910ac486da60a7863cbcb5bae7e145',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf', 'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf',
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
] ]
} }

View File

@@ -7,13 +7,13 @@ apply plugin: 'witness'
apply from: 'witness.gradle' apply from: 'witness.gradle'
dependencies { dependencies {
implementation "com.google.dagger:dagger:2.24" implementation "com.google.dagger:dagger:$dagger_version"
implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'com.google.code.findbugs:jsr305:3.0.2'
testImplementation 'junit:junit:4.12' testImplementation "junit:junit:$junit_version"
testImplementation "org.jmock:jmock:2.8.2" testImplementation "org.jmock:jmock:$jmock_version"
testImplementation "org.jmock:jmock-junit4:2.8.2" testImplementation "org.jmock:jmock-junit4:$jmock_version"
testImplementation "org.jmock:jmock-legacy:2.8.2" testImplementation "org.jmock:jmock-legacy:$jmock_version"
signature 'org.codehaus.mojo.signature:java16:1.1@signature' signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }

View File

@@ -12,4 +12,8 @@ public interface FeatureFlags {
boolean shouldEnableDisappearingMessages(); boolean shouldEnableDisappearingMessages();
boolean shouldEnableConnectViaBluetooth(); boolean shouldEnableConnectViaBluetooth();
boolean shouldEnableShareAppViaOfflineHotspot();
boolean shouldEnableTransferData();
} }

View File

@@ -32,28 +32,31 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook {
/** /**
* Called once for each incoming message that passes validation. * Called once for each incoming message that passes validation.
* <p>
* If an unexpected exception occurs while handling data that is assumed
* to be valid (e.g. locally created metadata), it may be sensible to
* rethrow the unexpected exception as a DbException so that delivery is
* attempted again at next startup. This will allow delivery to succeed if
* the unexpected exception was caused by a bug that has subsequently been
* fixed.
* *
* @param txn A read-write transaction * @param txn A read-write transaction
* @return Whether or not this message should be shared * @throws DbException if a database error occurs while delivering the
* @throws DbException Should only be used for real database errors. * message. Delivery will be attempted again at next startup. Throwing
* If this is thrown, delivery will be attempted again at next startup, * this exception has the same effect as returning
* whereas if a FormatException is thrown, the message will be permanently * {@link DeliveryAction#DEFER}.
* invalidated. * @throws FormatException if the message is invalid in the context of its
* @throws FormatException Use this for any non-database error * dependencies. The message and any dependents will be marked as invalid
* that occurs while handling remotely created data. * and deleted along with their metadata. Throwing this exception has the
* This includes errors that occur while handling locally created data * same effect as returning {@link DeliveryAction#REJECT}.
* in a context controlled by remotely created data
* (for example, parsing the metadata of a dependency
* of an incoming message).
* Never rethrow DbException as FormatException!
*/ */
protected abstract boolean incomingMessage(Transaction txn, Message m, protected abstract DeliveryAction incomingMessage(Transaction txn,
BdfList body, BdfDictionary meta) throws DbException, Message m, BdfList body, BdfDictionary meta)
FormatException; throws DbException, FormatException;
@Override @Override
public boolean incomingMessage(Transaction txn, Message m, Metadata meta) public DeliveryAction incomingMessage(Transaction txn, Message m,
throws DbException, InvalidMessageException { Metadata meta) throws DbException, InvalidMessageException {
try { try {
BdfList body = clientHelper.toList(m); BdfList body = clientHelper.toList(m);
BdfDictionary metaDictionary = metadataParser.parse(meta); BdfDictionary metaDictionary = metadataParser.parse(meta);

View File

@@ -1,23 +1,19 @@
package org.briarproject.bramble.api.crypto; package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes;
/** /**
* A secret key used for encryption and/or authentication. * A secret key used for encryption and/or authentication.
*/ */
public class SecretKey { public class SecretKey extends Bytes {
/** /**
* The length of a secret key in bytes. * The length of a secret key in bytes.
*/ */
public static final int LENGTH = 32; public static final int LENGTH = 32;
private final byte[] key;
public SecretKey(byte[] key) { public SecretKey(byte[] key) {
super(key);
if (key.length != LENGTH) throw new IllegalArgumentException(); if (key.length != LENGTH) throw new IllegalArgumentException();
this.key = key;
}
public byte[] getBytes() {
return key;
} }
} }

View File

@@ -101,7 +101,7 @@ public interface DatabaseComponent extends TransactionManager {
/** /**
* Stores a transport. * Stores a transport.
*/ */
void addTransport(Transaction txn, TransportId t, int maxLatency) void addTransport(Transaction txn, TransportId t, long maxLatency)
throws DbException; throws DbException;
/** /**
@@ -118,6 +118,18 @@ public interface DatabaseComponent extends TransactionManager {
KeySetId addTransportKeys(Transaction txn, PendingContactId p, KeySetId addTransportKeys(Transaction txn, PendingContactId p,
TransportKeys k) throws DbException; TransportKeys k) throws DbException;
/**
* Returns true if there are any acks or messages to send to the given
* contact over a transport with the given maximum latency.
* <p/>
* Read-only.
*
* @param eager True if messages that are not yet due for retransmission
* should be included
*/
boolean containsAnythingToSend(Transaction txn, ContactId c,
long maxLatency, boolean eager) throws DbException;
/** /**
* Returns true if the database contains the given contact for the given * Returns true if the database contains the given contact for the given
* local pseudonym. * local pseudonym.
@@ -150,6 +162,16 @@ public interface DatabaseComponent extends TransactionManager {
boolean containsPendingContact(Transaction txn, PendingContactId p) boolean containsPendingContact(Transaction txn, PendingContactId p)
throws DbException; throws DbException;
/**
* Returns true if the database contains keys for communicating with the
* given contact over the given transport. Handshake mode and rotation mode
* keys are included, whether activated or not.
* <p/>
* Read-only.
*/
boolean containsTransportKeys(Transaction txn, ContactId c, TransportId t)
throws DbException;
/** /**
* Deletes the message with the given ID. Unlike * Deletes the message with the given ID. Unlike
* {@link #removeMessage(Transaction, MessageId)}, the message ID, * {@link #removeMessage(Transaction, MessageId)}, the message ID,
@@ -178,7 +200,19 @@ public interface DatabaseComponent extends TransactionManager {
*/ */
@Nullable @Nullable
Collection<Message> generateBatch(Transaction txn, ContactId c, Collection<Message> generateBatch(Transaction txn, ContactId c,
int maxLength, int maxLatency) throws DbException; int maxLength, long maxLatency) throws DbException;
/**
* Returns a batch of messages for the given contact containing the
* messages with the given IDs, for transmission over a transport with
* the given maximum latency.
* <p/>
* If any of the given messages are not in the database or are not visible
* to the contact, they are omitted from the batch without throwing an
* exception.
*/
Collection<Message> generateBatch(Transaction txn, ContactId c,
Collection<MessageId> ids, long maxLatency) throws DbException;
/** /**
* Returns an offer for the given contact for transmission over a * Returns an offer for the given contact for transmission over a
@@ -187,7 +221,7 @@ public interface DatabaseComponent extends TransactionManager {
*/ */
@Nullable @Nullable
Offer generateOffer(Transaction txn, ContactId c, int maxMessages, Offer generateOffer(Transaction txn, ContactId c, int maxMessages,
int maxLatency) throws DbException; long maxLatency) throws DbException;
/** /**
* Returns a request for the given contact, or null if there are no * Returns a request for the given contact, or null if there are no
@@ -206,7 +240,7 @@ public interface DatabaseComponent extends TransactionManager {
*/ */
@Nullable @Nullable
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c, Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
int maxLength, int maxLatency) throws DbException; int maxLength, long maxLatency) throws DbException;
/** /**
* Returns the contact with the given ID. * Returns the contact with the given ID.
@@ -292,16 +326,6 @@ public interface DatabaseComponent extends TransactionManager {
*/ */
Message getMessage(Transaction txn, MessageId m) throws DbException; Message getMessage(Transaction txn, MessageId m) throws DbException;
/**
* Returns the total length, including headers, of any messages that are
* eligible to be sent to the given contact via a transport with the given
* max latency.
* <p/>
* Read-only.
*/
long getMessageBytesToSend(Transaction txn, ContactId c, int maxLatency)
throws DbException;
/** /**
* Returns the IDs of all delivered messages in the given group. * Returns the IDs of all delivered messages in the given group.
* <p/> * <p/>
@@ -436,6 +460,27 @@ public interface DatabaseComponent extends TransactionManager {
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m) MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
throws DbException; throws DbException;
/**
* Returns the IDs of all messages that are eligible to be sent to the
* given contact, together with their raw lengths. This may include
* messages that have already been sent and are not yet due for
* retransmission.
* <p/>
* Read-only.
*/
Map<MessageId, Integer> getUnackedMessagesToSend(Transaction txn,
ContactId c) throws DbException;
/**
* Returns the total length, including headers, of all messages that are
* eligible to be sent to the given contact. This may include messages
* that have already been sent and are not yet due for retransmission.
* <p/>
* Read-only.
*/
long getUnackedMessageBytesToSend(Transaction txn, ContactId c)
throws DbException;
/** /**
* Returns the next time (in milliseconds since the Unix epoch) when a * Returns the next time (in milliseconds since the Unix epoch) when a
* message is due to be deleted, or {@link #NO_CLEANUP_DEADLINE} * message is due to be deleted, or {@link #NO_CLEANUP_DEADLINE}
@@ -493,6 +538,16 @@ public interface DatabaseComponent extends TransactionManager {
Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t) Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t)
throws DbException; throws DbException;
/**
* Returns the contact IDs and transport IDs for which the DB contains
* at least one set of transport keys. Handshake mode and rotation mode
* keys are included, whether activated or not.
* <p/>
* Read-only.
*/
Map<ContactId, Collection<TransportId>> getTransportsWithKeys(
Transaction txn) throws DbException;
/** /**
* Increments the outgoing stream counter for the given transport keys. * Increments the outgoing stream counter for the given transport keys.
*/ */

View File

@@ -3,7 +3,7 @@ package org.briarproject.bramble.api.keyagreement.event;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
/** /**
* An event that is broadcast when a BQP protocol completes. * An event that is broadcast when a BQP protocol begins.
*/ */
public class KeyAgreementStartedEvent extends Event { public class KeyAgreementStartedEvent extends Event {
} }

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.Clock;
import org.briarproject.bramble.api.system.Wakeful; import org.briarproject.bramble.api.system.Wakeful;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -22,6 +23,7 @@ public interface LifecycleManager {
*/ */
enum StartResult { enum StartResult {
ALREADY_RUNNING, ALREADY_RUNNING,
CLOCK_ERROR,
DB_ERROR, DB_ERROR,
DATA_TOO_OLD_ERROR, DATA_TOO_OLD_ERROR,
DATA_TOO_NEW_ERROR, DATA_TOO_NEW_ERROR,
@@ -65,6 +67,10 @@ 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}.
*
* @return {@link StartResult#CLOCK_ERROR} if the system clock is earlier
* than {@link Clock#MIN_REASONABLE_TIME_MS} or later than
* {@link Clock#MAX_REASONABLE_TIME_MS}.
*/ */
@Wakeful @Wakeful
StartResult startServices(SecretKey dbKey); StartResult startServices(SecretKey dbKey);

View File

@@ -61,7 +61,7 @@ public interface Plugin {
/** /**
* Returns the transport's maximum latency in milliseconds. * Returns the transport's maximum latency in milliseconds.
*/ */
int getMaxLatency(); long getMaxLatency();
/** /**
* Returns the transport's maximum idle time in milliseconds. * Returns the transport's maximum idle time in milliseconds.

View File

@@ -0,0 +1,25 @@
package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
@NotNullByDefault
public interface PluginFactory<P extends Plugin> {
/**
* Returns the plugin's transport identifier.
*/
TransportId getId();
/**
* Returns the maximum latency of the transport in milliseconds.
*/
long getMaxLatency();
/**
* Creates and returns a plugin, or null if no plugin can be created.
*/
@Nullable
P createPlugin(PluginCallback callback);
}

View File

@@ -15,13 +15,18 @@ public interface TransportConnectionWriter {
/** /**
* Returns the maximum latency of the transport in milliseconds. * Returns the maximum latency of the transport in milliseconds.
*/ */
int getMaxLatency(); long getMaxLatency();
/** /**
* Returns the maximum idle time of the transport in milliseconds. * Returns the maximum idle time of the transport in milliseconds.
*/ */
int getMaxIdleTime(); int getMaxIdleTime();
/**
* Returns true if the transport is lossy and cheap.
*/
boolean isLossyAndCheap();
/** /**
* Returns an output stream for writing to the transport connection. * Returns an output stream for writing to the transport connection.
*/ */

View File

@@ -70,7 +70,7 @@ public abstract class AbstractDuplexTransportConnection
private class Writer implements TransportConnectionWriter { private class Writer implements TransportConnectionWriter {
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return plugin.getMaxLatency(); return plugin.getMaxLatency();
} }
@@ -79,6 +79,11 @@ public abstract class AbstractDuplexTransportConnection
return plugin.getMaxIdleTime(); return plugin.getMaxIdleTime();
} }
@Override
public boolean isLossyAndCheap() {
return false;
}
@Override @Override
public OutputStream getOutputStream() throws IOException { public OutputStream getOutputStream() throws IOException {
return AbstractDuplexTransportConnection.this.getOutputStream(); return AbstractDuplexTransportConnection.this.getOutputStream();

View File

@@ -1,30 +1,11 @@
package org.briarproject.bramble.api.plugin.duplex; package org.briarproject.bramble.api.plugin.duplex;
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.PluginFactory;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.Nullable;
/** /**
* Factory for creating a plugin for a duplex transport. * Factory for creating a plugin for a duplex transport.
*/ */
@NotNullByDefault @NotNullByDefault
public interface DuplexPluginFactory { public interface DuplexPluginFactory extends PluginFactory<DuplexPlugin> {
/**
* Returns the plugin's transport identifier.
*/
TransportId getId();
/**
* Returns the maximum latency of the transport in milliseconds.
*/
int getMaxLatency();
/**
* Creates and returns a plugin, or null if no plugin can be created.
*/
@Nullable
DuplexPlugin createPlugin(PluginCallback callback);
} }

View File

@@ -8,4 +8,5 @@ public interface RemovableDriveConstants {
String PROP_PATH = "path"; String PROP_PATH = "path";
String PROP_URI = "uri"; String PROP_URI = "uri";
String PROP_SUPPORTED = "supported";
} }

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api.plugin.file; package org.briarproject.bramble.api.plugin.file;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
@@ -10,32 +11,42 @@ import javax.annotation.Nullable;
public interface RemovableDriveManager { public interface RemovableDriveManager {
/** /**
* Returns the currently running reader task for the given contact, * Returns the currently running reader task, or null if no reader task
* or null if no task is running. * is running.
*/ */
@Nullable @Nullable
RemovableDriveTask getCurrentReaderTask(ContactId c); RemovableDriveTask getCurrentReaderTask();
/** /**
* Returns the currently running writer task for the given contact, * Returns the currently running writer task, or null if no writer task
* or null if no task is running. * is running.
*/ */
@Nullable @Nullable
RemovableDriveTask getCurrentWriterTask(ContactId c); RemovableDriveTask getCurrentWriterTask();
/** /**
* Starts and returns a reader task for the given contact, reading from * Starts and returns a reader task, reading from a stream described by
* a stream described by the given transport properties. If a reader task * the given transport properties. If a reader task is already running,
* for the contact is already running, it will be returned and the * it will be returned and the argument will be ignored.
* transport properties will be ignored.
*/ */
RemovableDriveTask startReaderTask(ContactId c, TransportProperties p); RemovableDriveTask startReaderTask(TransportProperties p);
/** /**
* Starts and returns a writer task for the given contact, writing to * Starts and returns a writer task for the given contact, writing to
* a stream described by the given transport properties. If a writer task * a stream described by the given transport properties. If a writer task
* for the contact is already running, it will be returned and the * is already running, it will be returned and the arguments will be
* transport properties will be ignored. * ignored.
*/ */
RemovableDriveTask startWriterTask(ContactId c, TransportProperties p); RemovableDriveTask startWriterTask(ContactId c, TransportProperties p);
/**
* Returns true if the given contact has indicated support for the
* removable drive transport.
*/
boolean isTransportSupportedByContact(ContactId c) throws DbException;
/**
* Returns true if there is anything to send to the given contact.
*/
boolean isWriterTaskNeeded(ContactId c) throws DbException;
} }

View File

@@ -39,7 +39,7 @@ public interface RemovableDriveTask extends Runnable {
/** /**
* Returns the total length in bytes of the messages read or written * Returns the total length in bytes of the messages read or written
* so far. * so far, or zero if the total is unknown.
*/ */
public long getDone() { public long getDone() {
return done; return done;

View File

@@ -15,6 +15,12 @@ import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
public interface SimplexPlugin extends Plugin { public interface SimplexPlugin extends Plugin {
/**
* Returns true if the transport is likely to lose streams and the cost of
* transmitting redundant copies of data is cheap.
*/
boolean isLossyAndCheap();
/** /**
* 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.

View File

@@ -1,30 +1,11 @@
package org.briarproject.bramble.api.plugin.simplex; package org.briarproject.bramble.api.plugin.simplex;
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.PluginFactory;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.Nullable;
/** /**
* Factory for creating a plugin for a simplex transport. * Factory for creating a plugin for a simplex transport.
*/ */
@NotNullByDefault @NotNullByDefault
public interface SimplexPluginFactory { public interface SimplexPluginFactory extends PluginFactory<SimplexPlugin> {
/**
* Returns the plugin's transport identifier.
*/
TransportId getId();
/**
* Returns the maximum latency of the transport in milliseconds.
*/
int getMaxLatency();
/**
* Creates and returns a plugin, or null if no plugin can be created.
*/
@Nullable
SimplexPlugin createPlugin(PluginCallback callback);
} }

View File

@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.UniqueId;
import java.util.List; import java.util.List;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.DAYS;
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES; import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
public interface SyncConstants { public interface SyncConstants {
@@ -55,4 +56,9 @@ public interface SyncConstants {
* connections. * connections.
*/ */
int PRIORITY_NONCE_BYTES = 16; int PRIORITY_NONCE_BYTES = 16;
/**
* The maximum allowed latency for any transport, in milliseconds.
*/
long MAX_TRANSPORT_LATENCY = DAYS.toMillis(365);
} }

View File

@@ -16,9 +16,9 @@ public interface SyncSessionFactory {
PriorityHandler handler); PriorityHandler handler);
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t, SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, StreamWriter streamWriter); long maxLatency, boolean eager, StreamWriter streamWriter);
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t, SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, int maxIdleTime, StreamWriter streamWriter, long maxLatency, int maxIdleTime, StreamWriter streamWriter,
@Nullable Priority priority); @Nullable Priority priority);
} }

View File

@@ -10,23 +10,54 @@ public interface IncomingMessageHook {
/** /**
* Called once for each incoming message that passes validation. * Called once for each incoming message that passes validation.
* <p>
* If an unexpected exception occurs while handling data that is assumed
* to be valid (e.g. locally created metadata), it may be sensible to
* rethrow the unexpected exception as a DbException so that delivery is
* attempted again at next startup. This will allow delivery to succeed if
* the unexpected exception was caused by a bug that has subsequently been
* fixed.
* *
* @param txn A read-write transaction * @param txn A read-write transaction
* @return Whether or not this message should be shared * @throws DbException if a database error occurs while delivering the
* @throws DbException Should only be used for real database errors. * message. Delivery will be attempted again at next startup. Throwing
* If this is thrown, delivery will be attempted again at next startup, * this exception has the same effect as returning
* whereas if an InvalidMessageException is thrown, * {@link DeliveryAction#DEFER}.
* the message will be permanently invalidated. * @throws InvalidMessageException if the message is invalid in the context
* @throws InvalidMessageException for any non-database error * of its dependencies. The message and any dependents will be marked as
* that occurs while handling remotely created data. * invalid and deleted along with their metadata. Throwing this exception
* This includes errors that occur while handling locally created data * has the same effect as returning {@link DeliveryAction#REJECT}.
* in a context controlled by remotely created data
* (for example, parsing the metadata of a dependency
* of an incoming message).
* Throwing this will delete the incoming message and its metadata
* marking it as invalid in the database.
* Never rethrow DbException as InvalidMessageException!
*/ */
boolean incomingMessage(Transaction txn, Message m, Metadata meta) DeliveryAction incomingMessage(Transaction txn, Message m, Metadata meta)
throws DbException, InvalidMessageException; throws DbException, InvalidMessageException;
enum DeliveryAction {
/**
* The message and any dependent messages will be moved to the
* {@link MessageState#INVALID INVALID state} and deleted, along with
* their metadata.
*/
REJECT,
/**
* The message will be moved to the
* {@link MessageState#PENDING PENDING state}. Delivery will be
* attempted again at next startup.
*/
DEFER,
/**
* The message will be moved to the
* {@link MessageState#DELIVERED DELIVERED state} and shared.
*/
ACCEPT_SHARE,
/**
* The message will be moved to the
* {@link MessageState#DELIVERED DELIVERED state} and will not be
* shared.
*/
ACCEPT_DO_NOT_SHARE
}
} }

View File

@@ -1,8 +1,33 @@
package org.briarproject.bramble.api.sync.validation; package org.briarproject.bramble.api.sync.validation;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction;
public enum MessageState { public enum MessageState {
UNKNOWN(0), INVALID(1), PENDING(2), DELIVERED(3); /**
* A remote message that has not yet been validated.
*/
UNKNOWN(0),
/**
* A remote message that has failed validation, has been
* {@link DeliveryAction#REJECT rejected} by the local sync client, or
* depends on another message that has failed validation or been rejected.
*/
INVALID(1),
/**
* A remote message that has passed validation and is awaiting delivery to
* the local sync client. The message will not be delivered until all its
* dependencies have been validated and delivered.
*/
PENDING(2),
/**
* A local message, or a remote message that has passed validation and
* been delivered to the local sync client.
*/
DELIVERED(3);
private final int value; private final int value;

View File

@@ -6,6 +6,22 @@ package org.briarproject.bramble.api.system;
*/ */
public interface Clock { public interface Clock {
/**
* The minimum reasonable value for the system clock, in milliseconds
* since the Unix epoch.
* <p/>
* 1 Jan 2021, 00:00:00 UTC
*/
long MIN_REASONABLE_TIME_MS = 1_609_459_200_000L;
/**
* The maximum reasonable value for the system clock, in milliseconds
* since the Unix epoch.
* <p/>
* 1 Jan 2121, 00:00:00 UTC
*/
long MAX_REASONABLE_TIME_MS = 4_765_132_800_000L;
/** /**
* @see System#currentTimeMillis() * @see System#currentTimeMillis()
*/ */

View File

@@ -22,8 +22,24 @@ public interface KeyManager {
/** /**
* Derives and stores a set of rotation mode transport keys for * Derives and stores a set of rotation mode transport keys for
* communicating with the given contact over each transport and returns the * communicating with the given contact over the given transport and
* key set IDs. * returns the key set ID, or null if the transport is not supported.
* <p/>
* {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned.
*
* @param alice True if the local party is Alice
* @param active Whether the derived keys can be used for outgoing streams
*/
@Nullable
KeySetId addRotationKeys(Transaction txn, ContactId c, TransportId t,
SecretKey rootKey, long timestamp, boolean alice,
boolean active) throws DbException;
/**
* Derives and stores a set of rotation mode transport keys for
* communicating with the given contact over each supported transport and
* returns the key set IDs.
* <p/> * <p/>
* {@link StreamContext StreamContexts} for the contact can be created * {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned. * after this method has returned.

View File

@@ -0,0 +1,24 @@
package org.briarproject.bramble.api.transport.agreement;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
@NotNullByDefault
public interface TransportKeyAgreementManager {
/**
* The unique ID of the transport key agreement client.
*/
ClientId CLIENT_ID =
new ClientId("org.briarproject.bramble.transport.agreement");
/**
* The current major version of the transport key agreement client.
*/
int MAJOR_VERSION = 0;
/**
* The current minor version of the transport key agreement client.
*/
int MINOR_VERSION = 0;
}

View File

@@ -163,10 +163,15 @@ public class TestUtils {
public static Message getMessage(GroupId groupId) { public static Message getMessage(GroupId groupId) {
int bodyLength = 1 + random.nextInt(MAX_MESSAGE_BODY_LENGTH); int bodyLength = 1 + random.nextInt(MAX_MESSAGE_BODY_LENGTH);
return getMessage(groupId, bodyLength); return getMessage(groupId, bodyLength, timestamp);
} }
public static Message getMessage(GroupId groupId, int bodyLength) { public static Message getMessage(GroupId groupId, int bodyLength) {
return getMessage(groupId, bodyLength, timestamp);
}
public static Message getMessage(GroupId groupId, int bodyLength,
long timestamp) {
MessageId id = new MessageId(getRandomId()); MessageId id = new MessageId(getRandomId());
byte[] body = getRandomBytes(bodyLength); byte[] body = getRandomBytes(bodyLength);
return new Message(id, groupId, timestamp, body); return new Message(id, groupId, timestamp, body);

View File

@@ -1,24 +1,27 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64', 'com.google.dagger:dagger:2.33:dagger-2.33.jar:d8798c5b8cf6b125234e33af5c6293bb9f2208ce29b57924c35b8c0be7b6bdcb',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8', 'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8', 'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619', 'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90', 'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52', 'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', 'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c', 'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c', 'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b', 'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760', 'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16', 'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80', 'org.jmock:jmock-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04',
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d', 'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220', 'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
] ]
} }

View File

@@ -17,16 +17,17 @@ dependencies {
implementation 'org.whispersystems:curve25519-java:0.5.0' implementation 'org.whispersystems:curve25519-java:0.5.0'
implementation 'org.briarproject:jtorctl:0.3' implementation 'org.briarproject:jtorctl:0.3'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24' annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6 testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
testImplementation 'junit:junit:4.12' testImplementation 'net.jodah:concurrentunit:0.4.2'
testImplementation "org.jmock:jmock:2.8.2" testImplementation "junit:junit:$junit_version"
testImplementation "org.jmock:jmock-junit4:2.8.2" testImplementation "org.jmock:jmock:$jmock_version"
testImplementation "org.jmock:jmock-legacy:2.8.2" testImplementation "org.jmock:jmock-junit4:$jmock_version"
testImplementation "org.jmock:jmock-legacy:$jmock_version"
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.24' testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
signature 'org.codehaus.mojo.signature:java16:1.1@signature' signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }

View File

@@ -11,6 +11,7 @@ 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.transport.TransportModule; import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.transport.agreement.TransportKeyAgreementModule;
import org.briarproject.bramble.versioning.VersioningModule; import org.briarproject.bramble.versioning.VersioningModule;
public interface BrambleCoreEagerSingletons { public interface BrambleCoreEagerSingletons {
@@ -33,6 +34,8 @@ public interface BrambleCoreEagerSingletons {
void inject(RendezvousModule.EagerSingletons init); void inject(RendezvousModule.EagerSingletons init);
void inject(TransportKeyAgreementModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init); void inject(TransportModule.EagerSingletons init);
void inject(ValidationModule.EagerSingletons init); void inject(ValidationModule.EagerSingletons init);
@@ -51,6 +54,7 @@ public interface BrambleCoreEagerSingletons {
c.inject(new RendezvousModule.EagerSingletons()); c.inject(new RendezvousModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons()); c.inject(new PluginModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons()); c.inject(new PropertiesModule.EagerSingletons());
c.inject(new TransportKeyAgreementModule.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

@@ -23,6 +23,7 @@ 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.transport.TransportModule; import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.transport.agreement.TransportKeyAgreementModule;
import org.briarproject.bramble.versioning.VersioningModule; import org.briarproject.bramble.versioning.VersioningModule;
import dagger.Module; import dagger.Module;
@@ -49,6 +50,7 @@ import dagger.Module;
RendezvousModule.class, RendezvousModule.class,
SettingsModule.class, SettingsModule.class,
SyncModule.class, SyncModule.class,
TransportKeyAgreementModule.class,
TransportModule.class, TransportModule.class,
ValidationModule.class, ValidationModule.class,
VersioningModule.class VersioningModule.class

View File

@@ -45,7 +45,6 @@ abstract class Connection {
@Nullable @Nullable
StreamContext recogniseTag(TransportConnectionReader reader, StreamContext recogniseTag(TransportConnectionReader reader,
TransportId transportId) { TransportId transportId) {
StreamContext ctx;
try { try {
byte[] tag = readTag(reader.getInputStream()); byte[] tag = readTag(reader.getInputStream());
return keyManager.getStreamContext(transportId, tag); return keyManager.getStreamContext(transportId, tag);

View File

@@ -71,8 +71,10 @@ class OutgoingSimplexSyncConnection extends SyncConnection implements Runnable {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter( StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx); w.getOutputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId()); ContactId c = requireNonNull(ctx.getContactId());
// Use eager retransmission if the transport is lossy and cheap
return syncSessionFactory.createSimplexOutgoingSession(c, return syncSessionFactory.createSimplexOutgoingSession(c,
ctx.getTransportId(), w.getMaxLatency(), streamWriter); ctx.getTransportId(), w.getMaxLatency(), w.isLossyAndCheap(),
streamWriter);
} }
} }

View File

@@ -47,6 +47,7 @@ import javax.inject.Inject;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
import static org.briarproject.bramble.contact.ContactExchangeConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.contact.ContactExchangeConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.contact.ContactExchangeRecordTypes.CONTACT_INFO; import static org.briarproject.bramble.contact.ContactExchangeRecordTypes.CONTACT_INFO;
import static org.briarproject.bramble.util.ValidationUtils.checkLength; import static org.briarproject.bramble.util.ValidationUtils.checkLength;
@@ -184,6 +185,10 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
// The agreed timestamp is the minimum of the peers' timestamps // The agreed timestamp is the minimum of the peers' timestamps
long timestamp = Math.min(localTimestamp, remoteInfo.timestamp); long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
if (timestamp < MIN_REASONABLE_TIME_MS) {
LOG.warning("Timestamp is too old");
throw new FormatException();
}
// Add the contact // Add the contact
Contact contact = addContact(p, remoteInfo.author, localAuthor, Contact contact = addContact(p, remoteInfo.author, localAuthor,

View File

@@ -145,7 +145,7 @@ interface Database<T> {
/** /**
* Stores a transport. * Stores a transport.
*/ */
void addTransport(T txn, TransportId t, int maxLatency) void addTransport(T txn, TransportId t, long maxLatency)
throws DbException; throws DbException;
/** /**
@@ -162,6 +162,18 @@ interface Database<T> {
KeySetId addTransportKeys(T txn, PendingContactId p, TransportKeys k) KeySetId addTransportKeys(T txn, PendingContactId p, TransportKeys k)
throws DbException; throws DbException;
/**
* Returns true if there are any acks or messages to send to the given
* contact over a transport with the given maximum latency.
* <p/>
* Read-only.
*
* @param eager True if messages that are not yet due for retransmission
* should be included
*/
boolean containsAnythingToSend(T txn, ContactId c, long maxLatency,
boolean eager) throws DbException;
/** /**
* Returns true if the database contains the given contact for the given * Returns true if the database contains the given contact for the given
* local pseudonym. * local pseudonym.
@@ -215,6 +227,16 @@ interface Database<T> {
*/ */
boolean containsTransport(T txn, TransportId t) throws DbException; boolean containsTransport(T txn, TransportId t) throws DbException;
/**
* Returns true if the database contains keys for communicating with the
* given contact over the given transport. Handshake mode and rotation mode
* keys are included, whether activated or not.
* <p/>
* Read-only.
*/
boolean containsTransportKeys(T txn, ContactId c, TransportId t)
throws DbException;
/** /**
* Returns true if the database contains the given message, the message is * Returns true if the database contains the given message, the message is
* shared, and the visibility of the message's group to the given contact * shared, and the visibility of the message's group to the given contact
@@ -347,16 +369,6 @@ interface Database<T> {
*/ */
Message getMessage(T txn, MessageId m) throws DbException; Message getMessage(T txn, MessageId m) throws DbException;
/**
* Returns the total length, including headers, of any messages that are
* eligible to be sent to the given contact via a transport with the given
* max latency.
* <p/>
* Read-only.
*/
long getMessageBytesToSend(T txn, ContactId c, int maxLatency)
throws DbException;
/** /**
* Returns the IDs and states of all dependencies of the given message. * Returns the IDs and states of all dependencies of the given message.
* For missing dependencies and dependencies in other groups, the state * For missing dependencies and dependencies in other groups, the state
@@ -471,7 +483,7 @@ interface Database<T> {
* Read-only. * Read-only.
*/ */
Collection<MessageId> getMessagesToOffer(T txn, ContactId c, Collection<MessageId> getMessagesToOffer(T txn, ContactId c,
int maxMessages, int maxLatency) throws DbException; int maxMessages, long maxLatency) throws DbException;
/** /**
* Returns the IDs of some messages that are eligible to be requested from * Returns the IDs of some messages that are eligible to be requested from
@@ -486,10 +498,36 @@ interface Database<T> {
* Returns the IDs of some messages that are eligible to be sent to the * Returns the IDs of some messages that are eligible to be sent to the
* given contact, up to the given total length. * given contact, up to the given total length.
* <p/> * <p/>
* Unlike {@link #getUnackedMessagesToSend(Object, ContactId)} this method
* does not return messages that have already been sent unless they are
* due for retransmission.
* <p/>
* Read-only. * Read-only.
*/ */
Collection<MessageId> getMessagesToSend(T txn, ContactId c, int maxLength, Collection<MessageId> getMessagesToSend(T txn, ContactId c, int maxLength,
int maxLatency) throws DbException; long maxLatency) throws DbException;
/**
* Returns the IDs of all messages that are eligible to be sent to the
* given contact, together with their raw lengths.
* <p/>
* Unlike {@link #getMessagesToSend(Object, ContactId, int, long)} this
* method may return messages that have already been sent and are not yet
* due for retransmission.
* <p/>
* Read-only.
*/
Map<MessageId, Integer> getUnackedMessagesToSend(T txn, ContactId c)
throws DbException;
/**
* Returns the total length, including headers, of all messages that are
* eligible to be sent to the given contact. This may include messages
* that have already been sent and are not yet due for retransmission.
* <p/>
* Read-only.
*/
long getUnackedMessageBytesToSend(T txn, ContactId c) throws DbException;
/** /**
* Returns the IDs of any messages that need to be validated. * Returns the IDs of any messages that need to be validated.
@@ -566,7 +604,7 @@ interface Database<T> {
* Read-only. * Read-only.
*/ */
Collection<MessageId> getRequestedMessagesToSend(T txn, ContactId c, Collection<MessageId> getRequestedMessagesToSend(T txn, ContactId c,
int maxLength, int maxLatency) throws DbException; int maxLength, long maxLatency) throws DbException;
/** /**
* Returns all settings in the given namespace. * Returns all settings in the given namespace.
@@ -590,6 +628,16 @@ interface Database<T> {
Collection<TransportKeySet> getTransportKeys(T txn, TransportId t) Collection<TransportKeySet> getTransportKeys(T txn, TransportId t)
throws DbException; throws DbException;
/**
* Returns the contact IDs and transport IDs for which the DB contains
* at least one set of transport keys. Handshake mode and rotation mode
* keys are included, whether activated or not.
* <p/>
* Read-only.
*/
Map<ContactId, Collection<TransportId>> getTransportsWithKeys(T txn)
throws DbException;
/** /**
* Increments the outgoing stream counter for the given transport keys. * Increments the outgoing stream counter for the given transport keys.
*/ */
@@ -797,7 +845,7 @@ interface Database<T> {
* of the given message with respect to the given contact, using the latency * of the given message with respect to the given contact, using the latency
* of the transport over which it was sent. * of the transport over which it was sent.
*/ */
void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, int maxLatency) void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, long maxLatency)
throws DbException; throws DbException;
/** /**

View File

@@ -310,7 +310,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void addTransport(Transaction transaction, TransportId t, public void addTransport(Transaction transaction, TransportId t,
int maxLatency) throws DbException { long maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -341,6 +341,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.addTransportKeys(txn, p, k); return db.addTransportKeys(txn, p, k);
} }
@Override
public boolean containsAnythingToSend(Transaction transaction, ContactId c,
long maxLatency, boolean eager) throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
return db.containsAnythingToSend(txn, c, maxLatency, eager);
}
@Override @Override
public boolean containsContact(Transaction transaction, AuthorId remote, public boolean containsContact(Transaction transaction, AuthorId remote,
AuthorId local) throws DbException { AuthorId local) throws DbException {
@@ -371,6 +380,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.containsPendingContact(txn, p); return db.containsPendingContact(txn, p);
} }
@Override
public boolean containsTransportKeys(Transaction transaction, ContactId c,
TransportId t) throws DbException {
T txn = unbox(transaction);
return db.containsTransportKeys(txn, c, t);
}
@Override @Override
public void deleteMessage(Transaction transaction, MessageId m) public void deleteMessage(Transaction transaction, MessageId m)
throws DbException { throws DbException {
@@ -408,7 +424,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Nullable @Nullable
@Override @Override
public Collection<Message> generateBatch(Transaction transaction, public Collection<Message> generateBatch(Transaction transaction,
ContactId c, int maxLength, int maxLatency) throws DbException { ContactId c, int maxLength, long maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
@@ -429,10 +445,36 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return messages; return messages;
} }
@Override
public Collection<Message> generateBatch(Transaction transaction,
ContactId c, Collection<MessageId> ids, long maxLatency)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
long totalLength = 0;
List<Message> messages = new ArrayList<>(ids.size());
List<MessageId> sentIds = new ArrayList<>(ids.size());
for (MessageId m : ids) {
if (db.containsVisibleMessage(txn, c, m)) {
Message message = db.getMessage(txn, m);
totalLength += message.getRawLength();
messages.add(message);
sentIds.add(m);
db.updateExpiryTimeAndEta(txn, c, m, maxLatency);
}
}
if (messages.isEmpty()) return messages;
db.lowerRequestedFlag(txn, c, sentIds);
transaction.attach(new MessagesSentEvent(c, sentIds, totalLength));
return messages;
}
@Nullable @Nullable
@Override @Override
public Offer generateOffer(Transaction transaction, ContactId c, public Offer generateOffer(Transaction transaction, ContactId c,
int maxMessages, int maxLatency) throws DbException { int maxMessages, long maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
@@ -463,7 +505,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Nullable @Nullable
@Override @Override
public Collection<Message> generateRequestedBatch(Transaction transaction, public Collection<Message> generateRequestedBatch(Transaction transaction,
ContactId c, int maxLength, int maxLatency) throws DbException { ContactId c, int maxLength, long maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
@@ -575,15 +617,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getMessage(txn, m); return db.getMessage(txn, m);
} }
@Override
public long getMessageBytesToSend(Transaction transaction, ContactId c,
int maxLatency) throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
return db.getMessageBytesToSend(txn, c, maxLatency);
}
@Override @Override
public Collection<MessageId> getMessageIds(Transaction transaction, public Collection<MessageId> getMessageIds(Transaction transaction,
GroupId g) throws DbException { GroupId g) throws DbException {
@@ -707,6 +740,25 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return status; return status;
} }
@Override
public Map<MessageId, Integer> getUnackedMessagesToSend(
Transaction transaction,
ContactId c) throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
return db.getUnackedMessagesToSend(txn, c);
}
@Override
public long getUnackedMessageBytesToSend(Transaction transaction,
ContactId c) throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
return db.getUnackedMessageBytesToSend(txn, c);
}
@Override @Override
public Map<MessageId, MessageState> getMessageDependencies( public Map<MessageId, MessageState> getMessageDependencies(
Transaction transaction, MessageId m) throws DbException { Transaction transaction, MessageId m) throws DbException {
@@ -780,6 +832,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getTransportKeys(txn, t); return db.getTransportKeys(txn, t);
} }
@Override
public Map<ContactId, Collection<TransportId>> getTransportsWithKeys(
Transaction transaction) throws DbException {
T txn = unbox(transaction);
return db.getTransportsWithKeys(txn);
}
@Override @Override
public void incrementStreamCounter(Transaction transaction, TransportId t, public void incrementStreamCounter(Transaction transaction, TransportId t,
KeySetId k) throws DbException { KeySetId k) throws DbException {

View File

@@ -1,5 +1,7 @@
package org.briarproject.bramble.db; package org.briarproject.bramble.db;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_TRANSPORT_LATENCY;
class ExponentialBackoff { class ExponentialBackoff {
/** /**
@@ -11,9 +13,11 @@ class ExponentialBackoff {
* transmissions increases exponentially. If the expiry time would * transmissions increases exponentially. If the expiry time would
* be greater than Long.MAX_VALUE, Long.MAX_VALUE is returned. * be greater than Long.MAX_VALUE, Long.MAX_VALUE is returned.
*/ */
static long calculateExpiry(long now, int maxLatency, int txCount) { static long calculateExpiry(long now, long maxLatency, int txCount) {
if (now < 0) throw new IllegalArgumentException(); if (now < 0) throw new IllegalArgumentException();
if (maxLatency <= 0) throw new IllegalArgumentException(); if (maxLatency <= 0 || maxLatency > MAX_TRANSPORT_LATENCY) {
throw new IllegalArgumentException();
}
if (txCount < 0) throw new IllegalArgumentException(); if (txCount < 0) throw new IllegalArgumentException();
// The maximum round-trip time is twice the maximum latency // The maximum round-trip time is twice the maximum latency
long roundTrip = maxLatency * 2L; long roundTrip = maxLatency * 2L;

View File

@@ -51,6 +51,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -101,7 +102,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
abstract class JdbcDatabase implements Database<Connection> { abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // Package access for testing
static final int CODE_SCHEMA_VERSION = 48; static final int CODE_SCHEMA_VERSION = 49;
// Time period offsets for incoming transport keys // Time period offsets for incoming transport keys
private static final int OFFSET_PREV = -1; private static final int OFFSET_PREV = -1;
@@ -266,7 +267,7 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String CREATE_TRANSPORTS = private static final String CREATE_TRANSPORTS =
"CREATE TABLE transports" "CREATE TABLE transports"
+ " (transportId _STRING NOT NULL," + " (transportId _STRING NOT NULL,"
+ " maxLatency INT NOT NULL," + " maxLatency BIGINT NOT NULL,"
+ " PRIMARY KEY (transportId))"; + " PRIMARY KEY (transportId))";
private static final String CREATE_PENDING_CONTACTS = private static final String CREATE_PENDING_CONTACTS =
@@ -344,6 +345,11 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE INDEX IF NOT EXISTS statusesByContactIdTimestamp" "CREATE INDEX IF NOT EXISTS statusesByContactIdTimestamp"
+ " ON statuses (contactId, timestamp)"; + " ON statuses (contactId, timestamp)";
private static final String
INDEX_STATUSES_BY_CONTACT_ID_TX_COUNT_TIMESTAMP =
"CREATE INDEX IF NOT EXISTS statusesByContactIdTxCountTimestamp"
+ " ON statuses (contactId, txCount, timestamp)";
private static final String INDEX_MESSAGES_BY_CLEANUP_DEADLINE = private static final String INDEX_MESSAGES_BY_CLEANUP_DEADLINE =
"CREATE INDEX IF NOT EXISTS messagesByCleanupDeadline" "CREATE INDEX IF NOT EXISTS messagesByCleanupDeadline"
+ " ON messages (cleanupDeadline)"; + " ON messages (cleanupDeadline)";
@@ -492,7 +498,8 @@ abstract class JdbcDatabase implements Database<Connection> {
new Migration44_45(), new Migration44_45(),
new Migration45_46(), new Migration45_46(),
new Migration46_47(dbTypes), new Migration46_47(dbTypes),
new Migration47_48() new Migration47_48(),
new Migration48_49()
); );
} }
@@ -570,6 +577,7 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(INDEX_MESSAGE_DEPENDENCIES_BY_DEPENDENCY_ID); s.executeUpdate(INDEX_MESSAGE_DEPENDENCIES_BY_DEPENDENCY_ID);
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_GROUP_ID); s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_GROUP_ID);
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP); s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TX_COUNT_TIMESTAMP);
s.executeUpdate(INDEX_MESSAGES_BY_CLEANUP_DEADLINE); s.executeUpdate(INDEX_MESSAGES_BY_CLEANUP_DEADLINE);
s.close(); s.close();
} catch (SQLException e) { } catch (SQLException e) {
@@ -999,7 +1007,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public void addTransport(Connection txn, TransportId t, int maxLatency) public void addTransport(Connection txn, TransportId t, long maxLatency)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
@@ -1120,6 +1128,55 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public boolean containsAnythingToSend(Connection txn, ContactId c,
long maxLatency, boolean eager) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT NULL FROM statuses"
+ " WHERE contactId = ? AND ack = TRUE";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
rs = ps.executeQuery();
boolean acksToSend = rs.next();
rs.close();
ps.close();
if (acksToSend) return true;
if (eager) {
sql = "SELECT NULL from statuses"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE AND seen = FALSE";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, DELIVERED.getValue());
} else {
long now = clock.currentTimeMillis();
long eta = now + maxLatency;
sql = "SELECT NULL FROM statuses"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE AND seen = FALSE"
+ " AND (expiry <= ? OR eta > ?)";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, DELIVERED.getValue());
ps.setLong(3, now);
ps.setLong(4, eta);
}
rs = ps.executeQuery();
boolean messagesToSend = rs.next();
rs.close();
ps.close();
return messagesToSend;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public boolean containsContact(Connection txn, AuthorId remote, public boolean containsContact(Connection txn, AuthorId remote,
AuthorId local) throws DbException { AuthorId local) throws DbException {
@@ -1277,6 +1334,29 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public boolean containsTransportKeys(Connection txn, ContactId c,
TransportId t) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT NULL FROM outgoingKeys"
+ " WHERE contactId = ? AND transportId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setString(2, t.getString());
rs = ps.executeQuery();
boolean found = rs.next();
rs.close();
ps.close();
return found;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public boolean containsVisibleMessage(Connection txn, ContactId c, public boolean containsVisibleMessage(Connection txn, ContactId c,
MessageId m) throws DbException { MessageId m) throws DbException {
@@ -1758,37 +1838,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public long getMessageBytesToSend(Connection txn, ContactId c,
int maxLatency) throws DbException {
long now = clock.currentTimeMillis();
long eta = now + maxLatency;
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT SUM(length) FROM statuses"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE AND seen = FALSE"
+ " AND (expiry <= ? OR eta > ?)";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, DELIVERED.getValue());
ps.setLong(3, now);
ps.setLong(4, eta);
rs = ps.executeQuery();
rs.next();
long total = rs.getInt(1);
rs.close();
ps.close();
return total;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public Collection<MessageId> getMessageIds(Connection txn, GroupId g) public Collection<MessageId> getMessageIds(Connection txn, GroupId g)
throws DbException { throws DbException {
@@ -2140,7 +2189,7 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public Collection<MessageId> getMessagesToOffer(Connection txn, public Collection<MessageId> getMessagesToOffer(Connection txn,
ContactId c, int maxMessages, int maxLatency) throws DbException { ContactId c, int maxMessages, long maxLatency) throws DbException {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
long eta = now + maxLatency; long eta = now + maxLatency;
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -2199,7 +2248,7 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public Collection<MessageId> getMessagesToSend(Connection txn, ContactId c, public Collection<MessageId> getMessagesToSend(Connection txn, ContactId c,
int maxLength, int maxLatency) throws DbException { int maxLength, long maxLatency) throws DbException {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
long eta = now + maxLatency; long eta = now + maxLatency;
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -2236,6 +2285,63 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public Map<MessageId, Integer> getUnackedMessagesToSend(Connection txn,
ContactId c) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT length, messageId FROM statuses"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE AND seen = FALSE"
+ " ORDER BY txCount, timestamp";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, DELIVERED.getValue());
rs = ps.executeQuery();
Map<MessageId, Integer> results = new LinkedHashMap<>();
while (rs.next()) {
int length = rs.getInt(1);
MessageId id = new MessageId(rs.getBytes(2));
results.put(id, length);
}
rs.close();
ps.close();
return results;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public long getUnackedMessageBytesToSend(Connection txn, ContactId c)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT SUM(length) FROM statuses"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE AND seen = FALSE";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, DELIVERED.getValue());
rs = ps.executeQuery();
rs.next();
long total = rs.getInt(1);
rs.close();
ps.close();
return total;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public Collection<MessageId> getMessagesToValidate(Connection txn) public Collection<MessageId> getMessagesToValidate(Connection txn)
throws DbException { throws DbException {
@@ -2441,7 +2547,7 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public Collection<MessageId> getRequestedMessagesToSend(Connection txn, public Collection<MessageId> getRequestedMessagesToSend(Connection txn,
ContactId c, int maxLength, int maxLatency) throws DbException { ContactId c, int maxLength, long maxLatency) throws DbException {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
long eta = now + maxLatency; long eta = now + maxLatency;
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -2605,6 +2711,38 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public Map<ContactId, Collection<TransportId>> getTransportsWithKeys(
Connection txn) throws DbException {
Statement s = null;
ResultSet rs = null;
try {
String sql = "SELECT DISTINCT contactId, transportId"
+ " FROM outgoingKeys";
s = txn.createStatement();
rs = s.executeQuery(sql);
Map<ContactId, Collection<TransportId>> ids = new HashMap<>();
while (rs.next()) {
ContactId c = new ContactId(rs.getInt(1));
TransportId t = new TransportId(rs.getString(2));
Collection<TransportId> transportIds = ids.get(c);
if (transportIds == null) {
transportIds = new ArrayList<>();
ids.put(c, transportIds);
}
transportIds.add(t);
}
rs.close();
s.close();
return ids;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(s, LOG, WARNING);
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void incrementStreamCounter(Connection txn, TransportId t, public void incrementStreamCounter(Connection txn, TransportId t,
KeySetId k) throws DbException { KeySetId k) throws DbException {
@@ -3480,7 +3618,7 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m, public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m,
int maxLatency) throws DbException { long maxLatency) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {

View File

@@ -0,0 +1,41 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration48_49 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration48_49.class.getName());
@Override
public int getStartVersion() {
return 48;
}
@Override
public int getEndVersion() {
return 49;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("ALTER TABLE transports"
+ " ALTER COLUMN maxLatency"
+ " SET DATA TYPE BIGINT");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
@@ -34,11 +35,14 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleS
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING_SERVICES; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING_SERVICES;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.CLOCK_ERROR;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DATA_TOO_NEW_ERROR; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DATA_TOO_NEW_ERROR;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DATA_TOO_OLD_ERROR; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DATA_TOO_OLD_ERROR;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DB_ERROR; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DB_ERROR;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
import static org.briarproject.bramble.api.system.Clock.MAX_REASONABLE_TIME_MS;
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@@ -52,6 +56,7 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
private final DatabaseComponent db; private final DatabaseComponent db;
private final EventBus eventBus; private final EventBus eventBus;
private final Clock clock;
private final List<Service> services; private final List<Service> services;
private final List<OpenDatabaseHook> openDatabaseHooks; private final List<OpenDatabaseHook> openDatabaseHooks;
private final List<ExecutorService> executors; private final List<ExecutorService> executors;
@@ -63,9 +68,11 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
private volatile LifecycleState state = STARTING; private volatile LifecycleState state = STARTING;
@Inject @Inject
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus) { LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
Clock clock) {
this.db = db; this.db = db;
this.eventBus = eventBus; this.eventBus = eventBus;
this.clock = clock;
services = new CopyOnWriteArrayList<>(); services = new CopyOnWriteArrayList<>();
openDatabaseHooks = new CopyOnWriteArrayList<>(); openDatabaseHooks = new CopyOnWriteArrayList<>();
executors = new CopyOnWriteArrayList<>(); executors = new CopyOnWriteArrayList<>();
@@ -99,6 +106,13 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
LOG.info("Already starting or stopping"); LOG.info("Already starting or stopping");
return ALREADY_RUNNING; return ALREADY_RUNNING;
} }
long now = clock.currentTimeMillis();
if (now < MIN_REASONABLE_TIME_MS || now > MAX_REASONABLE_TIME_MS) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("System clock is unreasonable: " + now);
}
return CLOCK_ERROR;
}
try { try {
LOG.info("Opening database"); LOG.info("Opening database");
long start = now(); long start = now();

View File

@@ -79,7 +79,8 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
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 long maxLatency;
private final int maxIdleTime;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private final AtomicBoolean everConnected = new AtomicBoolean(false); private final AtomicBoolean everConnected = new AtomicBoolean(false);
@@ -121,7 +122,7 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
SecureRandom secureRandom, SecureRandom secureRandom,
Backoff backoff, Backoff backoff,
PluginCallback callback, PluginCallback callback,
int maxLatency, long maxLatency,
int maxIdleTime) { int maxIdleTime) {
this.connectionLimiter = connectionLimiter; this.connectionLimiter = connectionLimiter;
this.connectionFactory = connectionFactory; this.connectionFactory = connectionFactory;
@@ -158,7 +159,7 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return maxLatency; return maxLatency;
} }
@@ -473,6 +474,16 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
return discoverSemaphore.availablePermits() == 0; return discoverSemaphore.availablePermits() == 0;
} }
@Override
public void disablePolling() {
connectionLimiter.startLimiting();
}
@Override
public void enablePolling() {
connectionLimiter.endLimiting();
}
@Override @Override
public DuplexTransportConnection discoverAndConnectForSetup(String uuid) { public DuplexTransportConnection discoverAndConnectForSetup(String uuid) {
DuplexTransportConnection conn = discoverAndConnect(uuid); DuplexTransportConnection conn = discoverAndConnect(uuid);
@@ -501,9 +512,9 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
if (s.getNamespace().equals(ID.getString())) if (s.getNamespace().equals(ID.getString()))
ioExecutor.execute(() -> onSettingsUpdated(s.getSettings())); ioExecutor.execute(() -> onSettingsUpdated(s.getSettings()));
} else if (e instanceof KeyAgreementListeningEvent) { } else if (e instanceof KeyAgreementListeningEvent) {
ioExecutor.execute(connectionLimiter::keyAgreementStarted); connectionLimiter.startLimiting();
} else if (e instanceof KeyAgreementStoppedListeningEvent) { } else if (e instanceof KeyAgreementStoppedListeningEvent) {
ioExecutor.execute(connectionLimiter::keyAgreementEnded); connectionLimiter.endLimiting();
} else if (e instanceof RemoteTransportPropertiesUpdatedEvent) { } else if (e instanceof RemoteTransportPropertiesUpdatedEvent) {
RemoteTransportPropertiesUpdatedEvent r = RemoteTransportPropertiesUpdatedEvent r =
(RemoteTransportPropertiesUpdatedEvent) e; (RemoteTransportPropertiesUpdatedEvent) e;

View File

@@ -7,14 +7,15 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
interface BluetoothConnectionLimiter { interface BluetoothConnectionLimiter {
/** /**
* Informs the limiter that key agreement has started. * Tells the limiter to not allow regular polling connections (because we
* are about to do key agreement, or connect via BT for setup).
*/ */
void keyAgreementStarted(); void startLimiting();
/** /**
* Informs the limiter that key agreement has ended. * Tells the limiter to no longer limit regular polling connections.
*/ */
void keyAgreementEnded(); void endLimiting();
/** /**
* Returns true if a contact connection can be opened. This method does not * Returns true if a contact connection can be opened. This method does not

View File

@@ -30,34 +30,37 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
private final List<DuplexTransportConnection> connections = private final List<DuplexTransportConnection> connections =
new LinkedList<>(); new LinkedList<>();
@GuardedBy("lock") @GuardedBy("lock")
private boolean keyAgreementInProgress = false; private int limitingInProgress = 0;
BluetoothConnectionLimiterImpl(EventBus eventBus) { BluetoothConnectionLimiterImpl(EventBus eventBus) {
this.eventBus = eventBus; this.eventBus = eventBus;
} }
@Override @Override
public void keyAgreementStarted() { public void startLimiting() {
synchronized (lock) { synchronized (lock) {
keyAgreementInProgress = true; limitingInProgress++;
} }
LOG.info("Key agreement started"); LOG.info("Limiting started");
eventBus.broadcast(new CloseSyncConnectionsEvent(ID)); eventBus.broadcast(new CloseSyncConnectionsEvent(ID));
} }
@Override @Override
public void keyAgreementEnded() { public void endLimiting() {
synchronized (lock) { synchronized (lock) {
keyAgreementInProgress = false; limitingInProgress--;
if (limitingInProgress < 0) {
throw new IllegalStateException();
}
} }
LOG.info("Key agreement ended"); LOG.info("Limiting ended");
} }
@Override @Override
public boolean canOpenContactConnection() { public boolean canOpenContactConnection() {
synchronized (lock) { synchronized (lock) {
if (keyAgreementInProgress) { if (limitingInProgress > 0) {
LOG.info("Can't open contact connection during key agreement"); LOG.info("Can't open contact connection while limiting");
return false; return false;
} else { } else {
LOG.info("Can open contact connection"); LOG.info("Can open contact connection");

View File

@@ -11,6 +11,10 @@ public interface BluetoothPlugin extends DuplexPlugin {
boolean isDiscovering(); boolean isDiscovering();
void disablePolling();
void enablePolling();
@Nullable @Nullable
DuplexTransportConnection discoverAndConnectForSetup(String uuid); DuplexTransportConnection discoverAndConnectForSetup(String uuid);

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.PluginCallback;
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.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -17,10 +18,12 @@ import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static java.util.Collections.singletonMap;
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.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.ID; import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.ID;
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.PROP_SUPPORTED;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@Immutable @Immutable
@@ -30,7 +33,8 @@ abstract class AbstractRemovableDrivePlugin implements SimplexPlugin {
private static final Logger LOG = private static final Logger LOG =
getLogger(AbstractRemovableDrivePlugin.class.getName()); getLogger(AbstractRemovableDrivePlugin.class.getName());
private final int maxLatency; private final long maxLatency;
private final PluginCallback callback;
abstract InputStream openInputStream(TransportProperties p) abstract InputStream openInputStream(TransportProperties p)
throws IOException; throws IOException;
@@ -38,7 +42,8 @@ abstract class AbstractRemovableDrivePlugin implements SimplexPlugin {
abstract OutputStream openOutputStream(TransportProperties p) abstract OutputStream openOutputStream(TransportProperties p)
throws IOException; throws IOException;
AbstractRemovableDrivePlugin(int maxLatency) { AbstractRemovableDrivePlugin(PluginCallback callback, long maxLatency) {
this.callback = callback;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
} }
@@ -48,7 +53,7 @@ abstract class AbstractRemovableDrivePlugin implements SimplexPlugin {
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return maxLatency; return maxLatency;
} }
@@ -60,6 +65,8 @@ abstract class AbstractRemovableDrivePlugin implements SimplexPlugin {
@Override @Override
public void start() { public void start() {
callback.mergeLocalProperties(
new TransportProperties(singletonMap(PROP_SUPPORTED, "true")));
} }
@Override @Override
@@ -92,6 +99,11 @@ abstract class AbstractRemovableDrivePlugin implements SimplexPlugin {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public boolean isLossyAndCheap() {
return true;
}
@Override @Override
public TransportConnectionReader createReader(TransportProperties p) { public TransportConnectionReader createReader(TransportProperties p) {
try { try {

View File

@@ -27,20 +27,20 @@ abstract class FilePlugin implements SimplexPlugin {
getLogger(FilePlugin.class.getName()); getLogger(FilePlugin.class.getName());
protected final PluginCallback callback; protected final PluginCallback callback;
protected final int maxLatency; protected final long maxLatency;
protected abstract void writerFinished(File f, boolean exception); protected abstract void writerFinished(File f, boolean exception);
protected abstract void readerFinished(File f, boolean exception, protected abstract void readerFinished(File f, boolean exception,
boolean recognised); boolean recognised);
FilePlugin(PluginCallback callback, int maxLatency) { FilePlugin(PluginCallback callback, long maxLatency) {
this.callback = callback; this.callback = callback;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return maxLatency; return maxLatency;
} }

View File

@@ -27,7 +27,7 @@ class FileTransportWriter implements TransportConnectionWriter {
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return plugin.getMaxLatency(); return plugin.getMaxLatency();
} }
@@ -36,6 +36,11 @@ class FileTransportWriter implements TransportConnectionWriter {
return plugin.getMaxIdleTime(); return plugin.getMaxIdleTime();
} }
@Override
public boolean isLossyAndCheap() {
return plugin.isLossyAndCheap();
}
@Override @Override
public OutputStream getOutputStream() { public OutputStream getOutputStream() {
return out; return out;

View File

@@ -1,83 +1,125 @@
package org.briarproject.bramble.plugin.file; package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.file.RemovableDriveManager; import org.briarproject.bramble.api.plugin.file.RemovableDriveManager;
import org.briarproject.bramble.api.plugin.file.RemovableDriveTask; import org.briarproject.bramble.api.plugin.file.RemovableDriveTask;
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.transport.KeyManager;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.ID;
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.PROP_SUPPORTED;
import static org.briarproject.bramble.plugin.file.RemovableDrivePluginFactory.MAX_LATENCY;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
class RemovableDriveManagerImpl class RemovableDriveManagerImpl
implements RemovableDriveManager, RemovableDriveTaskRegistry { implements RemovableDriveManager, RemovableDriveTaskRegistry {
private final Executor ioExecutor; private final Executor ioExecutor;
private final DatabaseComponent db;
private final KeyManager keyManager;
private final TransportPropertyManager transportPropertyManager;
private final RemovableDriveTaskFactory taskFactory; private final RemovableDriveTaskFactory taskFactory;
private final ConcurrentHashMap<ContactId, RemovableDriveTask> private final Object lock = new Object();
readers = new ConcurrentHashMap<>();
private final ConcurrentHashMap<ContactId, RemovableDriveTask> @GuardedBy("lock")
writers = new ConcurrentHashMap<>(); @Nullable
private RemovableDriveTask reader = null;
@GuardedBy("lock")
@Nullable
private RemovableDriveTask writer = null;
@Inject @Inject
RemovableDriveManagerImpl(@IoExecutor Executor ioExecutor, RemovableDriveManagerImpl(
@IoExecutor Executor ioExecutor,
DatabaseComponent db,
KeyManager keyManager,
TransportPropertyManager transportPropertyManager,
RemovableDriveTaskFactory taskFactory) { RemovableDriveTaskFactory taskFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.db = db;
this.keyManager = keyManager;
this.transportPropertyManager = transportPropertyManager;
this.taskFactory = taskFactory; this.taskFactory = taskFactory;
} }
@Nullable @Nullable
@Override @Override
public RemovableDriveTask getCurrentReaderTask(ContactId c) { public RemovableDriveTask getCurrentReaderTask() {
return readers.get(c); synchronized (lock) {
return reader;
}
} }
@Nullable @Nullable
@Override @Override
public RemovableDriveTask getCurrentWriterTask(ContactId c) { public RemovableDriveTask getCurrentWriterTask() {
return writers.get(c); synchronized (lock) {
return writer;
}
} }
@Override @Override
public RemovableDriveTask startReaderTask(ContactId c, public RemovableDriveTask startReaderTask(TransportProperties p) {
TransportProperties p) { RemovableDriveTask created;
RemovableDriveTask task = taskFactory.createReader(this, c, p); synchronized (lock) {
RemovableDriveTask old = readers.putIfAbsent(c, task); if (reader != null) return reader;
if (old == null) { reader = created = taskFactory.createReader(this, p);
ioExecutor.execute(task);
return task;
} else {
return old;
} }
ioExecutor.execute(created);
return created;
} }
@Override @Override
public RemovableDriveTask startWriterTask(ContactId c, public RemovableDriveTask startWriterTask(ContactId c,
TransportProperties p) { TransportProperties p) {
RemovableDriveTask task = taskFactory.createWriter(this, c, p); RemovableDriveTask created;
RemovableDriveTask old = writers.putIfAbsent(c, task); synchronized (lock) {
if (old == null) { if (writer != null) return writer;
ioExecutor.execute(task); writer = created = taskFactory.createWriter(this, c, p);
return task; }
} else { ioExecutor.execute(created);
return old; return created;
}
@Override
public boolean isTransportSupportedByContact(ContactId c)
throws DbException {
if (!keyManager.canSendOutgoingStreams(c, ID)) return false;
TransportProperties p =
transportPropertyManager.getRemoteProperties(c, ID);
return "true".equals(p.get(PROP_SUPPORTED));
}
@Override
public boolean isWriterTaskNeeded(ContactId c) throws DbException {
return db.transactionWithResult(true, txn ->
db.containsAnythingToSend(txn, c, MAX_LATENCY, true));
}
@Override
public void removeReader(RemovableDriveTask task) {
synchronized (lock) {
if (reader == task) reader = null;
} }
} }
@Override @Override
public void removeReader(ContactId c, RemovableDriveTask task) { public void removeWriter(RemovableDriveTask task) {
readers.remove(c, task); synchronized (lock) {
} if (writer == task) writer = null;
}
@Override
public void removeWriter(ContactId c, RemovableDriveTask task) {
writers.remove(c, task);
} }
} }

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.plugin.file; package org.briarproject.bramble.plugin.file;
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.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -18,8 +19,8 @@ import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@NotNullByDefault @NotNullByDefault
class RemovableDrivePlugin extends AbstractRemovableDrivePlugin { class RemovableDrivePlugin extends AbstractRemovableDrivePlugin {
RemovableDrivePlugin(int maxLatency) { RemovableDrivePlugin(PluginCallback callback, long maxLatency) {
super(maxLatency); super(callback, maxLatency);
} }
@Override @Override

View File

@@ -17,7 +17,7 @@ import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.I
@NotNullByDefault @NotNullByDefault
public class RemovableDrivePluginFactory implements SimplexPluginFactory { public class RemovableDrivePluginFactory implements SimplexPluginFactory {
private static final int MAX_LATENCY = (int) DAYS.toMillis(14); static final long MAX_LATENCY = DAYS.toMillis(28);
@Inject @Inject
RemovableDrivePluginFactory() { RemovableDrivePluginFactory() {
@@ -29,13 +29,13 @@ public class RemovableDrivePluginFactory implements SimplexPluginFactory {
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return MAX_LATENCY; return MAX_LATENCY;
} }
@Nullable @Nullable
@Override @Override
public SimplexPlugin createPlugin(PluginCallback callback) { public SimplexPlugin createPlugin(PluginCallback callback) {
return new RemovableDrivePlugin(MAX_LATENCY); return new RemovableDrivePlugin(callback, MAX_LATENCY);
} }
} }

View File

@@ -1,15 +1,11 @@
package org.briarproject.bramble.plugin.file; package org.briarproject.bramble.plugin.file;
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.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.event.MessageAddedEvent;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -20,8 +16,7 @@ import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.ID; import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.ID;
@NotNullByDefault @NotNullByDefault
class RemovableDriveReaderTask extends RemovableDriveTaskImpl class RemovableDriveReaderTask extends RemovableDriveTaskImpl {
implements EventListener {
private final static Logger LOG = private final static Logger LOG =
getLogger(RemovableDriveReaderTask.class.getName()); getLogger(RemovableDriveReaderTask.class.getName());
@@ -32,10 +27,9 @@ class RemovableDriveReaderTask extends RemovableDriveTaskImpl
ConnectionManager connectionManager, ConnectionManager connectionManager,
EventBus eventBus, EventBus eventBus,
RemovableDriveTaskRegistry registry, RemovableDriveTaskRegistry registry,
ContactId contactId,
TransportProperties transportProperties) { TransportProperties transportProperties) {
super(eventExecutor, pluginManager, connectionManager, eventBus, super(eventExecutor, pluginManager, connectionManager, eventBus,
registry, contactId, transportProperties); registry, transportProperties);
} }
@Override @Override
@@ -44,25 +38,13 @@ class RemovableDriveReaderTask extends RemovableDriveTaskImpl
getPlugin().createReader(transportProperties); getPlugin().createReader(transportProperties);
if (r == null) { if (r == null) {
LOG.warning("Failed to create reader"); LOG.warning("Failed to create reader");
registry.removeReader(contactId, this); registry.removeReader(this);
setSuccess(false); setSuccess(false);
return; return;
} }
eventBus.addListener(this);
connectionManager.manageIncomingConnection(ID, new DecoratedReader(r)); connectionManager.manageIncomingConnection(ID, new DecoratedReader(r));
} }
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageAddedEvent) {
MessageAddedEvent m = (MessageAddedEvent) e;
if (contactId.equals(m.getContactId())) {
LOG.info("Message received");
addDone(m.getMessage().getRawLength());
}
}
}
private class DecoratedReader implements TransportConnectionReader { private class DecoratedReader implements TransportConnectionReader {
private final TransportConnectionReader delegate; private final TransportConnectionReader delegate;
@@ -80,8 +62,7 @@ class RemovableDriveReaderTask extends RemovableDriveTaskImpl
public void dispose(boolean exception, boolean recognised) public void dispose(boolean exception, boolean recognised)
throws IOException { throws IOException {
delegate.dispose(exception, recognised); delegate.dispose(exception, recognised);
registry.removeReader(contactId, RemovableDriveReaderTask.this); registry.removeReader(RemovableDriveReaderTask.this);
eventBus.removeListener(RemovableDriveReaderTask.this);
setSuccess(!exception && recognised); setSuccess(!exception && recognised);
} }
} }

View File

@@ -9,7 +9,7 @@ import org.briarproject.bramble.api.properties.TransportProperties;
interface RemovableDriveTaskFactory { interface RemovableDriveTaskFactory {
RemovableDriveTask createReader(RemovableDriveTaskRegistry registry, RemovableDriveTask createReader(RemovableDriveTaskRegistry registry,
ContactId c, TransportProperties p); TransportProperties p);
RemovableDriveTask createWriter(RemovableDriveTaskRegistry registry, RemovableDriveTask createWriter(RemovableDriveTaskRegistry registry,
ContactId c, TransportProperties p); ContactId c, TransportProperties p);

View File

@@ -41,9 +41,9 @@ class RemovableDriveTaskFactoryImpl implements RemovableDriveTaskFactory {
@Override @Override
public RemovableDriveTask createReader(RemovableDriveTaskRegistry registry, public RemovableDriveTask createReader(RemovableDriveTaskRegistry registry,
ContactId c, TransportProperties p) { TransportProperties p) {
return new RemovableDriveReaderTask(eventExecutor, pluginManager, return new RemovableDriveReaderTask(eventExecutor, pluginManager,
connectionManager, eventBus, registry, c, p); connectionManager, eventBus, registry, p);
} }
@Override @Override

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.Consumer; import org.briarproject.bramble.api.Consumer;
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.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
@@ -30,7 +29,6 @@ abstract class RemovableDriveTaskImpl implements RemovableDriveTask {
final ConnectionManager connectionManager; final ConnectionManager connectionManager;
final EventBus eventBus; final EventBus eventBus;
final RemovableDriveTaskRegistry registry; final RemovableDriveTaskRegistry registry;
final ContactId contactId;
final TransportProperties transportProperties; final TransportProperties transportProperties;
private final Object lock = new Object(); private final Object lock = new Object();
@@ -45,14 +43,12 @@ abstract class RemovableDriveTaskImpl implements RemovableDriveTask {
ConnectionManager connectionManager, ConnectionManager connectionManager,
EventBus eventBus, EventBus eventBus,
RemovableDriveTaskRegistry registry, RemovableDriveTaskRegistry registry,
ContactId contactId,
TransportProperties transportProperties) { TransportProperties transportProperties) {
this.eventExecutor = eventExecutor; this.eventExecutor = eventExecutor;
this.pluginManager = pluginManager; this.pluginManager = pluginManager;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.eventBus = eventBus; this.eventBus = eventBus;
this.registry = registry; this.registry = registry;
this.contactId = contactId;
this.transportProperties = transportProperties; this.transportProperties = transportProperties;
} }
@@ -67,8 +63,6 @@ abstract class RemovableDriveTaskImpl implements RemovableDriveTask {
synchronized (lock) { synchronized (lock) {
observers.add(o); observers.add(o);
state = this.state; state = this.state;
}
if (state.isFinished()) {
eventExecutor.execute(() -> o.accept(state)); eventExecutor.execute(() -> o.accept(state));
} }
} }
@@ -98,15 +92,15 @@ abstract class RemovableDriveTaskImpl implements RemovableDriveTask {
done = min(state.getDone() + done, state.getTotal()); done = min(state.getDone() + done, state.getTotal());
state = new State(done, state.getTotal(), state.isFinished(), state = new State(done, state.getTotal(), state.isFinished(),
state.isSuccess()); state.isSuccess());
notifyObservers();
} }
notifyObservers();
} }
void setSuccess(boolean success) { void setSuccess(boolean success) {
synchronized (lock) { synchronized (lock) {
state = new State(state.getDone(), state.getTotal(), true, success); state = new State(state.getDone(), state.getTotal(), true, success);
notifyObservers();
} }
notifyObservers();
} }
@GuardedBy("lock") @GuardedBy("lock")

View File

@@ -1,13 +1,12 @@
package org.briarproject.bramble.plugin.file; package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.file.RemovableDriveTask; import org.briarproject.bramble.api.plugin.file.RemovableDriveTask;
@NotNullByDefault @NotNullByDefault
interface RemovableDriveTaskRegistry { interface RemovableDriveTaskRegistry {
void removeReader(ContactId c, RemovableDriveTask task); void removeReader(RemovableDriveTask task);
void removeWriter(ContactId c, RemovableDriveTask task); void removeWriter(RemovableDriveTask task);
} }

View File

@@ -33,6 +33,7 @@ class RemovableDriveWriterTask extends RemovableDriveTaskImpl
getLogger(RemovableDriveWriterTask.class.getName()); getLogger(RemovableDriveWriterTask.class.getName());
private final DatabaseComponent db; private final DatabaseComponent db;
private final ContactId contactId;
RemovableDriveWriterTask( RemovableDriveWriterTask(
DatabaseComponent db, DatabaseComponent db,
@@ -44,8 +45,9 @@ class RemovableDriveWriterTask extends RemovableDriveTaskImpl
ContactId contactId, ContactId contactId,
TransportProperties transportProperties) { TransportProperties transportProperties) {
super(eventExecutor, pluginManager, connectionManager, eventBus, super(eventExecutor, pluginManager, connectionManager, eventBus,
registry, contactId, transportProperties); registry, transportProperties);
this.db = db; this.db = db;
this.contactId = contactId;
} }
@Override @Override
@@ -54,17 +56,16 @@ class RemovableDriveWriterTask extends RemovableDriveTaskImpl
TransportConnectionWriter w = plugin.createWriter(transportProperties); TransportConnectionWriter w = plugin.createWriter(transportProperties);
if (w == null) { if (w == null) {
LOG.warning("Failed to create writer"); LOG.warning("Failed to create writer");
registry.removeWriter(contactId, this); registry.removeWriter(this);
setSuccess(false); setSuccess(false);
return; return;
} }
int maxLatency = plugin.getMaxLatency();
try { try {
setTotal(db.transactionWithResult(true, txn -> setTotal(db.transactionWithResult(true, txn ->
db.getMessageBytesToSend(txn, contactId, maxLatency))); db.getUnackedMessageBytesToSend(txn, contactId)));
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
registry.removeWriter(contactId, this); registry.removeWriter(this);
setSuccess(false); setSuccess(false);
return; return;
} }
@@ -95,7 +96,7 @@ class RemovableDriveWriterTask extends RemovableDriveTaskImpl
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return delegate.getMaxLatency(); return delegate.getMaxLatency();
} }
@@ -104,6 +105,11 @@ class RemovableDriveWriterTask extends RemovableDriveTaskImpl
return delegate.getMaxIdleTime(); return delegate.getMaxIdleTime();
} }
@Override
public boolean isLossyAndCheap() {
return delegate.isLossyAndCheap();
}
@Override @Override
public OutputStream getOutputStream() throws IOException { public OutputStream getOutputStream() throws IOException {
return delegate.getOutputStream(); return delegate.getOutputStream();
@@ -112,7 +118,7 @@ class RemovableDriveWriterTask extends RemovableDriveTaskImpl
@Override @Override
public void dispose(boolean exception) throws IOException { public void dispose(boolean exception) throws IOException {
delegate.dispose(exception); delegate.dispose(exception);
registry.removeWriter(contactId, RemovableDriveWriterTask.this); registry.removeWriter(RemovableDriveWriterTask.this);
eventBus.removeListener(RemovableDriveWriterTask.this); eventBus.removeListener(RemovableDriveWriterTask.this);
setSuccess(!exception); setSuccess(!exception);
} }

View File

@@ -1,8 +1,8 @@
package org.briarproject.bramble.plugin.file; package org.briarproject.bramble.plugin.file;
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.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -17,16 +17,16 @@ class TransportOutputStreamWriter implements TransportConnectionWriter {
private static final Logger LOG = private static final Logger LOG =
getLogger(TransportOutputStreamWriter.class.getName()); getLogger(TransportOutputStreamWriter.class.getName());
private final Plugin plugin; private final SimplexPlugin plugin;
private final OutputStream out; private final OutputStream out;
TransportOutputStreamWriter(Plugin plugin, OutputStream out) { TransportOutputStreamWriter(SimplexPlugin plugin, OutputStream out) {
this.plugin = plugin; this.plugin = plugin;
this.out = out; this.out = out;
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return plugin.getMaxLatency(); return plugin.getMaxLatency();
} }
@@ -35,6 +35,11 @@ class TransportOutputStreamWriter implements TransportConnectionWriter {
return plugin.getMaxIdleTime(); return plugin.getMaxIdleTime();
} }
@Override
public boolean isLossyAndCheap() {
return plugin.isLossyAndCheap();
}
@Override @Override
public OutputStream getOutputStream() { public OutputStream getOutputStream() {
return out; return out;

View File

@@ -92,7 +92,7 @@ class LanTcpPlugin extends TcpPlugin {
Executor wakefulIoExecutor, Executor wakefulIoExecutor,
Backoff backoff, Backoff backoff,
PluginCallback callback, PluginCallback callback,
int maxLatency, long maxLatency,
int maxIdleTime, int maxIdleTime,
int connectionTimeout) { int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency, super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,

View File

@@ -50,7 +50,7 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return MAX_LATENCY; return MAX_LATENCY;
} }

View File

@@ -69,8 +69,8 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
protected final Executor ioExecutor, wakefulIoExecutor, 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 long maxLatency;
protected final int connectionTimeout, socketTimeout; protected final int maxIdleTime, connectionTimeout, socketTimeout;
protected final AtomicBoolean used = new AtomicBoolean(false); protected final AtomicBoolean used = new AtomicBoolean(false);
protected final PluginState state = new PluginState(); protected final PluginState state = new PluginState();
@@ -111,7 +111,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
Executor wakefulIoExecutor, Executor wakefulIoExecutor,
Backoff backoff, Backoff backoff,
PluginCallback callback, PluginCallback callback,
int maxLatency, long maxLatency,
int maxIdleTime, int maxIdleTime,
int connectionTimeout) { int connectionTimeout) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
@@ -129,7 +129,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return maxLatency; return maxLatency;
} }

View File

@@ -35,7 +35,7 @@ class WanTcpPlugin extends TcpPlugin {
Backoff backoff, Backoff backoff,
PortMapper portMapper, PortMapper portMapper,
PluginCallback callback, PluginCallback callback,
int maxLatency, long maxLatency,
int maxIdleTime, int maxIdleTime,
int connectionTimeout) { int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency, super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,

View File

@@ -54,7 +54,7 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return MAX_LATENCY; return MAX_LATENCY;
} }

View File

@@ -131,7 +131,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final String architecture; private final String architecture;
private final CircumventionProvider circumventionProvider; private final CircumventionProvider circumventionProvider;
private final ResourceProvider resourceProvider; private final ResourceProvider resourceProvider;
private final int maxLatency, maxIdleTime, socketTimeout; private final long maxLatency;
private final int maxIdleTime, socketTimeout;
private final File torDirectory, geoIpFile, configFile; private final File torDirectory, geoIpFile, configFile;
private final File doneFile, cookieFile; private final File doneFile, cookieFile;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@@ -159,7 +160,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, PluginCallback callback,
String architecture, String architecture,
int maxLatency, long maxLatency,
int maxIdleTime, int maxIdleTime,
File torDirectory) { File torDirectory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
@@ -204,7 +205,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public int getMaxLatency() { public long getMaxLatency() {
return maxLatency; return maxLatency;
} }

View File

@@ -44,6 +44,7 @@ import static org.briarproject.bramble.api.properties.TransportPropertyConstants
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.api.properties.TransportPropertyConstants.REFLECTED_PROPERTY_PREFIX;
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@Immutable @Immutable
@@ -115,8 +116,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
} }
@Override @Override
public boolean incomingMessage(Transaction txn, Message m, Metadata meta) public DeliveryAction incomingMessage(Transaction txn, Message m,
throws DbException, InvalidMessageException { Metadata meta) throws DbException, InvalidMessageException {
try { try {
// Find the latest update for this transport, if any // Find the latest update for this transport, if any
BdfDictionary d = metadataParser.parse(meta); BdfDictionary d = metadataParser.parse(meta);
@@ -131,14 +132,14 @@ 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; return ACCEPT_DO_NOT_SHARE;
} }
} }
txn.attach(new RemoteTransportPropertiesUpdatedEvent(t)); txn.attach(new RemoteTransportPropertiesUpdatedEvent(t));
} catch (FormatException e) { } catch (FormatException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }
return false; return ACCEPT_DO_NOT_SHARE;
} }
@Override @Override

View File

@@ -77,7 +77,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private final Clock clock; private final Clock clock;
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId; private final TransportId transportId;
private final int maxLatency, maxIdleTime; private final long maxLatency, maxIdleTime;
private final StreamWriter streamWriter; private final StreamWriter streamWriter;
private final SyncRecordWriter recordWriter; private final SyncRecordWriter recordWriter;
@Nullable @Nullable
@@ -95,7 +95,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, Clock clock, ContactId contactId, EventBus eventBus, Clock clock, ContactId contactId,
TransportId transportId, int maxLatency, int maxIdleTime, TransportId transportId, long maxLatency, int maxIdleTime,
StreamWriter streamWriter, SyncRecordWriter recordWriter, StreamWriter streamWriter, SyncRecordWriter recordWriter,
@Nullable Priority priority) { @Nullable Priority priority) {
this.db = db; this.db = db;

View File

@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.SyncRecordWriter; import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.SyncSession; import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.Versions; import org.briarproject.bramble.api.sync.Versions;
@@ -22,7 +23,11 @@ import org.briarproject.bramble.api.sync.event.CloseSyncConnectionsEvent;
import org.briarproject.bramble.api.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@@ -60,7 +65,8 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
private final EventBus eventBus; private final EventBus eventBus;
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId; private final TransportId transportId;
private final int maxLatency; private final long maxLatency;
private final boolean eager;
private final StreamWriter streamWriter; private final StreamWriter streamWriter;
private final SyncRecordWriter recordWriter; private final SyncRecordWriter recordWriter;
private final AtomicInteger outstandingQueries; private final AtomicInteger outstandingQueries;
@@ -70,7 +76,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, ContactId contactId, TransportId transportId, EventBus eventBus, ContactId contactId, TransportId transportId,
int maxLatency, StreamWriter streamWriter, long maxLatency, boolean eager, StreamWriter streamWriter,
SyncRecordWriter recordWriter) { SyncRecordWriter recordWriter) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
@@ -78,6 +84,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
this.contactId = contactId; this.contactId = contactId;
this.transportId = transportId; this.transportId = transportId;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.eager = eager;
this.streamWriter = streamWriter; this.streamWriter = streamWriter;
this.recordWriter = recordWriter; this.recordWriter = recordWriter;
outstandingQueries = new AtomicInteger(2); // One per type of record outstandingQueries = new AtomicInteger(2); // One per type of record
@@ -92,8 +99,9 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
// Send our supported protocol versions // Send our supported protocol versions
recordWriter.writeVersions(new Versions(SUPPORTED_VERSIONS)); recordWriter.writeVersions(new Versions(SUPPORTED_VERSIONS));
// Start a query for each type of record // Start a query for each type of record
dbExecutor.execute(new GenerateAck()); dbExecutor.execute(this::generateAck);
dbExecutor.execute(new GenerateBatch()); if (eager) dbExecutor.execute(this::loadUnackedMessageIds);
else dbExecutor.execute(this::generateBatch);
// Write records until interrupted or no more records to write // Write records until interrupted or no more records to write
try { try {
while (!interrupted) { while (!interrupted) {
@@ -138,81 +146,110 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
} }
} }
private class GenerateAck implements Runnable { @DatabaseExecutor
private void loadUnackedMessageIds() {
@DatabaseExecutor if (interrupted) return;
@Override try {
public void run() { Map<MessageId, Integer> ids = db.transactionWithResult(true, txn ->
if (interrupted) return; db.getUnackedMessagesToSend(txn, contactId));
try { if (LOG.isLoggable(INFO)) {
Ack a = db.transactionWithNullableResult(false, txn -> LOG.info(ids.size() + " unacked messages to send");
db.generateAck(txn, contactId, MAX_MESSAGE_IDS));
if (LOG.isLoggable(INFO))
LOG.info("Generated ack: " + (a != null));
if (a == null) decrementOutstandingQueries();
else writerTasks.add(new WriteAck(a));
} catch (DbException e) {
logException(LOG, WARNING, e);
interrupt();
} }
if (ids.isEmpty()) decrementOutstandingQueries();
else dbExecutor.execute(() -> generateEagerBatch(ids));
} catch (DbException e) {
logException(LOG, WARNING, e);
interrupt();
} }
} }
private class WriteAck implements ThrowingRunnable<IOException> { @DatabaseExecutor
private void generateEagerBatch(Map<MessageId, Integer> ids) {
private final Ack ack; if (interrupted) return;
// Take some message IDs from `ids` to form a batch
private WriteAck(Ack ack) { Collection<MessageId> batchIds = new ArrayList<>();
this.ack = ack; long totalLength = 0;
Iterator<Entry<MessageId, Integer>> it = ids.entrySet().iterator();
while (it.hasNext()) {
// Check whether the next message will fit in the batch
Entry<MessageId, Integer> e = it.next();
int length = e.getValue();
if (totalLength + length > MAX_RECORD_PAYLOAD_BYTES) break;
// Add the message to the batch
it.remove();
batchIds.add(e.getKey());
totalLength += length;
} }
if (batchIds.isEmpty()) throw new AssertionError();
@IoExecutor try {
@Override Collection<Message> batch =
public void run() throws IOException { db.transactionWithResult(false, txn ->
if (interrupted) return; db.generateBatch(txn, contactId, batchIds,
recordWriter.writeAck(ack); maxLatency));
LOG.info("Sent ack"); writerTasks.add(() -> writeEagerBatch(batch, ids));
dbExecutor.execute(new GenerateAck()); } catch (DbException e) {
logException(LOG, WARNING, e);
interrupt();
} }
} }
private class GenerateBatch implements Runnable { @IoExecutor
private void writeEagerBatch(Collection<Message> batch,
Map<MessageId, Integer> ids) throws IOException {
if (interrupted) return;
for (Message m : batch) recordWriter.writeMessage(m);
LOG.info("Sent eager batch");
if (ids.isEmpty()) decrementOutstandingQueries();
else dbExecutor.execute(() -> generateEagerBatch(ids));
}
@DatabaseExecutor @DatabaseExecutor
@Override private void generateAck() {
public void run() { if (interrupted) return;
if (interrupted) return; try {
try { Ack a = db.transactionWithNullableResult(false, txn ->
Collection<Message> b = db.generateAck(txn, contactId, MAX_MESSAGE_IDS));
db.transactionWithNullableResult(false, txn -> if (LOG.isLoggable(INFO))
db.generateBatch(txn, contactId, LOG.info("Generated ack: " + (a != null));
MAX_RECORD_PAYLOAD_BYTES, maxLatency)); if (a == null) decrementOutstandingQueries();
if (LOG.isLoggable(INFO)) else writerTasks.add(() -> writeAck(a));
LOG.info("Generated batch: " + (b != null)); } catch (DbException e) {
if (b == null) decrementOutstandingQueries(); logException(LOG, WARNING, e);
else writerTasks.add(new WriteBatch(b)); interrupt();
} catch (DbException e) {
logException(LOG, WARNING, e);
interrupt();
}
} }
} }
private class WriteBatch implements ThrowingRunnable<IOException> { @IoExecutor
private void writeAck(Ack ack) throws IOException {
if (interrupted) return;
recordWriter.writeAck(ack);
LOG.info("Sent ack");
dbExecutor.execute(this::generateAck);
}
private final Collection<Message> batch; @DatabaseExecutor
private void generateBatch() {
private WriteBatch(Collection<Message> batch) { if (interrupted) return;
this.batch = batch; try {
Collection<Message> b =
db.transactionWithNullableResult(false, txn ->
db.generateBatch(txn, contactId,
MAX_RECORD_PAYLOAD_BYTES, maxLatency));
if (LOG.isLoggable(INFO))
LOG.info("Generated batch: " + (b != null));
if (b == null) decrementOutstandingQueries();
else writerTasks.add(() -> writeBatch(b));
} catch (DbException e) {
logException(LOG, WARNING, e);
interrupt();
} }
}
@IoExecutor @IoExecutor
@Override private void writeBatch(Collection<Message> batch) throws IOException {
public void run() throws IOException { if (interrupted) return;
if (interrupted) return; for (Message m : batch) recordWriter.writeMessage(m);
for (Message m : batch) recordWriter.writeMessage(m); LOG.info("Sent batch");
LOG.info("Sent batch"); dbExecutor.execute(this::generateBatch);
dbExecutor.execute(new GenerateBatch());
}
} }
} }

View File

@@ -60,17 +60,17 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
@Override @Override
public SyncSession createSimplexOutgoingSession(ContactId c, TransportId t, public SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, StreamWriter streamWriter) { long maxLatency, boolean eager, StreamWriter streamWriter) {
OutputStream out = streamWriter.getOutputStream(); OutputStream out = streamWriter.getOutputStream();
SyncRecordWriter recordWriter = SyncRecordWriter recordWriter =
recordWriterFactory.createRecordWriter(out); recordWriterFactory.createRecordWriter(out);
return new SimplexOutgoingSession(db, dbExecutor, eventBus, c, t, return new SimplexOutgoingSession(db, dbExecutor, eventBus, c, t,
maxLatency, streamWriter, recordWriter); maxLatency, eager, streamWriter, recordWriter);
} }
@Override @Override
public SyncSession createDuplexOutgoingSession(ContactId c, TransportId t, public SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, int maxIdleTime, StreamWriter streamWriter, long maxLatency, int maxIdleTime, StreamWriter streamWriter,
@Nullable Priority priority) { @Nullable Priority priority) {
OutputStream out = streamWriter.getOutputStream(); OutputStream out = streamWriter.getOutputStream();
SyncRecordWriter recordWriter = SyncRecordWriter recordWriter =

View File

@@ -20,6 +20,7 @@ import org.briarproject.bramble.api.sync.MessageContext;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.event.MessageAddedEvent; import org.briarproject.bramble.api.sync.event.MessageAddedEvent;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook; import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.sync.validation.MessageValidator; import org.briarproject.bramble.api.sync.validation.MessageValidator;
import org.briarproject.bramble.api.sync.validation.ValidationManager; import org.briarproject.bramble.api.sync.validation.ValidationManager;
@@ -40,6 +41,10 @@ import javax.inject.Inject;
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 org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE;
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_SHARE;
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.DEFER;
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.REJECT;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED; import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID; import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING; import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
@@ -185,16 +190,19 @@ class ValidationManagerImpl implements ValidationManager, Service,
int majorVersion = g.getMajorVersion(); int majorVersion = g.getMajorVersion();
Metadata meta = Metadata meta =
db.getMessageMetadataForValidator(txn, id); db.getMessageMetadataForValidator(txn, id);
DeliveryResult result = DeliveryAction action =
deliverMessage(txn, m, c, majorVersion, meta); deliverMessage(txn, m, c, majorVersion, meta);
if (result.valid) { if (action == REJECT) {
addPendingDependents(txn, id, pending); invalidateMessage(txn, id);
if (result.share) {
db.setMessageShared(txn, id);
toShare.addAll(states.keySet());
}
} else {
addDependentsToInvalidate(txn, id, invalidate); addDependentsToInvalidate(txn, id, invalidate);
} else if (action == ACCEPT_SHARE) {
db.setMessageState(txn, m.getId(), DELIVERED);
addPendingDependents(txn, id, pending);
db.setMessageShared(txn, id);
toShare.addAll(states.keySet());
} else if (action == ACCEPT_DO_NOT_SHARE) {
db.setMessageState(txn, m.getId(), DELIVERED);
addPendingDependents(txn, id, pending);
} }
} }
} }
@@ -275,16 +283,21 @@ class ValidationManagerImpl implements ValidationManager, Service,
Metadata meta = context.getMetadata(); Metadata meta = context.getMetadata();
db.mergeMessageMetadata(txn, id, meta); db.mergeMessageMetadata(txn, id, meta);
if (allDelivered) { if (allDelivered) {
DeliveryResult result = DeliveryAction action =
deliverMessage(txn, m, c, majorVersion, meta); deliverMessage(txn, m, c, majorVersion, meta);
if (result.valid) { if (action == REJECT) {
addPendingDependents(txn, id, pending); invalidateMessage(txn, id);
if (result.share) {
db.setMessageShared(txn, id);
toShare.addAll(dependencies);
}
} else {
addDependentsToInvalidate(txn, id, invalidate); addDependentsToInvalidate(txn, id, invalidate);
} else if (action == DEFER) {
db.setMessageState(txn, id, PENDING);
} else if (action == ACCEPT_SHARE) {
db.setMessageState(txn, id, DELIVERED);
addPendingDependents(txn, id, pending);
db.setMessageShared(txn, id);
toShare.addAll(dependencies);
} else if (action == ACCEPT_DO_NOT_SHARE) {
db.setMessageState(txn, id, DELIVERED);
addPendingDependents(txn, id, pending);
} }
} else { } else {
db.setMessageState(txn, id, PENDING); db.setMessageState(txn, id, PENDING);
@@ -304,23 +317,21 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
@DatabaseExecutor @DatabaseExecutor
private DeliveryResult deliverMessage(Transaction txn, Message m, private DeliveryAction deliverMessage(Transaction txn, Message m,
ClientId c, int majorVersion, Metadata meta) throws DbException { ClientId c, int majorVersion, Metadata meta) {
// Deliver the message to the client if it's registered a hook // Deliver the message to the client if it has registered a hook
boolean shareMsg = false;
ClientMajorVersion cv = new ClientMajorVersion(c, majorVersion); ClientMajorVersion cv = new ClientMajorVersion(c, majorVersion);
IncomingMessageHook hook = hooks.get(cv); IncomingMessageHook hook = hooks.get(cv);
if (hook != null) { if (hook == null) return ACCEPT_DO_NOT_SHARE;
try { try {
shareMsg = hook.incomingMessage(txn, m, meta); return hook.incomingMessage(txn, m, meta);
} catch (InvalidMessageException e) { } catch (DbException e) {
logException(LOG, INFO, e); logException(LOG, INFO, e);
invalidateMessage(txn, m.getId()); return DEFER;
return new DeliveryResult(false, false); } catch (InvalidMessageException e) {
} logException(LOG, INFO, e);
return REJECT;
} }
db.setMessageState(txn, m.getId(), DELIVERED);
return new DeliveryResult(true, shareMsg);
} }
@DatabaseExecutor @DatabaseExecutor
@@ -447,14 +458,4 @@ class ValidationManagerImpl implements ValidationManager, Service,
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
} }
} }
private static class DeliveryResult {
private final boolean valid, share;
private DeliveryResult(boolean valid, boolean share) {
this.valid = valid;
this.share = share;
}
}
} }

View File

@@ -19,9 +19,8 @@ import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginFactory;
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.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
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;
@@ -40,6 +39,7 @@ import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_TRANSPORT_LATENCY;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
@@ -51,7 +51,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final DatabaseComponent db; private final DatabaseComponent db;
private final Executor dbExecutor; private final Executor dbExecutor;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final TransportKeyManagerFactory transportKeyManagerFactory;
private final TransportCrypto transportCrypto; private final TransportCrypto transportCrypto;
private final ConcurrentHashMap<TransportId, TransportKeyManager> managers; private final ConcurrentHashMap<TransportId, TransportKeyManager> managers;
@@ -61,34 +60,35 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
KeyManagerImpl(DatabaseComponent db, KeyManagerImpl(DatabaseComponent db,
@DatabaseExecutor Executor dbExecutor, @DatabaseExecutor Executor dbExecutor,
PluginConfig pluginConfig, PluginConfig pluginConfig,
TransportKeyManagerFactory transportKeyManagerFactory, TransportCrypto transportCrypto,
TransportCrypto transportCrypto) { TransportKeyManagerFactory transportKeyManagerFactory) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.transportKeyManagerFactory = transportKeyManagerFactory;
this.transportCrypto = transportCrypto; this.transportCrypto = transportCrypto;
managers = new ConcurrentHashMap<>(); managers = new ConcurrentHashMap<>();
for (PluginFactory<?> f : pluginConfig.getSimplexFactories()) {
TransportKeyManager m = transportKeyManagerFactory.
createTransportKeyManager(f.getId(), f.getMaxLatency());
managers.put(f.getId(), m);
}
for (PluginFactory<?> f : pluginConfig.getDuplexFactories()) {
TransportKeyManager m = transportKeyManagerFactory.
createTransportKeyManager(f.getId(), f.getMaxLatency());
managers.put(f.getId(), m);
}
} }
@Override @Override
public void startService() throws ServiceException { public void startService() throws ServiceException {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
Map<TransportId, Integer> transports = new HashMap<>();
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories())
transports.put(f.getId(), f.getMaxLatency());
for (DuplexPluginFactory f : pluginConfig.getDuplexFactories())
transports.put(f.getId(), f.getMaxLatency());
try { try {
db.transaction(false, txn -> { db.transaction(false, txn -> {
for (Entry<TransportId, Integer> e : transports.entrySet()) for (PluginFactory<?> f : pluginConfig.getSimplexFactories()) {
db.addTransport(txn, e.getKey(), e.getValue()); addTransport(txn, f);
for (Entry<TransportId, Integer> e : transports.entrySet()) { }
TransportKeyManager m = transportKeyManagerFactory for (PluginFactory<?> f : pluginConfig.getDuplexFactories()) {
.createTransportKeyManager(e.getKey(), addTransport(txn, f);
e.getValue());
managers.put(e.getKey(), m);
m.start(txn);
} }
}); });
} catch (DbException e) { } catch (DbException e) {
@@ -96,14 +96,32 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
} }
private void addTransport(Transaction txn, PluginFactory<?> f)
throws DbException {
long maxLatency = f.getMaxLatency();
if (maxLatency > MAX_TRANSPORT_LATENCY) {
throw new IllegalStateException();
}
db.addTransport(txn, f.getId(), maxLatency);
managers.get(f.getId()).start(txn);
}
@Override @Override
public void stopService() { public void stopService() {
} }
@Override @Override
public Map<TransportId, KeySetId> addRotationKeys( public KeySetId addRotationKeys(Transaction txn, ContactId c,
Transaction txn, ContactId c, SecretKey rootKey, long timestamp, TransportId t, SecretKey rootKey, long timestamp, boolean alice,
boolean alice, boolean active) throws DbException { boolean active) throws DbException {
return withManager(t, m ->
m.addRotationKeys(txn, c, rootKey, timestamp, alice, active));
}
@Override
public Map<TransportId, KeySetId> addRotationKeys(Transaction txn,
ContactId c, SecretKey rootKey, long timestamp, boolean alice,
boolean active) throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>(); Map<TransportId, KeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) { for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
@@ -137,7 +155,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair) PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair)
throws DbException, GeneralSecurityException { throws DbException, GeneralSecurityException {
SecretKey staticMasterKey = transportCrypto SecretKey staticMasterKey = transportCrypto
.deriveStaticMasterKey(theirPublicKey, ourKeyPair); .deriveStaticMasterKey(theirPublicKey, ourKeyPair);
SecretKey rootKey = SecretKey rootKey =
transportCrypto.deriveHandshakeRootKey(staticMasterKey, true); transportCrypto.deriveHandshakeRootKey(staticMasterKey, true);
boolean alice = transportCrypto.isAlice(theirPublicKey, ourKeyPair); boolean alice = transportCrypto.isAlice(theirPublicKey, ourKeyPair);

View File

@@ -0,0 +1,22 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
@NotNullByDefault
interface MessageEncoder {
Message encodeKeyMessage(GroupId contactGroupId,
TransportId transportId, PublicKey publicKey);
Message encodeActivateMessage(GroupId contactGroupId,
TransportId transportId, MessageId previousMessageId);
BdfDictionary encodeMessageMetadata(TransportId transportId,
MessageType type, boolean local);
}

View File

@@ -0,0 +1,77 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.transport.agreement.MessageType.ACTIVATE;
import static org.briarproject.bramble.transport.agreement.MessageType.KEY;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_IS_SESSION;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_TRANSPORT_ID;
@Immutable
@NotNullByDefault
class MessageEncoderImpl implements MessageEncoder {
private final ClientHelper clientHelper;
private final Clock clock;
@Inject
MessageEncoderImpl(ClientHelper clientHelper, Clock clock) {
this.clientHelper = clientHelper;
this.clock = clock;
}
@Override
public Message encodeKeyMessage(GroupId contactGroupId,
TransportId transportId, PublicKey publicKey) {
BdfList body = BdfList.of(
KEY.getValue(),
transportId.getString(),
publicKey.getEncoded());
return encodeMessage(contactGroupId, body);
}
@Override
public Message encodeActivateMessage(GroupId contactGroupId,
TransportId transportId, MessageId previousMessageId) {
BdfList body = BdfList.of(
ACTIVATE.getValue(),
transportId.getString(),
previousMessageId);
return encodeMessage(contactGroupId, body);
}
@Override
public BdfDictionary encodeMessageMetadata(TransportId transportId,
MessageType type, boolean local) {
return BdfDictionary.of(
new BdfEntry(MSG_KEY_IS_SESSION, false),
new BdfEntry(MSG_KEY_TRANSPORT_ID, transportId.getString()),
new BdfEntry(MSG_KEY_MESSAGE_TYPE, type.getValue()),
new BdfEntry(MSG_KEY_LOCAL, local));
}
private Message encodeMessage(GroupId contactGroupId, BdfList body) {
try {
return clientHelper.createMessage(contactGroupId,
clock.currentTimeMillis(), clientHelper.toByteArray(body));
} catch (FormatException e) {
throw new AssertionError();
}
}
}

View File

@@ -0,0 +1,29 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
enum MessageType {
KEY(0),
ACTIVATE(1);
private final int value;
MessageType(int value) {
this.value = value;
}
int getValue() {
return value;
}
static MessageType fromValue(int value) throws FormatException {
for (MessageType t : values()) if (t.value == value) return t;
throw new FormatException();
}
}

View File

@@ -0,0 +1,58 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.transport.KeySetId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class Session {
private final State state;
@Nullable
private final MessageId lastLocalMessageId;
@Nullable
private final KeyPair localKeyPair;
@Nullable
private final Long localTimestamp;
@Nullable
private final KeySetId keySetId;
Session(State state, @Nullable MessageId lastLocalMessageId,
@Nullable KeyPair localKeyPair, @Nullable Long localTimestamp,
@Nullable KeySetId keySetId) {
this.state = state;
this.lastLocalMessageId = lastLocalMessageId;
this.localKeyPair = localKeyPair;
this.localTimestamp = localTimestamp;
this.keySetId = keySetId;
}
State getState() {
return state;
}
@Nullable
MessageId getLastLocalMessageId() {
return lastLocalMessageId;
}
@Nullable
KeyPair getLocalKeyPair() {
return localKeyPair;
}
@Nullable
Long getLocalTimestamp() {
return localTimestamp;
}
@Nullable
KeySetId getKeySetId() {
return keySetId;
}
}

View File

@@ -0,0 +1,13 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
@NotNullByDefault
interface SessionEncoder {
BdfDictionary encodeSession(Session s, TransportId transportId);
BdfDictionary getSessionQuery(TransportId transportId);
}

View File

@@ -0,0 +1,68 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.KeySetId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_IS_SESSION;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_TRANSPORT_ID;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_KEY_SET_ID;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_LOCAL_PRIVATE_KEY;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_LOCAL_PUBLIC_KEY;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_LOCAL_TIMESTAMP;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_STATE;
@Immutable
@NotNullByDefault
class SessionEncoderImpl implements SessionEncoder {
@Inject
SessionEncoderImpl() {
}
@Override
public BdfDictionary encodeSession(Session s, TransportId transportId) {
BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_IS_SESSION, true);
meta.put(MSG_KEY_TRANSPORT_ID, transportId.getString());
meta.put(SESSION_KEY_STATE, s.getState().getValue());
putNullable(meta, SESSION_KEY_LAST_LOCAL_MESSAGE_ID,
s.getLastLocalMessageId());
KeyPair localKeyPair = s.getLocalKeyPair();
if (localKeyPair == null) {
meta.put(SESSION_KEY_LOCAL_PUBLIC_KEY, NULL_VALUE);
meta.put(SESSION_KEY_LOCAL_PRIVATE_KEY, NULL_VALUE);
} else {
meta.put(SESSION_KEY_LOCAL_PUBLIC_KEY,
localKeyPair.getPublic().getEncoded());
meta.put(SESSION_KEY_LOCAL_PRIVATE_KEY,
localKeyPair.getPrivate().getEncoded());
}
putNullable(meta, SESSION_KEY_LOCAL_TIMESTAMP, s.getLocalTimestamp());
KeySetId keySetId = s.getKeySetId();
if (keySetId == null) meta.put(SESSION_KEY_KEY_SET_ID, NULL_VALUE);
else meta.put(SESSION_KEY_KEY_SET_ID, keySetId.getInt());
return meta;
}
@Override
public BdfDictionary getSessionQuery(TransportId transportId) {
return BdfDictionary.of(
new BdfEntry(MSG_KEY_IS_SESSION, true),
new BdfEntry(MSG_KEY_TRANSPORT_ID, transportId.getString()));
}
private void putNullable(BdfDictionary meta, String key,
@Nullable Object o) {
meta.put(key, o == null ? NULL_VALUE : o);
}
}

View File

@@ -0,0 +1,11 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
interface SessionParser {
Session parseSession(BdfDictionary meta) throws FormatException;
}

View File

@@ -0,0 +1,67 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.transport.KeySetId;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_KEY_SET_ID;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_LOCAL_PRIVATE_KEY;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_LOCAL_PUBLIC_KEY;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_LOCAL_TIMESTAMP;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.SESSION_KEY_STATE;
@Immutable
@NotNullByDefault
class SessionParserImpl implements SessionParser {
private final TransportKeyAgreementCrypto crypto;
@Inject
SessionParserImpl(TransportKeyAgreementCrypto crypto) {
this.crypto = crypto;
}
@Override
public Session parseSession(BdfDictionary meta) throws FormatException {
State state =
State.fromValue(meta.getLong(SESSION_KEY_STATE).intValue());
MessageId lastLocalMessageId = null;
byte[] lastLocalMessageIdBytes =
meta.getOptionalRaw(SESSION_KEY_LAST_LOCAL_MESSAGE_ID);
if (lastLocalMessageIdBytes != null) {
lastLocalMessageId = new MessageId(lastLocalMessageIdBytes);
}
KeyPair localKeyPair = null;
byte[] localPublicKeyBytes =
meta.getOptionalRaw(SESSION_KEY_LOCAL_PUBLIC_KEY);
byte[] localPrivateKeyBytes =
meta.getOptionalRaw(SESSION_KEY_LOCAL_PRIVATE_KEY);
if (localPublicKeyBytes != null && localPrivateKeyBytes != null) {
PublicKey pub = crypto.parsePublicKey(localPublicKeyBytes);
PrivateKey priv = crypto.parsePrivateKey(localPrivateKeyBytes);
localKeyPair = new KeyPair(pub, priv);
}
Long localTimestamp = meta.getOptionalLong(SESSION_KEY_LOCAL_TIMESTAMP);
KeySetId keySetId = null;
Long keySetIdLong = meta.getOptionalLong(SESSION_KEY_KEY_SET_ID);
if (keySetIdLong != null) {
keySetId = new KeySetId(keySetIdLong.intValue());
}
return new Session(state, lastLocalMessageId, localKeyPair,
localTimestamp, keySetId);
}
}

View File

@@ -0,0 +1,43 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
enum State {
/**
* We've sent a key message and are awaiting the contact's key message.
*/
AWAIT_KEY(0),
/**
* We've exchanged key messages, derived the transport keys and sent an
* activate message, and now we're awaiting the contact's activate message.
*/
AWAIT_ACTIVATE(1),
/**
* We've exchanged key messages and activate messages, and have derived and
* activated the transport keys. This is the end state.
*/
ACTIVATED(2);
private final int value;
State(int value) {
this.value = value;
}
int getValue() {
return value;
}
static State fromValue(int value) throws FormatException {
for (State s : values()) if (s.value == value) return s;
throw new FormatException();
}
}

View File

@@ -0,0 +1,27 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
interface TransportKeyAgreementConstants {
String MSG_KEY_IS_SESSION = "isSession";
String MSG_KEY_MESSAGE_TYPE = "messageType";
String MSG_KEY_TRANSPORT_ID = "transportId";
String MSG_KEY_PUBLIC_KEY = "publicKey";
String MSG_KEY_LOCAL = "local";
String SESSION_KEY_STATE = "state";
String SESSION_KEY_LAST_LOCAL_MESSAGE_ID = "lastLocalMessageId";
String SESSION_KEY_LOCAL_PUBLIC_KEY = "localPublicKey";
String SESSION_KEY_LOCAL_PRIVATE_KEY = "localPrivateKey";
String SESSION_KEY_LOCAL_TIMESTAMP = "localTimestamp";
String SESSION_KEY_KEY_SET_ID = "keySetId";
/**
* Label for deriving the root key from key pairs.
*/
String ROOT_KEY_LABEL =
"org.briarproject.bramble.transport.agreement/ROOT_KEY";
}

View File

@@ -0,0 +1,23 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException;
@NotNullByDefault
interface TransportKeyAgreementCrypto {
KeyPair generateKeyPair();
SecretKey deriveRootKey(KeyPair localKeyPair, PublicKey remotePublicKey)
throws GeneralSecurityException;
PublicKey parsePublicKey(byte[] encoded) throws FormatException;
PrivateKey parsePrivateKey(byte[] encoded) throws FormatException;
}

View File

@@ -0,0 +1,66 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.Bytes.compare;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.ROOT_KEY_LABEL;
@Immutable
@NotNullByDefault
class TransportKeyAgreementCryptoImpl implements TransportKeyAgreementCrypto {
private final CryptoComponent crypto;
@Inject
TransportKeyAgreementCryptoImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
@Override
public KeyPair generateKeyPair() {
return crypto.generateAgreementKeyPair();
}
@Override
public SecretKey deriveRootKey(KeyPair localKeyPair,
PublicKey remotePublicKey) throws GeneralSecurityException {
byte[] theirPublic = remotePublicKey.getEncoded();
byte[] ourPublic = localKeyPair.getPublic().getEncoded();
boolean alice = compare(ourPublic, theirPublic) < 0;
byte[][] inputs = {
alice ? ourPublic : theirPublic,
alice ? theirPublic : ourPublic
};
return crypto.deriveSharedSecret(ROOT_KEY_LABEL, remotePublicKey,
localKeyPair, inputs);
}
@Override
public PublicKey parsePublicKey(byte[] encoded) throws FormatException {
try {
return crypto.getAgreementKeyParser().parsePublicKey(encoded);
} catch (GeneralSecurityException e) {
throw new FormatException();
}
}
@Override
public PrivateKey parsePrivateKey(byte[] encoded) throws FormatException {
try {
return crypto.getAgreementKeyParser().parsePrivateKey(encoded);
} catch (GeneralSecurityException e) {
throw new FormatException();
}
}
}

View File

@@ -0,0 +1,408 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfIncomingMessageHook;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginFactory;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.agreement.TransportKeyAgreementManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static java.lang.Math.min;
import static java.util.Collections.singletonMap;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.Bytes.compare;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE;
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.DEFER;
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.REJECT;
import static org.briarproject.bramble.transport.agreement.MessageType.ACTIVATE;
import static org.briarproject.bramble.transport.agreement.MessageType.KEY;
import static org.briarproject.bramble.transport.agreement.State.ACTIVATED;
import static org.briarproject.bramble.transport.agreement.State.AWAIT_ACTIVATE;
import static org.briarproject.bramble.transport.agreement.State.AWAIT_KEY;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_PUBLIC_KEY;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_TRANSPORT_ID;
@Immutable
@NotNullByDefault
class TransportKeyAgreementManagerImpl extends BdfIncomingMessageHook
implements TransportKeyAgreementManager, OpenDatabaseHook, ContactHook,
ClientVersioningHook {
private static final Logger LOG =
getLogger(TransportKeyAgreementManagerImpl.class.getName());
private final ContactGroupFactory contactGroupFactory;
private final ClientVersioningManager clientVersioningManager;
private final IdentityManager identityManager;
private final KeyManager keyManager;
private final MessageEncoder messageEncoder;
private final SessionEncoder sessionEncoder;
private final SessionParser sessionParser;
private final TransportKeyAgreementCrypto crypto;
private final List<TransportId> transports;
private final Group localGroup;
@Inject
TransportKeyAgreementManagerImpl(
DatabaseComponent db,
ClientHelper clientHelper,
MetadataParser metadataParser,
ContactGroupFactory contactGroupFactory,
ClientVersioningManager clientVersioningManager,
IdentityManager identityManager,
KeyManager keyManager,
MessageEncoder messageEncoder,
SessionEncoder sessionEncoder,
SessionParser sessionParser,
TransportKeyAgreementCrypto crypto,
PluginConfig config) {
super(db, clientHelper, metadataParser);
this.contactGroupFactory = contactGroupFactory;
this.clientVersioningManager = clientVersioningManager;
this.identityManager = identityManager;
this.keyManager = keyManager;
this.messageEncoder = messageEncoder;
this.sessionEncoder = sessionEncoder;
this.sessionParser = sessionParser;
this.crypto = crypto;
transports = new ArrayList<>();
for (PluginFactory<?> f : config.getDuplexFactories()) {
transports.add(f.getId());
}
for (PluginFactory<?> f : config.getSimplexFactories()) {
transports.add(f.getId());
}
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
MAJOR_VERSION);
}
@Override
public void onDatabaseOpened(Transaction txn) throws DbException {
Collection<Contact> contacts = db.getContacts(txn);
if (!db.containsGroup(txn, localGroup.getId())) {
db.addGroup(txn, localGroup);
// Set things up for any pre-existing contacts
for (Contact c : contacts) addingContact(txn, c);
}
// Find any contacts and transports that need keys
Map<ContactId, Collection<TransportId>> transportsWithKeys =
db.getTransportsWithKeys(txn);
for (Contact c : contacts) {
Collection<TransportId> withKeys =
transportsWithKeys.get(c.getId());
for (TransportId t : transports) {
if (withKeys == null || !withKeys.contains(t)) {
// We need keys for this contact and transport
GroupId contactGroupId = getContactGroup(c).getId();
SavedSession ss = loadSession(txn, contactGroupId, t);
if (ss == null) {
// Start a session by sending our key message
startSession(txn, contactGroupId, t);
}
}
}
}
}
@Override
public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact
Group g = getContactGroup(c);
db.addGroup(txn, g);
// Attach the contact ID to the group
clientHelper.setContactId(txn, g.getId(), c.getId());
// Apply the client's visibility to the contact group
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), CLIENT_ID, MAJOR_VERSION);
db.setGroupVisibility(txn, c.getId(), g.getId(), client);
}
@Override
public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getContactGroup(c));
}
@Override
public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException {
// Apply the client's visibility to the contact group
Group g = getContactGroup(c);
db.setGroupVisibility(txn, c.getId(), g.getId(), v);
}
@Override
protected DeliveryAction incomingMessage(Transaction txn, Message m,
BdfList body, BdfDictionary meta)
throws DbException, FormatException {
MessageType type = MessageType.fromValue(
meta.getLong(MSG_KEY_MESSAGE_TYPE).intValue());
TransportId t = new TransportId(meta.getString(MSG_KEY_TRANSPORT_ID));
if (LOG.isLoggable(INFO)) {
LOG.info("Received " + type + " message for " + t);
}
if (!transports.contains(t)) {
// Defer handling the message until we support the transport
return DEFER;
}
SavedSession ss = loadSession(txn, m.getGroupId(), t);
if (type == KEY) return handleKeyMessage(txn, t, m, meta, ss);
else if (type == ACTIVATE) return handleActivateMessage(txn, t, ss);
else throw new AssertionError();
}
private DeliveryAction handleKeyMessage(Transaction txn, TransportId t,
Message m, BdfDictionary meta, @Nullable SavedSession ss)
throws DbException, FormatException {
ContactId c = clientHelper.getContactId(txn, m.getGroupId());
boolean haveKeys = db.containsTransportKeys(txn, c, t);
if (ss == null) {
if (haveKeys) {
// We have keys but no session, so we must have derived keys
// when adding the contact. If the contact didn't support
// the transport when they added us, they wouldn't have
// derived keys at that time. If they later added support for
// the transport then they would have started a session, so a
// key message is valid in this case
return handleKeyMessageForNewSession(txn, c, t, m, meta);
} else {
// We don't have keys, so we should have created a session at
// startup
throw new IllegalStateException();
}
} else if (ss.session.getState() == AWAIT_KEY) {
if (haveKeys) {
// We have keys, so we shouldn't be in the AWAIT_KEY state,
// even if the contact didn't derive keys when adding us and
// later started a session
throw new IllegalStateException();
} else {
// This is the key message we're waiting for
return handleKeyMessageForExistingSession(txn, c, t, m, meta,
ss);
}
} else {
return REJECT; // Not valid in this state
}
}
private DeliveryAction handleActivateMessage(Transaction txn,
TransportId t, @Nullable SavedSession ss) throws DbException {
if (ss != null && ss.session.getState() == AWAIT_ACTIVATE) {
// Activate the keys and finish the session
KeySetId keySetId = requireNonNull(ss.session.getKeySetId());
keyManager.activateKeys(txn, singletonMap(t, keySetId));
Session session = new Session(ACTIVATED,
ss.session.getLastLocalMessageId(), null, null, null);
saveSession(txn, t, ss.storageId, session);
return ACCEPT_DO_NOT_SHARE;
} else {
return REJECT; // Not valid in this state
}
}
private DeliveryAction handleKeyMessageForNewSession(Transaction txn,
ContactId c, TransportId t, Message m, BdfDictionary meta)
throws DbException, FormatException {
KeyPair localKeyPair = crypto.generateKeyPair();
PublicKey remotePublicKey =
crypto.parsePublicKey(meta.getRaw(MSG_KEY_PUBLIC_KEY));
Message keyMessage = sendKeyMessage(txn, m.getGroupId(), t,
localKeyPair.getPublic());
long minTimestamp = min(keyMessage.getTimestamp(), m.getTimestamp());
SecretKey rootKey;
try {
rootKey = crypto.deriveRootKey(localKeyPair, remotePublicKey);
} catch (GeneralSecurityException e) {
return REJECT; // Invalid public key
}
boolean alice = isLocalPartyAlice(txn, db.getContact(txn, c));
KeySetId keySetId = keyManager.addRotationKeys(txn, c, t, rootKey,
minTimestamp, alice, false);
Message activateMessage =
sendActivateMessage(txn, m.getGroupId(), t, keyMessage.getId());
Session session = new Session(AWAIT_ACTIVATE, activateMessage.getId(),
null, null, keySetId);
saveNewSession(txn, m.getGroupId(), t, session);
return ACCEPT_DO_NOT_SHARE;
}
private DeliveryAction handleKeyMessageForExistingSession(Transaction txn,
ContactId c, TransportId t, Message m, BdfDictionary meta,
SavedSession ss) throws DbException, FormatException {
KeyPair localKeyPair = requireNonNull(ss.session.getLocalKeyPair());
PublicKey remotePublicKey =
crypto.parsePublicKey(meta.getRaw(MSG_KEY_PUBLIC_KEY));
long localTimestamp = requireNonNull(ss.session.getLocalTimestamp());
long minTimestamp = min(localTimestamp, m.getTimestamp());
SecretKey rootKey;
try {
rootKey = crypto.deriveRootKey(localKeyPair, remotePublicKey);
} catch (GeneralSecurityException e) {
return REJECT; // Invalid public key
}
boolean alice = isLocalPartyAlice(txn, db.getContact(txn, c));
KeySetId keySetId = keyManager.addRotationKeys(txn, c, t, rootKey,
minTimestamp, alice, false);
MessageId previousMessageId =
requireNonNull(ss.session.getLastLocalMessageId());
Message activateMessage =
sendActivateMessage(txn, m.getGroupId(), t, previousMessageId);
Session session = new Session(AWAIT_ACTIVATE, activateMessage.getId(),
null, null, keySetId);
saveSession(txn, t, ss.storageId, session);
return ACCEPT_DO_NOT_SHARE;
}
private void startSession(Transaction txn, GroupId contactGroupId,
TransportId t) throws DbException {
KeyPair localKeyPair = crypto.generateKeyPair();
Message keyMessage = sendKeyMessage(txn, contactGroupId, t,
localKeyPair.getPublic());
Session session = new Session(AWAIT_KEY, keyMessage.getId(),
localKeyPair, keyMessage.getTimestamp(), null);
saveNewSession(txn, contactGroupId, t, session);
}
@Nullable
private SavedSession loadSession(Transaction txn, GroupId contactGroupId,
TransportId t) throws DbException {
try {
BdfDictionary query = sessionEncoder.getSessionQuery(t);
Collection<MessageId> ids =
clientHelper.getMessageIds(txn, contactGroupId, query);
if (ids.size() > 1) throw new DbException();
if (ids.isEmpty()) {
if (LOG.isLoggable(INFO)) LOG.info("No session for " + t);
return null;
}
MessageId storageId = ids.iterator().next();
BdfDictionary bdfSession =
clientHelper.getMessageMetadataAsDictionary(txn, storageId);
Session session = sessionParser.parseSession(bdfSession);
if (LOG.isLoggable(INFO)) {
LOG.info("Loaded session in state " + session.getState()
+ " for " + t);
}
return new SavedSession(session, storageId);
} catch (FormatException e) {
throw new DbException(e);
}
}
private void saveNewSession(Transaction txn, GroupId contactGroupId,
TransportId t, Session session) throws DbException {
Message m =
clientHelper.createMessageForStoringMetadata(contactGroupId);
db.addLocalMessage(txn, m, new Metadata(), false, false);
MessageId storageId = m.getId();
saveSession(txn, t, storageId, session);
}
private void saveSession(Transaction txn, TransportId t,
MessageId storageId, Session session) throws DbException {
if (LOG.isLoggable(INFO)) {
LOG.info("Saving session in state " + session.getState()
+ " for " + t);
}
BdfDictionary meta = sessionEncoder.encodeSession(session, t);
try {
clientHelper.mergeMessageMetadata(txn, storageId, meta);
} catch (FormatException e) {
throw new AssertionError();
}
}
private Message sendKeyMessage(Transaction txn, GroupId contactGroupId,
TransportId t, PublicKey publicKey) throws DbException {
Message m = messageEncoder.encodeKeyMessage(contactGroupId, t,
publicKey);
sendMessage(txn, t, m, KEY);
return m;
}
private Message sendActivateMessage(Transaction txn,
GroupId contactGroupId, TransportId t, MessageId previousMessageId)
throws DbException {
Message m = messageEncoder.encodeActivateMessage(contactGroupId, t,
previousMessageId);
sendMessage(txn, t, m, ACTIVATE);
return m;
}
private void sendMessage(Transaction txn, TransportId t, Message m,
MessageType type) throws DbException {
BdfDictionary meta =
messageEncoder.encodeMessageMetadata(t, type, true);
try {
clientHelper.addLocalMessage(txn, m, meta, true, false);
} catch (FormatException e) {
throw new AssertionError();
}
}
private Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID,
MAJOR_VERSION, c);
}
private boolean isLocalPartyAlice(Transaction txn, Contact c)
throws DbException {
Author local = identityManager.getLocalAuthor(txn);
Author remote = c.getAuthor();
return compare(local.getId().getBytes(), remote.getId().getBytes()) < 0;
}
private static class SavedSession {
private final Session session;
private final MessageId storageId;
private SavedSession(Session session, MessageId storageId) {
this.session = session;
this.storageId = storageId;
}
}
}

View File

@@ -0,0 +1,83 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.validation.ValidationManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.agreement.TransportKeyAgreementManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static org.briarproject.bramble.api.transport.agreement.TransportKeyAgreementManager.CLIENT_ID;
import static org.briarproject.bramble.api.transport.agreement.TransportKeyAgreementManager.MAJOR_VERSION;
import static org.briarproject.bramble.api.transport.agreement.TransportKeyAgreementManager.MINOR_VERSION;
@Module
public class TransportKeyAgreementModule {
public static class EagerSingletons {
@Inject
TransportKeyAgreementManager transportKeyAgreementManager;
@Inject
TransportKeyAgreementValidator transportKeyAgreementValidator;
}
@Provides
@Singleton
TransportKeyAgreementManager provideTransportKeyAgreementManager(
LifecycleManager lifecycleManager,
ValidationManager validationManager,
ContactManager contactManager,
ClientVersioningManager clientVersioningManager,
TransportKeyAgreementManagerImpl transportKeyAgreementManager) {
lifecycleManager.registerOpenDatabaseHook(transportKeyAgreementManager);
validationManager.registerIncomingMessageHook(CLIENT_ID,
MAJOR_VERSION, transportKeyAgreementManager);
contactManager.registerContactHook(transportKeyAgreementManager);
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, transportKeyAgreementManager);
return transportKeyAgreementManager;
}
@Provides
@Singleton
TransportKeyAgreementValidator provideTransportKeyAgreementValidator(
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock, MessageEncoder messageEncoder,
ValidationManager validationManager) {
TransportKeyAgreementValidator validator =
new TransportKeyAgreementValidator(clientHelper,
metadataEncoder, clock, messageEncoder);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator;
}
@Provides
MessageEncoder provideMessageEncoder(MessageEncoderImpl messageEncoder) {
return messageEncoder;
}
@Provides
SessionEncoder provideSessionEncoder(SessionEncoderImpl sessionEncoder) {
return sessionEncoder;
}
@Provides
SessionParser provideSessionParser(SessionParserImpl sessionParser) {
return sessionParser;
}
@Provides
TransportKeyAgreementCrypto provideTransportKeyAgreementCrypto(
TransportKeyAgreementCryptoImpl transportKeyAgreementCrypto) {
return transportKeyAgreementCrypto;
}
}

View File

@@ -0,0 +1,79 @@
package org.briarproject.bramble.transport.agreement;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.client.BdfMessageValidator;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import javax.annotation.concurrent.Immutable;
import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
import static org.briarproject.bramble.transport.agreement.MessageType.ACTIVATE;
import static org.briarproject.bramble.transport.agreement.MessageType.KEY;
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_PUBLIC_KEY;
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
@Immutable
@NotNullByDefault
class TransportKeyAgreementValidator extends BdfMessageValidator {
private final MessageEncoder messageEncoder;
TransportKeyAgreementValidator(ClientHelper clientHelper,
MetadataEncoder metadataEncoder, Clock clock,
MessageEncoder messageEncoder) {
super(clientHelper, metadataEncoder, clock);
this.messageEncoder = messageEncoder;
}
@Override
protected BdfMessageContext validateMessage(Message m, Group g,
BdfList body) throws FormatException {
MessageType type = MessageType.fromValue(body.getLong(0).intValue());
if (type == KEY) return validateKeyMessage(m.getTimestamp(), body);
else if (type == ACTIVATE) return validateActivateMessage(body);
else throw new AssertionError();
}
private BdfMessageContext validateKeyMessage(long timestamp, BdfList body)
throws FormatException {
if (timestamp < MIN_REASONABLE_TIME_MS) throw new FormatException();
// Message type, transport ID, public key
checkSize(body, 3);
String transportId = body.getString(1);
checkLength(transportId, 1, MAX_TRANSPORT_ID_LENGTH);
byte[] publicKey = body.getRaw(2);
checkLength(publicKey, 1, MAX_AGREEMENT_PUBLIC_KEY_BYTES);
BdfDictionary meta = messageEncoder.encodeMessageMetadata(
new TransportId(transportId), KEY, false);
meta.put(MSG_KEY_PUBLIC_KEY, publicKey);
return new BdfMessageContext(meta);
}
private BdfMessageContext validateActivateMessage(BdfList body)
throws FormatException {
// Message type, transport ID, previous message ID
checkSize(body, 3);
String transportId = body.getString(1);
checkLength(transportId, 1, MAX_TRANSPORT_ID_LENGTH);
byte[] previousMessageId = body.getRaw(2);
checkLength(previousMessageId, MessageId.LENGTH);
BdfDictionary meta = messageEncoder.encodeMessageMetadata(
new TransportId(transportId), ACTIVATE, false);
MessageId dependency = new MessageId(previousMessageId);
return new BdfMessageContext(meta, singletonList(dependency));
}
}

View File

@@ -50,6 +50,7 @@ import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL; import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION; import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
@@ -173,8 +174,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
@Override @Override
public boolean incomingMessage(Transaction txn, Message m, Metadata meta) public DeliveryAction incomingMessage(Transaction txn, Message m,
throws DbException, InvalidMessageException { Metadata meta) throws DbException, InvalidMessageException {
try { try {
// Parse the new remote update // Parse the new remote update
Update newRemoteUpdate = parseUpdate(clientHelper.toList(m)); Update newRemoteUpdate = parseUpdate(clientHelper.toList(m));
@@ -187,7 +188,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
&& latest.remote.updateVersion > newRemoteUpdateVersion) { && latest.remote.updateVersion > newRemoteUpdateVersion) {
db.deleteMessage(txn, m.getId()); db.deleteMessage(txn, m.getId());
db.deleteMessageMetadata(txn, m.getId()); db.deleteMessageMetadata(txn, m.getId());
return false; return ACCEPT_DO_NOT_SHARE;
} }
// Load and parse the latest local update // Load and parse the latest local update
if (latest.local == null) throw new DbException(); if (latest.local == null) throw new DbException();
@@ -241,7 +242,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} catch (FormatException e) { } catch (FormatException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }
return false; return ACCEPT_DO_NOT_SHARE;
} }
private void storeClientVersions(Transaction txn, private void storeClientVersions(Transaction txn,

View File

@@ -1,6 +1,3 @@
Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1
Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1
Bridge obfs4 38.229.33.83:80 0BAC39417268B96B9F514E7F63FA6FBA1A788955 cert=VwEFpk9F/UN9JED7XpG1XOjm/O8ZCXK80oPecgWnNDZDv5pdkhq1OpbAH0wNqOT6H6BmRQ iat-mode=1
Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0 Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0 Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0 Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0

View File

@@ -120,7 +120,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final MessageId messageId, messageId1; private final MessageId messageId, messageId1;
private final Metadata metadata; private final Metadata metadata;
private final TransportId transportId; private final TransportId transportId;
private final int maxLatency; private final long maxLatency;
private final ContactId contactId; private final ContactId contactId;
private final Contact contact; private final Contact contact;
private final KeySetId keySetId; private final KeySetId keySetId;
@@ -358,7 +358,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try { try {
db.transaction(true, transaction -> db.transaction(true, transaction ->
db.getMessageBytesToSend(transaction, contactId, 123)); db.getUnackedMessageBytesToSend(transaction, contactId));
fail(); fail();
} catch (NoSuchContactException expected) { } catch (NoSuchContactException expected) {
// Expected // Expected

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test; import org.junit.Test;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_TRANSPORT_LATENCY;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
public class ExponentialBackoffTest extends BrambleTestCase { public class ExponentialBackoffTest extends BrambleTestCase {
@@ -36,28 +37,28 @@ public class ExponentialBackoffTest extends BrambleTestCase {
@Test @Test
public void testTransmissionCountOverflow() { public void testTransmissionCountOverflow() {
int maxLatency = Integer.MAX_VALUE; // RTT will not overflow long maxLatency = MAX_TRANSPORT_LATENCY; // RTT will not overflow
long expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 0); long expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 0);
assertEquals(Integer.MAX_VALUE * 2L, expiry); // No overflow assertEquals(MAX_TRANSPORT_LATENCY * 2L, expiry); // No overflow
expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 31); expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 27);
assertEquals(Integer.MAX_VALUE * (2L << 31), expiry); // No overflow assertEquals(MAX_TRANSPORT_LATENCY * (2L << 27), expiry); // No overflow
expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 32); expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 28);
assertEquals(Long.MAX_VALUE, expiry); // Overflow caught assertEquals(Long.MAX_VALUE, expiry); // Overflow caught
expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 33); expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 29);
assertEquals(Long.MAX_VALUE, expiry); // Overflow caught assertEquals(Long.MAX_VALUE, expiry); // Overflow caught
} }
@Test @Test
public void testCurrentTimeOverflow() { public void testCurrentTimeOverflow() {
int maxLatency = Integer.MAX_VALUE; // RTT will not overflow long maxLatency = MAX_TRANSPORT_LATENCY; // RTT will not overflow
long now = Long.MAX_VALUE - (Integer.MAX_VALUE * (2L << 31)); long now = Long.MAX_VALUE - (MAX_TRANSPORT_LATENCY * (2L << 27));
long expiry = ExponentialBackoff.calculateExpiry(now, maxLatency, 0); long expiry = ExponentialBackoff.calculateExpiry(now, maxLatency, 0);
assertEquals(now + Integer.MAX_VALUE * 2L, expiry); // No overflow assertEquals(now + MAX_TRANSPORT_LATENCY * 2L, expiry); // No overflow
expiry = ExponentialBackoff.calculateExpiry(now - 1, maxLatency, 31); expiry = ExponentialBackoff.calculateExpiry(now - 1, maxLatency, 27);
assertEquals(Long.MAX_VALUE - 1, expiry); // No overflow assertEquals(Long.MAX_VALUE - 1, expiry); // No overflow
expiry = ExponentialBackoff.calculateExpiry(now, maxLatency, 31); expiry = ExponentialBackoff.calculateExpiry(now, maxLatency, 27);
assertEquals(Long.MAX_VALUE, expiry); // No overflow assertEquals(Long.MAX_VALUE, expiry); // No overflow
expiry = ExponentialBackoff.calculateExpiry(now + 1, maxLatency, 32); expiry = ExponentialBackoff.calculateExpiry(now + 1, maxLatency, 27);
assertEquals(Long.MAX_VALUE, expiry); // Overflow caught assertEquals(Long.MAX_VALUE, expiry); // Overflow caught
} }
} }

View File

@@ -57,6 +57,7 @@ import java.util.concurrent.atomic.AtomicLong;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
@@ -222,21 +223,13 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, false, null);
// The contact has not seen the message, so it should be sendable // The contact has not seen the message, so it should be sendable
Collection<MessageId> ids = assertOneMessageToSendEagerly(db, txn);
db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertOneMessageToSendLazily(db, txn);
assertEquals(singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
assertEquals(message.getRawLength(),
db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Changing the status to seen = true should make the message unsendable // Changing the status to seen = true should make the message unsendable
db.raiseSeenFlag(txn, contactId, messageId); db.raiseSeenFlag(txn, contactId, messageId);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertNothingToSendEagerly(db, txn);
assertTrue(ids.isEmpty()); assertNothingToSendLazily(db, txn);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -256,37 +249,23 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessage(txn, message, UNKNOWN, true, false, null); db.addMessage(txn, message, UNKNOWN, true, false, null);
// The message has not been validated, so it should not be sendable // The message has not been validated, so it should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, assertNothingToSendLazily(db, txn);
ONE_MEGABYTE, MAX_LATENCY); assertNothingToSendEagerly(db, txn);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Marking the message delivered should make it sendable // Marking the message delivered should make it sendable
db.setMessageState(txn, messageId, DELIVERED); db.setMessageState(txn, messageId, DELIVERED);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertOneMessageToSendLazily(db, txn);
assertEquals(singletonList(messageId), ids); assertOneMessageToSendEagerly(db, txn);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
assertEquals(message.getRawLength(),
db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Marking the message invalid should make it unsendable // Marking the message invalid should make it unsendable
db.setMessageState(txn, messageId, INVALID); db.setMessageState(txn, messageId, INVALID);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertNothingToSendLazily(db, txn);
assertTrue(ids.isEmpty()); assertNothingToSendEagerly(db, txn);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Marking the message pending should make it unsendable // Marking the message pending should make it unsendable
db.setMessageState(txn, messageId, PENDING); db.setMessageState(txn, messageId, PENDING);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertNothingToSendLazily(db, txn);
assertTrue(ids.isEmpty()); assertNothingToSendEagerly(db, txn);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -305,45 +284,28 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, false, null);
// The group is invisible, so the message should not be sendable // The group is invisible, so the message should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, assertNothingToSendLazily(db, txn);
ONE_MEGABYTE, MAX_LATENCY); assertNothingToSendEagerly(db, txn);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Making the group visible should not make the message sendable // Making the group visible should not make the message sendable
db.addGroupVisibility(txn, contactId, groupId, false); db.addGroupVisibility(txn, contactId, groupId, false);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertNothingToSendLazily(db, txn);
assertTrue(ids.isEmpty()); assertNothingToSendEagerly(db, txn);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Sharing the group should make the message sendable // Sharing the group should make the message sendable
db.setGroupVisibility(txn, contactId, groupId, true); db.setGroupVisibility(txn, contactId, groupId, true);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertOneMessageToSendEagerly(db, txn);
assertEquals(singletonList(messageId), ids); assertOneMessageToSendLazily(db, txn);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
assertEquals(message.getRawLength(),
db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Unsharing the group should make the message unsendable // Unsharing the group should make the message unsendable
db.setGroupVisibility(txn, contactId, groupId, false); db.setGroupVisibility(txn, contactId, groupId, false);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertNothingToSendLazily(db, txn);
assertTrue(ids.isEmpty()); assertNothingToSendEagerly(db, txn);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Making the group invisible should make the message unsendable // Making the group invisible should make the message unsendable
db.removeGroupVisibility(txn, contactId, groupId); db.removeGroupVisibility(txn, contactId, groupId);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertNothingToSendLazily(db, txn);
assertTrue(ids.isEmpty()); assertNothingToSendEagerly(db, txn);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -363,21 +325,13 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessage(txn, message, DELIVERED, false, false, null); db.addMessage(txn, message, DELIVERED, false, false, null);
// The message is not shared, so it should not be sendable // The message is not shared, so it should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, assertNothingToSendLazily(db, txn);
ONE_MEGABYTE, MAX_LATENCY); assertNothingToSendEagerly(db, txn);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Sharing the message should make it sendable // Sharing the message should make it sendable
db.setMessageShared(txn, messageId, true); db.setMessageShared(txn, messageId, true);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertOneMessageToSendLazily(db, txn);
assertEquals(singletonList(messageId), ids); assertOneMessageToSendEagerly(db, txn);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
assertEquals(message.getRawLength(),
db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -397,19 +351,17 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, false, null);
// The message is sendable, but too large to send // The message is sendable, but too large to send
assertOneMessageToSendLazily(db, txn);
assertOneMessageToSendEagerly(db, txn);
Collection<MessageId> ids = Collection<MessageId> ids =
db.getMessagesToSend(txn, contactId, message.getRawLength() - 1, db.getMessagesToSend(txn, contactId, message.getRawLength() - 1,
MAX_LATENCY); MAX_LATENCY);
assertTrue(ids.isEmpty()); assertTrue(ids.isEmpty());
assertEquals(message.getRawLength(),
db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// The message is just the right size to send // The message is just the right size to send
ids = db.getMessagesToSend(txn, contactId, message.getRawLength(), ids = db.getMessagesToSend(txn, contactId, message.getRawLength(),
MAX_LATENCY); MAX_LATENCY);
assertEquals(singletonList(messageId), ids); assertEquals(singletonList(messageId), ids);
assertEquals(message.getRawLength(),
db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -427,6 +379,12 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, false); db.addGroupVisibility(txn, contactId, groupId, false);
// Initially there should be nothing to send
assertFalse(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, false));
assertFalse(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, true));
// Add some messages to ack // Add some messages to ack
Message message1 = getMessage(groupId); Message message1 = getMessage(groupId);
MessageId messageId1 = message1.getId(); MessageId messageId1 = message1.getId();
@@ -434,6 +392,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessage(txn, message1, DELIVERED, true, false, contactId); db.addMessage(txn, message1, DELIVERED, true, false, contactId);
// Both message IDs should be returned // Both message IDs should be returned
assertTrue(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, false));
assertTrue(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, true));
Collection<MessageId> ids = db.getMessagesToAck(txn, contactId, 1234); Collection<MessageId> ids = db.getMessagesToAck(txn, contactId, 1234);
assertEquals(asList(messageId, messageId1), ids); assertEquals(asList(messageId, messageId1), ids);
@@ -441,6 +403,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.lowerAckFlag(txn, contactId, asList(messageId, messageId1)); db.lowerAckFlag(txn, contactId, asList(messageId, messageId1));
// Both message IDs should have been removed // Both message IDs should have been removed
assertFalse(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, false));
assertFalse(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, true));
assertEquals(emptyList(), db.getMessagesToAck(txn, assertEquals(emptyList(), db.getMessagesToAck(txn,
contactId, 1234)); contactId, 1234));
@@ -449,6 +415,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.raiseAckFlag(txn, contactId, messageId1); db.raiseAckFlag(txn, contactId, messageId1);
// Both message IDs should be returned // Both message IDs should be returned
assertTrue(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, false));
assertTrue(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, true));
ids = db.getMessagesToAck(txn, contactId, 1234); ids = db.getMessagesToAck(txn, contactId, 1234);
assertEquals(asList(messageId, messageId1), ids); assertEquals(asList(messageId, messageId1), ids);
@@ -469,22 +439,25 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, false, null);
// Retrieve the message from the database and mark it as sent // The message should be sendable via lazy or eager retransmission
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, assertOneMessageToSendLazily(db, txn);
ONE_MEGABYTE, MAX_LATENCY); assertOneMessageToSendEagerly(db, txn);
assertEquals(singletonList(messageId), ids);
// Mark the message as sent
db.updateExpiryTimeAndEta(txn, contactId, messageId, MAX_LATENCY); db.updateExpiryTimeAndEta(txn, contactId, messageId, MAX_LATENCY);
// The message should no longer be sendable // The message should no longer be sendable via lazy retransmission,
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); // but it should still be sendable via eager retransmission
assertTrue(ids.isEmpty()); assertNothingToSendLazily(db, txn);
assertOneMessageToSendEagerly(db, txn);
// Pretend that the message was acked // Mark the message as acked
db.raiseSeenFlag(txn, contactId, messageId); db.raiseSeenFlag(txn, contactId, messageId);
// The message still should not be sendable // The message still should not be sendable via lazy or eager
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); // retransmission
assertTrue(ids.isEmpty()); assertNothingToSendLazily(db, txn);
assertNothingToSendEagerly(db, txn);
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -698,7 +671,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Initially there should be no transport keys in the database // Initially there should be no transport keys in the database
assertFalse(db.containsTransportKeys(txn, contactId, transportId));
assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
assertTrue(db.getTransportsWithKeys(txn).isEmpty());
// Add the contact, the transport and the transport keys // Add the contact, the transport and the transport keys
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
@@ -709,6 +684,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1)); assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
// Retrieve the transport keys // Retrieve the transport keys
assertTrue(db.containsTransportKeys(txn, contactId, transportId));
Collection<TransportKeySet> allKeys = Collection<TransportKeySet> allKeys =
db.getTransportKeys(txn, transportId); db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size()); assertEquals(2, allKeys.size());
@@ -721,6 +697,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertKeysEquals(keys1, ks.getKeys()); assertKeysEquals(keys1, ks.getKeys());
} }
} }
assertEquals(singletonMap(contactId, singletonList(transportId)),
db.getTransportsWithKeys(txn));
// Update the transport keys // Update the transport keys
TransportKeys updated = createTransportKeys(timePeriod + 1, active); TransportKeys updated = createTransportKeys(timePeriod + 1, active);
@@ -732,6 +710,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
null, updated1)); null, updated1));
// Retrieve the transport keys again // Retrieve the transport keys again
assertTrue(db.containsTransportKeys(txn, contactId, transportId));
allKeys = db.getTransportKeys(txn, transportId); allKeys = db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size()); assertEquals(2, allKeys.size());
for (TransportKeySet ks : allKeys) { for (TransportKeySet ks : allKeys) {
@@ -743,10 +722,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertKeysEquals(updated1, ks.getKeys()); assertKeysEquals(updated1, ks.getKeys());
} }
} }
assertEquals(singletonMap(contactId, singletonList(transportId)),
db.getTransportsWithKeys(txn));
// Removing the contact should remove the transport keys // Removing the contact should remove the transport keys
db.removeContact(txn, contactId); db.removeContact(txn, contactId);
assertFalse(db.containsTransportKeys(txn, contactId, transportId));
assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
assertTrue(db.getTransportsWithKeys(txn).isEmpty());
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -1947,11 +1930,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(db.containsVisibleMessage(txn, contactId, messageId)); assertTrue(db.containsVisibleMessage(txn, contactId, messageId));
// The message should be sendable // The message should be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, assertOneMessageToSendLazily(db, txn);
ONE_MEGABYTE, MAX_LATENCY); assertOneMessageToSendEagerly(db, txn);
assertEquals(singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
// The message should be available // The message should be available
Message m = db.getMessage(txn, messageId); Message m = db.getMessage(txn, messageId);
@@ -1967,10 +1947,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(db.containsVisibleMessage(txn, contactId, messageId)); assertTrue(db.containsVisibleMessage(txn, contactId, messageId));
// The message should not be sendable // The message should not be sendable
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); assertNothingToSendLazily(db, txn);
assertTrue(ids.isEmpty()); assertNothingToSendEagerly(db, txn);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
// Requesting the message should throw an exception // Requesting the message should throw an exception
try { try {
@@ -2599,6 +2577,50 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
deleteTestDirectory(testDir); deleteTestDirectory(testDir);
} }
private void assertNothingToSendLazily(Database<Connection> db,
Connection txn) throws Exception {
assertFalse(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, false));
Collection<MessageId> ids =
db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
}
private void assertOneMessageToSendLazily(Database<Connection> db,
Connection txn) throws Exception {
assertTrue(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, false));
Collection<MessageId> ids =
db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
}
private void assertNothingToSendEagerly(Database<Connection> db,
Connection txn) throws Exception {
assertFalse(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, true));
Map<MessageId, Integer> unacked =
db.getUnackedMessagesToSend(txn, contactId);
assertTrue(unacked.isEmpty());
assertEquals(0, db.getUnackedMessageBytesToSend(txn, contactId));
}
private void assertOneMessageToSendEagerly(Database<Connection> db,
Connection txn) throws Exception {
assertTrue(
db.containsAnythingToSend(txn, contactId, MAX_LATENCY, true));
Map<MessageId, Integer> unacked =
db.getUnackedMessagesToSend(txn, contactId);
assertEquals(singleton(messageId), unacked.keySet());
assertEquals(message.getRawLength(), unacked.get(messageId).intValue());
assertEquals(message.getRawLength(),
db.getUnackedMessageBytesToSend(txn, contactId));
}
private static class StoppedClock implements Clock { private static class StoppedClock implements Clock {
private final long time; private final long time;

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook; import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
import org.junit.Before; import org.junit.Before;
@@ -14,7 +15,10 @@ import org.junit.Test;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertTrue;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.CLOCK_ERROR;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
import static org.briarproject.bramble.api.system.Clock.MAX_REASONABLE_TIME_MS;
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -22,6 +26,7 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class); private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final EventBus eventBus = context.mock(EventBus.class); private final EventBus eventBus = context.mock(EventBus.class);
private final Clock clock = context.mock(Clock.class);
private final SecretKey dbKey = getSecretKey(); private final SecretKey dbKey = getSecretKey();
@@ -29,16 +34,19 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
@Before @Before
public void setUp() { public void setUp() {
lifecycleManager = new LifecycleManagerImpl(db, eventBus); lifecycleManager = new LifecycleManagerImpl(db, eventBus, clock);
} }
@Test @Test
public void testOpenDatabaseHooksAreCalledAtStartup() throws Exception { public void testOpenDatabaseHooksAreCalledAtStartup() throws Exception {
long now = System.currentTimeMillis();
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
AtomicBoolean called = new AtomicBoolean(false); AtomicBoolean called = new AtomicBoolean(false);
OpenDatabaseHook hook = transaction -> called.set(true); OpenDatabaseHook hook = transaction -> called.set(true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(db).open(dbKey, lifecycleManager); oneOf(db).open(dbKey, lifecycleManager);
will(returnValue(false)); will(returnValue(false));
oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(db).transaction(with(false), withDbRunnable(txn));
@@ -51,4 +59,26 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
assertEquals(SUCCESS, lifecycleManager.startServices(dbKey)); assertEquals(SUCCESS, lifecycleManager.startServices(dbKey));
assertTrue(called.get()); assertTrue(called.get());
} }
@Test
public void testStartupFailsIfClockIsUnreasonablyBehind() {
context.checking(new DbExpectations() {{
oneOf(clock).currentTimeMillis();
will(returnValue(MIN_REASONABLE_TIME_MS - 1));
}});
assertEquals(CLOCK_ERROR, lifecycleManager.startServices(dbKey));
}
@Test
public void testStartupFailsIfClockIsUnreasonablyAhead() {
context.checking(new DbExpectations() {{
oneOf(clock).currentTimeMillis();
will(returnValue(MAX_REASONABLE_TIME_MS + 1));
}});
assertEquals(CLOCK_ERROR, lifecycleManager.startServices(dbKey));
}
} }

View File

@@ -70,9 +70,9 @@ public class RemovableDriveIntegrationTest extends BrambleTestCase {
ContactId aliceId = setUp(bob, bobIdentity, ContactId aliceId = setUp(bob, bobIdentity,
aliceIdentity.getLocalAuthor(), false); aliceIdentity.getLocalAuthor(), false);
// Sync Alice's client versions and transport properties // Sync Alice's client versions and transport properties
read(bob, aliceId, write(alice, bobId), 2); read(bob, write(alice, bobId), 2);
// Sync Bob's client versions and transport properties // Sync Bob's client versions and transport properties
read(alice, bobId, write(bob, aliceId), 2); read(alice, write(bob, aliceId), 2);
} }
private ContactId setUp(RemovableDriveIntegrationTestComponent device, private ContactId setUp(RemovableDriveIntegrationTestComponent device,
@@ -92,7 +92,7 @@ public class RemovableDriveIntegrationTest extends BrambleTestCase {
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
private void read(RemovableDriveIntegrationTestComponent device, private void read(RemovableDriveIntegrationTestComponent device,
ContactId contactId, File file, int deliveries) throws Exception { File file, int deliveries) throws Exception {
// Listen for message deliveries // Listen for message deliveries
MessageDeliveryListener listener = MessageDeliveryListener listener =
new MessageDeliveryListener(deliveries); new MessageDeliveryListener(deliveries);
@@ -100,8 +100,8 @@ public class RemovableDriveIntegrationTest extends BrambleTestCase {
// Read the incoming stream // Read the incoming stream
TransportProperties p = new TransportProperties(); TransportProperties p = new TransportProperties();
p.put(PROP_PATH, file.getAbsolutePath()); p.put(PROP_PATH, file.getAbsolutePath());
RemovableDriveTask reader = device.getRemovableDriveManager() RemovableDriveTask reader =
.startReaderTask(contactId, p); device.getRemovableDriveManager().startReaderTask(p);
CountDownLatch disposedLatch = new CountDownLatch(1); CountDownLatch disposedLatch = new CountDownLatch(1);
reader.addObserver(state -> { reader.addObserver(state -> {
if (state.isFinished()) disposedLatch.countDown(); if (state.isFinished()) disposedLatch.countDown();

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