mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
494 Commits
beta-1.2.2
...
beta-1.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6517f3f2d0 | ||
|
|
70d5150faf | ||
|
|
770c87c723 | ||
|
|
2b5446759f | ||
|
|
edccb9ae14 | ||
|
|
1337fc46b3 | ||
|
|
ed26dc0b2b | ||
|
|
bf9fe6a146 | ||
|
|
448ea114f3 | ||
|
|
abc523fae3 | ||
|
|
6de5f424b8 | ||
|
|
703559102a | ||
|
|
4acc5f4d8c | ||
|
|
4a4d8f4ccf | ||
|
|
807677532c | ||
|
|
7e9d64b6ad | ||
|
|
f963c4cfdd | ||
|
|
7388da410f | ||
|
|
3635c35923 | ||
|
|
7c1399c326 | ||
|
|
c002cc2e73 | ||
|
|
f3273260bb | ||
|
|
abf99f0219 | ||
|
|
7405ed7196 | ||
|
|
b53203581c | ||
|
|
d522942bdd | ||
|
|
802015d995 | ||
|
|
c36352f2b8 | ||
|
|
21a2f91521 | ||
|
|
d8267ce559 | ||
|
|
8f887c609f | ||
|
|
b077e5f94f | ||
|
|
2b61b01b4e | ||
|
|
822a58c8a6 | ||
|
|
09b065f46e | ||
|
|
be9255029b | ||
|
|
f596811997 | ||
|
|
1be8ac6e14 | ||
|
|
571ec2257e | ||
|
|
7fb2faba45 | ||
|
|
a390bf1c4f | ||
|
|
9d031fa796 | ||
|
|
d678043f8e | ||
|
|
b11147265d | ||
|
|
2fe052d77e | ||
|
|
8e91322869 | ||
|
|
1de5779e2c | ||
|
|
99b2c8af69 | ||
|
|
b1cc4fe006 | ||
|
|
d65afc519a | ||
|
|
32cbdff532 | ||
|
|
48292d2e47 | ||
|
|
89bd9ee653 | ||
|
|
61aa3a839d | ||
|
|
e38e9b943d | ||
|
|
4eb5c2ac10 | ||
|
|
ebaa3271dd | ||
|
|
adb6b4fba5 | ||
|
|
917a470559 | ||
|
|
a188e41134 | ||
|
|
b9ba813b23 | ||
|
|
b7d46b9340 | ||
|
|
60aaa4a7c1 | ||
|
|
d411b99030 | ||
|
|
acacb59114 | ||
|
|
2e07e79e4c | ||
|
|
e9dbceefe8 | ||
|
|
8cdb314170 | ||
|
|
39d3f47e19 | ||
|
|
522474ac15 | ||
|
|
ed6c4ba634 | ||
|
|
49562cbd79 | ||
|
|
6337b86266 | ||
|
|
93eadb88f3 | ||
|
|
46e391645c | ||
|
|
355c487ec9 | ||
|
|
d1c0f1b2f6 | ||
|
|
445ef0818c | ||
|
|
8af743db71 | ||
|
|
806fce8c34 | ||
|
|
f9494d71de | ||
|
|
df38187288 | ||
|
|
b8009c35f1 | ||
|
|
1306761f4a | ||
|
|
703ff9835d | ||
|
|
4abaeed32f | ||
|
|
9192ee32cf | ||
|
|
aecd204efe | ||
|
|
03cb1010e2 | ||
|
|
30063f5fbf | ||
|
|
0fb52a7f53 | ||
|
|
094024eb4f | ||
|
|
e39c99fd6c | ||
|
|
6cd70e0e7f | ||
|
|
d646635b1f | ||
|
|
a534ec2b50 | ||
|
|
a23de6172f | ||
|
|
ff2dd33435 | ||
|
|
d5d0a03638 | ||
|
|
344fff4a7a | ||
|
|
f9749fda80 | ||
|
|
aabba3a6c8 | ||
|
|
673f530c14 | ||
|
|
36a1478661 | ||
|
|
1c056160e1 | ||
|
|
ab6b83d4fa | ||
|
|
a6c33d300c | ||
|
|
28d87dd153 | ||
|
|
16b79e0482 | ||
|
|
3eee144c6c | ||
|
|
1b7007d4ef | ||
|
|
19a5c2f79f | ||
|
|
8c163d8f10 | ||
|
|
c3cd32b12c | ||
|
|
7c8aa5bc21 | ||
|
|
54b239f45e | ||
|
|
97bd977108 | ||
|
|
aaba9f2417 | ||
|
|
6a909b6c5c | ||
|
|
4ef92f1c39 | ||
|
|
8f392b4599 | ||
|
|
f556bc7249 | ||
|
|
e48886c95a | ||
|
|
e2879cd664 | ||
|
|
c3977e9276 | ||
|
|
b93803060e | ||
|
|
4498187721 | ||
|
|
8666fe45b1 | ||
|
|
cd12447c2e | ||
|
|
0a79cc882a | ||
|
|
7f80b5d660 | ||
|
|
92f58e9465 | ||
|
|
387f7f1545 | ||
|
|
65e0845376 | ||
|
|
97bb695373 | ||
|
|
d8230afae3 | ||
|
|
07afb955f7 | ||
|
|
a57d668fc9 | ||
|
|
765dbcc111 | ||
|
|
ccb4f88b89 | ||
|
|
eee9e1a488 | ||
|
|
f832f663c9 | ||
|
|
032f56ad67 | ||
|
|
3f2ac528c1 | ||
|
|
d174757ef0 | ||
|
|
f457a5e831 | ||
|
|
ab2fe58d2f | ||
|
|
fe1c384aeb | ||
|
|
4c327e9874 | ||
|
|
928b951c25 | ||
|
|
ecba2a51d8 | ||
|
|
9668f62c6a | ||
|
|
dc3ba3d8f0 | ||
|
|
3f6f970d36 | ||
|
|
768356d8e2 | ||
|
|
65110090de | ||
|
|
f5cab63052 | ||
|
|
399d8adb3b | ||
|
|
b40055686b | ||
|
|
2dcecb2a46 | ||
|
|
0cc118c849 | ||
|
|
b1148ebc83 | ||
|
|
802f64e309 | ||
|
|
80749fec09 | ||
|
|
1f1ea8f3ed | ||
|
|
796cbcaf4b | ||
|
|
4cf5242aa5 | ||
|
|
8921f10ffd | ||
|
|
b60c129acf | ||
|
|
852413b36a | ||
|
|
a39b367477 | ||
|
|
8be274dc4d | ||
|
|
9ac72296c7 | ||
|
|
1405f5954a | ||
|
|
f406de6b0c | ||
|
|
0df57c82cb | ||
|
|
4853bcd724 | ||
|
|
37e95d4ce6 | ||
|
|
23acd186f7 | ||
|
|
5e98bd0b53 | ||
|
|
d7238312b1 | ||
|
|
ec40da4353 | ||
|
|
204ad8913f | ||
|
|
c0f5023b63 | ||
|
|
b3c105bfa7 | ||
|
|
68acbe5c7d | ||
|
|
12245d960c | ||
|
|
f82c2517fb | ||
|
|
fa49da68a4 | ||
|
|
cffbfdf6f2 | ||
|
|
cd126279ac | ||
|
|
bedd6f9a6e | ||
|
|
10e0c8d876 | ||
|
|
dc2ad48a7f | ||
|
|
c010dd9401 | ||
|
|
270ef76057 | ||
|
|
9d47f27293 | ||
|
|
f0687a082a | ||
|
|
edebde2bf4 | ||
|
|
71ce74c633 | ||
|
|
2dd5239b9d | ||
|
|
f0145eb8e6 | ||
|
|
556ed8fe16 | ||
|
|
ed753fd354 | ||
|
|
4ecc5e4367 | ||
|
|
b4ae480d93 | ||
|
|
9a563e0cdd | ||
|
|
c5d6ee6782 | ||
|
|
f7fdf7745e | ||
|
|
a48b60a24a | ||
|
|
520f06020c | ||
|
|
f96b60c0d0 | ||
|
|
00f03f6587 | ||
|
|
c68bd699f1 | ||
|
|
e5f78cdc1e | ||
|
|
00407539d3 | ||
|
|
62014f4d01 | ||
|
|
e7dddda7e5 | ||
|
|
208ff6f3a5 | ||
|
|
4d31465191 | ||
|
|
cf987427e0 | ||
|
|
b11b959fd3 | ||
|
|
61f660ca1d | ||
|
|
6d44521016 | ||
|
|
7289f5ba8a | ||
|
|
b657c7f347 | ||
|
|
40865425c5 | ||
|
|
f7249f5e84 | ||
|
|
cc04fb05c2 | ||
|
|
58eb1d9e24 | ||
|
|
76b7e6fecf | ||
|
|
980940a7cf | ||
|
|
9a021daae8 | ||
|
|
cc9904a454 | ||
|
|
b0faab9395 | ||
|
|
f1198b47fd | ||
|
|
37f2ab555f | ||
|
|
61ca2a391b | ||
|
|
88537b9323 | ||
|
|
e6c004e8f6 | ||
|
|
dc7290dab7 | ||
|
|
9dff8bd64a | ||
|
|
35c2f60129 | ||
|
|
471a2372c8 | ||
|
|
fba028db03 | ||
|
|
c647c52638 | ||
|
|
cad5edcf86 | ||
|
|
38f70bb6be | ||
|
|
33bdc81b3e | ||
|
|
21fd7f5eed | ||
|
|
6354e91b55 | ||
|
|
8123c06348 | ||
|
|
663c648337 | ||
|
|
bee4e94987 | ||
|
|
c44bdc8762 | ||
|
|
423ecf71d8 | ||
|
|
73c7882cc0 | ||
|
|
552b9ef21a | ||
|
|
683af1ec3a | ||
|
|
77ed15311c | ||
|
|
3d72557618 | ||
|
|
e2a11d42f8 | ||
|
|
0f5ea6ae66 | ||
|
|
5b2c9b85f9 | ||
|
|
94921230d9 | ||
|
|
34788356e6 | ||
|
|
2f719d7f2c | ||
|
|
eb2e8d75f4 | ||
|
|
338d288290 | ||
|
|
03fe1a2d2c | ||
|
|
ade48c7bea | ||
|
|
4a8d89e2bb | ||
|
|
0fed1681ed | ||
|
|
93ad483066 | ||
|
|
d995e73996 | ||
|
|
1d271bab18 | ||
|
|
a9d2aa9366 | ||
|
|
2a48d43e5b | ||
|
|
0a4e23118a | ||
|
|
528a15962f | ||
|
|
0a0cc4e79c | ||
|
|
2b3ba42d70 | ||
|
|
9465331ece | ||
|
|
b0d86bef3e | ||
|
|
a28a2360f1 | ||
|
|
f7a957150e | ||
|
|
67bd065bc3 | ||
|
|
5a3a12767c | ||
|
|
2f86bc312c | ||
|
|
f0fcadfaf4 | ||
|
|
a9d88c849a | ||
|
|
7397efca80 | ||
|
|
17bd6f9a33 | ||
|
|
2d4e7a9fb0 | ||
|
|
0266da993d | ||
|
|
ea5280713f | ||
|
|
0b89d29c7d | ||
|
|
688bac77a8 | ||
|
|
c736bf7c06 | ||
|
|
539730f8ec | ||
|
|
a9c4669c75 | ||
|
|
4c11f93ee2 | ||
|
|
f8dba6fd7f | ||
|
|
419247074f | ||
|
|
2ddb7b5b64 | ||
|
|
c14b59661e | ||
|
|
d70d27e665 | ||
|
|
8991762b0c | ||
|
|
2fc6741c99 | ||
|
|
1075af73f2 | ||
|
|
8671229f76 | ||
|
|
0fb67583ff | ||
|
|
2fc0fd17a2 | ||
|
|
f185860213 | ||
|
|
8ffcdbfc21 | ||
|
|
6a38e2cca8 | ||
|
|
10d9d78ca8 | ||
|
|
03a68038e7 | ||
|
|
fabefcdf4b | ||
|
|
9e4d8ecddf | ||
|
|
1dffbfd8dc | ||
|
|
950db5a87a | ||
|
|
21348d5557 | ||
|
|
eb1a089437 | ||
|
|
17fc81ab7a | ||
|
|
1c54fd1101 | ||
|
|
263bce38cd | ||
|
|
a923c1151c | ||
|
|
0bf10a827f | ||
|
|
49850e4198 | ||
|
|
95b437b311 | ||
|
|
7006f765a6 | ||
|
|
ec3360400c | ||
|
|
5c41d09c52 | ||
|
|
a7590956fd | ||
|
|
a581960121 | ||
|
|
dc57a0b925 | ||
|
|
d6082162ab | ||
|
|
1801afdbb7 | ||
|
|
458add0c9c | ||
|
|
f2374eb141 | ||
|
|
5db5897793 | ||
|
|
f3d628afa7 | ||
|
|
4d3482e40e | ||
|
|
a8cff454ec | ||
|
|
f66cae4749 | ||
|
|
aded1daf92 | ||
|
|
aa1ba0d950 | ||
|
|
071010e438 | ||
|
|
f6d8e364d6 | ||
|
|
f1453ed4c4 | ||
|
|
3a2146cb03 | ||
|
|
24eb76de20 | ||
|
|
693478e0a5 | ||
|
|
bf6be5c5a7 | ||
|
|
a12a639cd3 | ||
|
|
ef37428714 | ||
|
|
644afe8995 | ||
|
|
c66c428124 | ||
|
|
db5b2ea9b6 | ||
|
|
d84603bce2 | ||
|
|
b128370299 | ||
|
|
240e619248 | ||
|
|
c89bde08db | ||
|
|
3ecd1c62b8 | ||
|
|
e3c5497283 | ||
|
|
4bd8ee8ccf | ||
|
|
43b437af92 | ||
|
|
56e0d62597 | ||
|
|
d10e5f025d | ||
|
|
b1a80691db | ||
|
|
049aa61e85 | ||
|
|
7026361234 | ||
|
|
5e30dc5bf4 | ||
|
|
024bfc8ec8 | ||
|
|
04e5e8e4d0 | ||
|
|
7c5d47733f | ||
|
|
b24f2a1818 | ||
|
|
ee6664ce9d | ||
|
|
ab434946b5 | ||
|
|
35e431eb99 | ||
|
|
aa8cddf509 | ||
|
|
c9ede0bfc1 | ||
|
|
6ec9a0f2b2 | ||
|
|
2f86112801 | ||
|
|
c032befe6f | ||
|
|
55eccde031 | ||
|
|
5716820439 | ||
|
|
17d433dd9b | ||
|
|
000812bf6d | ||
|
|
5e2187a877 | ||
|
|
e10b6334f5 | ||
|
|
baa0341727 | ||
|
|
814b2b2582 | ||
|
|
56705bde74 | ||
|
|
dceb38b777 | ||
|
|
9947a6aa1b | ||
|
|
7a3be374c8 | ||
|
|
4ea3ce0e3c | ||
|
|
923185b3f4 | ||
|
|
d91e6c6c1a | ||
|
|
1c93a79448 | ||
|
|
e12ad0cd79 | ||
|
|
8d6bd29b93 | ||
|
|
f941a73999 | ||
|
|
c3057141d8 | ||
|
|
49080cb64c | ||
|
|
27dbe23914 | ||
|
|
d7a2de5817 | ||
|
|
0328aa0630 | ||
|
|
b6cf302131 | ||
|
|
e2a894acd3 | ||
|
|
00ed6d9bb8 | ||
|
|
c9a9734368 | ||
|
|
efc56a8724 | ||
|
|
6e6923b108 | ||
|
|
f459beccdb | ||
|
|
751c5a3245 | ||
|
|
8488499da6 | ||
|
|
96a7e3c425 | ||
|
|
0dcf510466 | ||
|
|
0427b12d52 | ||
|
|
9256c66fcc | ||
|
|
706f4e1c4c | ||
|
|
96debcd616 | ||
|
|
07f20e1e0d | ||
|
|
fee2e503bd | ||
|
|
f9f260bbc1 | ||
|
|
61718192ee | ||
|
|
27893f9cdd | ||
|
|
9b0b80ef04 | ||
|
|
3e1c2df4b1 | ||
|
|
fa745410cc | ||
|
|
a427624e8d | ||
|
|
3798ca1e17 | ||
|
|
113120b3ab | ||
|
|
b10ca5b77f | ||
|
|
f10e3d756a | ||
|
|
9608b974ec | ||
|
|
3b6cc9c633 | ||
|
|
5305dd62d1 | ||
|
|
a066190c60 | ||
|
|
cdae8b35f5 | ||
|
|
6ee57315dd | ||
|
|
64f682146d | ||
|
|
36525fbe9d | ||
|
|
8f628f2d45 | ||
|
|
5e84e5b8b6 | ||
|
|
a64878bd00 | ||
|
|
c53fc5eaa3 | ||
|
|
212751c835 | ||
|
|
fe1c6acebb | ||
|
|
ce47bfe018 | ||
|
|
0ee4ade404 | ||
|
|
e99df2b69e | ||
|
|
db84d07c38 | ||
|
|
db610cfb4c | ||
|
|
5b52417d20 | ||
|
|
8a768cf933 | ||
|
|
bebf3bbc39 | ||
|
|
8a3dd5472b | ||
|
|
f971533a5b | ||
|
|
a12166c13b | ||
|
|
51624a31e3 | ||
|
|
cdc632e1af | ||
|
|
31f87f647e | ||
|
|
dcd37f71d1 | ||
|
|
4ca286b28e | ||
|
|
4f3e4b019a | ||
|
|
95ef061a34 | ||
|
|
aaaf8aa66f | ||
|
|
29965e38d0 | ||
|
|
371d49a213 | ||
|
|
6ed95e145e | ||
|
|
8c025c1173 | ||
|
|
9ce541cc31 | ||
|
|
aa57a4c123 | ||
|
|
58d9deb3b8 | ||
|
|
f0685c4a43 | ||
|
|
484817db08 | ||
|
|
670bf15d31 | ||
|
|
6df1e0fd77 | ||
|
|
ec910cb80f | ||
|
|
1d44305e34 | ||
|
|
a37af592cd | ||
|
|
7f486eef4c | ||
|
|
bcc0442add | ||
|
|
700f6e05bf | ||
|
|
d8327d6de2 | ||
|
|
5a55b3d7e3 | ||
|
|
bed87ed439 | ||
|
|
6d1f1c7852 | ||
|
|
f6b3bde724 | ||
|
|
94ec22bef8 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,6 +18,7 @@ local.properties
|
||||
|
||||
# Android Studio
|
||||
.idea/*
|
||||
!.idea/inspectionProfiles/
|
||||
!.idea/runConfigurations/
|
||||
!.idea/codeStyleSettings.xml
|
||||
!.idea/codeStyles
|
||||
|
||||
@@ -5,6 +5,15 @@ stages:
|
||||
- optional_tests
|
||||
- check_reproducibility
|
||||
|
||||
workflow:
|
||||
# when to create a CI pipeline
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
|
||||
when: never # avoids duplicate jobs for branch and MR
|
||||
- if: '$CI_COMMIT_BRANCH'
|
||||
- if: '$CI_COMMIT_TAG'
|
||||
|
||||
.base-test:
|
||||
before_script:
|
||||
- set -e
|
||||
@@ -24,7 +33,11 @@ test:
|
||||
stage: test
|
||||
script:
|
||||
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
|
||||
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom check
|
||||
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources check
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
when: always
|
||||
- when: always
|
||||
|
||||
android test:
|
||||
extends: .base-test
|
||||
@@ -34,17 +47,31 @@ android test:
|
||||
# start emulator first, so it can fail early
|
||||
- start-emulator.sh
|
||||
# run normal and screenshot tests together (exclude Large tests)
|
||||
- ./gradlew -Djava.security.egd=file:/dev/urandom connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.notAnnotation=androidx.test.filters.LargeTest
|
||||
- ./gradlew -Djava.security.egd=file:/dev/urandom connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.package=org.briarproject.briar.android -Pandroid.testInstrumentationRunnerArguments.notAnnotation=androidx.test.filters.LargeTest
|
||||
after_script:
|
||||
- adb pull /sdcard/Pictures/screenshots
|
||||
artifacts:
|
||||
name: "${CI_PROJECT_PATH}_${CI_JOB_STAGE}_${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHA}"
|
||||
paths:
|
||||
- kernel.log
|
||||
- logcat.txt
|
||||
- briar-android/build/reports/androidTests/connected/flavors/*
|
||||
- screenshots
|
||||
expire_in: 3 days
|
||||
when: on_failure
|
||||
when: manual
|
||||
except:
|
||||
- tags
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
when: on_success
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
changes:
|
||||
- briar-android/**/*
|
||||
when: manual
|
||||
allow_failure: true
|
||||
- if: '$CI_COMMIT_TAG == null'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
retry:
|
||||
max: 1
|
||||
tags:
|
||||
- kvm
|
||||
|
||||
@@ -75,11 +102,15 @@ test_reproducible:
|
||||
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
|
||||
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
|
||||
|
||||
manual_tests:
|
||||
bridge test:
|
||||
extends: .optional_tests
|
||||
when: manual
|
||||
except:
|
||||
- tags
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
when: on_success
|
||||
allow_failure: true
|
||||
- if: '$CI_COMMIT_TAG == null'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
|
||||
pre_release_tests:
|
||||
extends: .optional_tests
|
||||
|
||||
15
.idea/codeStyles/Project.xml
generated
15
.idea/codeStyles/Project.xml
generated
@@ -31,15 +31,6 @@
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value />
|
||||
</option>
|
||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||
<value>
|
||||
<package name="" alias="false" withSubpackages="true" />
|
||||
<package name="java" alias="false" withSubpackages="true" />
|
||||
<package name="javax" alias="false" withSubpackages="true" />
|
||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||
<package name="" alias="true" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
@@ -197,9 +188,9 @@
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
|
||||
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
|
||||
<option name="ENUM_CONSTANTS_WRAP" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
1
.idea/dictionaries/briar.xml
generated
1
.idea/dictionaries/briar.xml
generated
@@ -7,6 +7,7 @@
|
||||
<w>encrypter</w>
|
||||
<w>identicon</w>
|
||||
<w>introducee</w>
|
||||
<w>introducees</w>
|
||||
<w>introducer</w>
|
||||
<w>onboarding</w>
|
||||
</words>
|
||||
|
||||
14
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
14
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="MissingOverrideAnnotation" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreObjectMethods" value="true" />
|
||||
<option name="ignoreAnonymousClassMethods" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="WeakerAccess" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="true" />
|
||||
<option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="true" />
|
||||
<option name="SUGGEST_PRIVATE_FOR_INNERS" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
43
.idea/runConfigurations/All_tests.xml
generated
43
.idea/runConfigurations/All_tests.xml
generated
@@ -1,21 +1,32 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.briar-android" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/briar-android" />
|
||||
<configuration default="false" name="All tests" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":briar-android:testOfficialDebugUnitTest" />
|
||||
<option value=":briar-android:testScreenshotDebugUnitTest" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="AndroidJUnit" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="AndroidJUnit" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-android" run_configuration_type="AndroidJUnit" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-java" run_configuration_type="AndroidJUnit" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-api" run_configuration_type="AndroidJUnit" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-headless" run_configuration_type="AndroidJUnit" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="GradleRunConfiguration" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="GradleRunConfiguration" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-java" run_configuration_type="GradleRunConfiguration" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-android" run_configuration_type="GradleRunConfiguration" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-api" run_configuration_type="GradleRunConfiguration" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="GradleRunConfiguration" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-headless" run_configuration_type="GradleRunConfiguration" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,14 +1,23 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in bramble-android" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.bramble-android" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-android" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
<configuration default="false" name="All tests in bramble-android" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":bramble-android:testDebugUnitTest" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
33
.idea/runConfigurations/All_tests_in_bramble_api.xml
generated
33
.idea/runConfigurations/All_tests_in_bramble_api.xml
generated
@@ -1,14 +1,25 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.bramble-api" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
<configuration default="false" name="All tests in bramble-api" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":bramble-api:animalSnifferMain" />
|
||||
<option value=":bramble-api:animalSnifferTest" />
|
||||
<option value=":bramble-api:test" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,14 +1,25 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in bramble-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.bramble-core" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
<configuration default="false" name="All tests in bramble-core" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":bramble-core:animalSnifferMain" />
|
||||
<option value=":bramble-core:animalSnifferTest" />
|
||||
<option value=":bramble-core:test" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,15 +1,23 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in bramble-java" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.bramble-java" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-java" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
<configuration default="false" name="All tests in bramble-java" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":bramble-java:test" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,14 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.briar-android" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
<configuration default="false" name="All tests in briar-android" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":briar-android:testOfficialDebugUnitTest" />
|
||||
<option value=":briar-android:testScreenshotDebugUnitTest" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
33
.idea/runConfigurations/All_tests_in_briar_api.xml
generated
33
.idea/runConfigurations/All_tests_in_briar_api.xml
generated
@@ -1,14 +1,25 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in briar-api" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.briar-api" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-api" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
<configuration default="false" name="All tests in briar-api" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":briar-api:animalSnifferMain" />
|
||||
<option value=":briar-api:animalSnifferTest" />
|
||||
<option value=":briar-api:test" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
33
.idea/runConfigurations/All_tests_in_briar_core.xml
generated
33
.idea/runConfigurations/All_tests_in_briar_core.xml
generated
@@ -1,14 +1,25 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.briar-core" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
<configuration default="false" name="All tests in briar-core" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":briar-core:animalSnifferMain" />
|
||||
<option value=":briar-core:animalSnifferTest" />
|
||||
<option value=":briar-core:test" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,15 +1,23 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in briar-headless" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.briar-headless" />
|
||||
<option name="PACKAGE_NAME" value="org.briarproject.briar.headless" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="VM_PARAMETERS" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/briar-headless" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
<configuration default="false" name="All tests in briar-headless" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":briar-headless:test" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
46
.idea/runConfigurations/BridgeTest.xml
generated
46
.idea/runConfigurations/BridgeTest.xml
generated
@@ -1,24 +1,28 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="BridgeTest" type="AndroidJUnit" factoryName="Android JUnit" nameIsGenerated="true">
|
||||
<module name="briar.bramble-java" />
|
||||
<useClassPathOnly />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.briarproject.bramble.plugin.tor.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="org.briarproject.bramble.plugin.tor" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.plugin.tor.BridgeTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
|
||||
<envs>
|
||||
<env name="OPTIONAL_TESTS" value="org.briarproject.bramble.plugin.tor.BridgeTest" />
|
||||
</envs>
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
<configuration default="false" name="BridgeTest" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="env">
|
||||
<map>
|
||||
<entry key="OPTIONAL_TESTS" value="org.briarproject.bramble.plugin.tor.BridgeTest" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="--tests "org.briarproject.bramble.plugin.tor.BridgeTest"" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":bramble-java:test" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
14
.idea/runConfigurations/H2_Performance_Test.xml
generated
14
.idea/runConfigurations/H2_Performance_Test.xml
generated
@@ -1,14 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="H2 Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.bramble-core" />
|
||||
<option name="PACKAGE_NAME" value="org.briarproject.bramble.db" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.db.H2DatabasePerformanceTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,14 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="HyperSQL Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<module name="briar.bramble-core" />
|
||||
<option name="PACKAGE_NAME" value="org.briarproject.bramble.db" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.db.HyperSqlDatabasePerformanceTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
29
README.md
29
README.md
@@ -1 +1,28 @@
|
||||
Briar is a messaging app designed for activists, journalists, and anyone else who needs a safe, easy and robust way to communicate. Unlike traditional messaging tools such as email, Twitter or Telegram, Briar doesn't rely on a central server - messages are synchronized directly between the users' devices. If the Internet's down, Briar can sync via Bluetooth or Wi-Fi, keeping the information flowing in a crisis. If the Internet's up, Briar can sync via the Tor network, protecting users and their relationships from surveillance.
|
||||
# Briar
|
||||
Briar is a messaging app designed for activists, journalists, and anyone else who needs a safe, easy and robust way to communicate.
|
||||
|
||||
Unlike traditional messaging tools such as email, Twitter or Telegram, Briar doesn't rely on a central server - messages are synchronized directly between the users' devices.
|
||||
|
||||
If the Internet's down, Briar can sync via Bluetooth or Wi-Fi, keeping information flowing in a crisis. If the Internet's up, Briar can sync via the Tor network, protecting users and their relationships from surveillance.
|
||||
|
||||
## Download Briar
|
||||
|
||||
[<img src="https://briarproject.org//img/fdroid_badge.png" width="240">](https://briarproject.org/fdroid)
|
||||
[<img src="https://briarproject.org/img/google_play_badge_web_generic.png" width="240">](https://play.google.com/store/apps/details?id=org.briarproject.briar.android)
|
||||
|
||||
You can also [download the APK file](https://briarproject.org/apk) directly from
|
||||
our site.
|
||||
|
||||
## Useful links
|
||||
[briarproject.org](https://briarproject.org/)
|
||||
|
||||
[Source code](https://code.briarproject.org/briar/briar/tree/master)
|
||||
|
||||
[Manual](https://briarproject.org/manual/)
|
||||
|
||||
[Wiki](https://code.briarproject.org/briar/briar/-/wikis/home)
|
||||
|
||||
## Donate
|
||||
|
||||
[](https://liberapay.com/Briar/donate) [](https://flattr.com/t/592836/)
|
||||
Bitcoin and BCH: 1NZCKkUCtJV2U2Y9hDb9uq8S7ksFCFGR6K
|
||||
|
||||
@@ -6,7 +6,7 @@ apply from: 'witness.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.2'
|
||||
buildToolsVersion '30.0.3'
|
||||
|
||||
packagingOptions {
|
||||
doNotStrip '**/*.so'
|
||||
@@ -14,9 +14,9 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
versionCode 10220
|
||||
versionName "1.2.20"
|
||||
targetSdkVersion 30
|
||||
versionCode 10401
|
||||
versionName "1.4.1"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
@@ -42,18 +42,18 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
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'
|
||||
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||
|
||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-legacy:$jmock_version"
|
||||
}
|
||||
|
||||
def torBinariesDir = 'src/main/res/raw'
|
||||
|
||||
@@ -59,8 +59,8 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
class AndroidBluetoothPlugin
|
||||
extends BluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
|
||||
class AndroidBluetoothPlugin extends
|
||||
AbstractBluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(AndroidBluetoothPlugin.class.getName());
|
||||
@@ -75,6 +75,7 @@ class AndroidBluetoothPlugin
|
||||
|
||||
// Non-null if the plugin started successfully
|
||||
private volatile BluetoothAdapter adapter = null;
|
||||
private volatile boolean stopDiscoverAndConnect;
|
||||
|
||||
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
||||
BluetoothConnectionFactory<BluetoothSocket> connectionFactory,
|
||||
@@ -86,7 +87,7 @@ class AndroidBluetoothPlugin
|
||||
Clock clock,
|
||||
Backoff backoff,
|
||||
PluginCallback callback,
|
||||
int maxLatency,
|
||||
long maxLatency,
|
||||
int maxIdleTime) {
|
||||
super(connectionLimiter, connectionFactory, ioExecutor,
|
||||
wakefulIoExecutor, secureRandom, backoff, callback,
|
||||
@@ -187,22 +188,40 @@ class AndroidBluetoothPlugin
|
||||
@Nullable
|
||||
DuplexTransportConnection discoverAndConnect(String uuid) {
|
||||
if (adapter == null) return null;
|
||||
for (String address : discoverDevices()) {
|
||||
try {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connecting to " + scrubMacAddress(address));
|
||||
return connectTo(address, uuid);
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Could not connect to "
|
||||
+ scrubMacAddress(address));
|
||||
if (!discoverSemaphore.tryAcquire()) {
|
||||
LOG.info("Discover already running");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
stopDiscoverAndConnect = false;
|
||||
for (String address : discoverDevices()) {
|
||||
if (stopDiscoverAndConnect) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connecting to " + scrubMacAddress(address));
|
||||
return connectTo(address, uuid);
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Could not connect to "
|
||||
+ scrubMacAddress(address));
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
discoverSemaphore.release();
|
||||
}
|
||||
LOG.info("Could not connect to any devices");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopDiscoverAndConnect() {
|
||||
stopDiscoverAndConnect = true;
|
||||
adapter.cancelDiscovery();
|
||||
}
|
||||
|
||||
private Collection<String> discoverDevices() {
|
||||
List<String> addresses = new ArrayList<>();
|
||||
BlockingQueue<Intent> intents = new LinkedBlockingQueue<>();
|
||||
|
||||
@@ -47,7 +47,7 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
||||
private final BackoffFactory backoffFactory;
|
||||
|
||||
@Inject
|
||||
public AndroidBluetoothPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
AndroidBluetoothPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||
AndroidExecutor androidExecutor,
|
||||
AndroidWakeLockManager wakeLockManager,
|
||||
@@ -75,7 +75,7 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLatency() {
|
||||
public long getMaxLatency() {
|
||||
return MAX_LATENCY;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.briarproject.bramble.plugin.file;
|
||||
|
||||
import android.app.Application;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.PROP_URI;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class AndroidRemovableDrivePlugin extends RemovableDrivePlugin {
|
||||
|
||||
private final Application app;
|
||||
|
||||
AndroidRemovableDrivePlugin(Application app, PluginCallback callback,
|
||||
long maxLatency) {
|
||||
super(callback, maxLatency);
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
@Override
|
||||
InputStream openInputStream(TransportProperties p) throws IOException {
|
||||
String uri = p.get(PROP_URI);
|
||||
if (isNullOrEmpty(uri)) throw new IllegalArgumentException();
|
||||
return app.getContentResolver().openInputStream(Uri.parse(uri));
|
||||
}
|
||||
|
||||
@Override
|
||||
OutputStream openOutputStream(TransportProperties p) throws IOException {
|
||||
String uri = p.get(PROP_URI);
|
||||
if (isNullOrEmpty(uri)) throw new IllegalArgumentException();
|
||||
return app.getContentResolver().openOutputStream(Uri.parse(uri));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.briarproject.bramble.plugin.file;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.ID;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class AndroidRemovableDrivePluginFactory implements
|
||||
SimplexPluginFactory {
|
||||
|
||||
private static final long MAX_LATENCY = DAYS.toMillis(28);
|
||||
|
||||
private final Application app;
|
||||
|
||||
@Inject
|
||||
AndroidRemovableDrivePluginFactory(Application app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportId getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxLatency() {
|
||||
return MAX_LATENCY;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SimplexPlugin createPlugin(PluginCallback callback) {
|
||||
return new AndroidRemovableDrivePlugin(app, callback, MAX_LATENCY);
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import java.net.UnknownHostException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.net.SocketFactory;
|
||||
@@ -48,6 +49,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
|
||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.NetworkUtils.getNetworkInterfaces;
|
||||
|
||||
@NotNullByDefault
|
||||
class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
@@ -55,6 +57,13 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
private static final Logger LOG =
|
||||
getLogger(AndroidLanTcpPlugin.class.getName());
|
||||
|
||||
/**
|
||||
* The interface name is used as a heuristic for deciding whether the
|
||||
* device is providing a wifi access point.
|
||||
*/
|
||||
private static final Pattern AP_INTERFACE_NAME =
|
||||
Pattern.compile("^(wlan|ap|p2p)[-0-9]");
|
||||
|
||||
private final Executor connectionStatusExecutor;
|
||||
private final ConnectivityManager connectivityManager;
|
||||
@Nullable
|
||||
@@ -67,7 +76,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
Application app,
|
||||
Backoff backoff,
|
||||
PluginCallback callback,
|
||||
int maxLatency,
|
||||
long maxLatency,
|
||||
int maxIdleTime,
|
||||
int connectionTimeout) {
|
||||
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,
|
||||
@@ -130,17 +139,14 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
if (info != null && info.getIpAddress() != 0) {
|
||||
return new Pair<>(intToInetAddress(info.getIpAddress()), false);
|
||||
}
|
||||
List<InterfaceAddress> ifAddrs = getLocalInterfaceAddresses();
|
||||
// If we're providing a normal access point, return its address
|
||||
for (InterfaceAddress ifAddr : ifAddrs) {
|
||||
if (isAndroidWifiApAddress(ifAddr)) {
|
||||
return new Pair<>(ifAddr.getAddress(), true);
|
||||
}
|
||||
}
|
||||
// If we're providing a wifi direct access point, return its address
|
||||
for (InterfaceAddress ifAddr : ifAddrs) {
|
||||
if (isAndroidWifiDirectApAddress(ifAddr)) {
|
||||
return new Pair<>(ifAddr.getAddress(), true);
|
||||
// If we're providing an access point, return its address
|
||||
for (NetworkInterface iface : getNetworkInterfaces()) {
|
||||
if (AP_INTERFACE_NAME.matcher(iface.getName()).find()) {
|
||||
for (InterfaceAddress ifAddr : iface.getInterfaceAddresses()) {
|
||||
if (isPossibleWifiApInterface(ifAddr)) {
|
||||
return new Pair<>(ifAddr.getAddress(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Not connected to wifi
|
||||
@@ -148,33 +154,18 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given address belongs to a network provided by an
|
||||
* Android access point (including the access point's own address).
|
||||
* Returns true if the given address may belong to an interface providing
|
||||
* a wifi access point (including wifi direct legacy mode access points).
|
||||
* <p>
|
||||
* The access point's address is usually 192.168.43.1, but at least one
|
||||
* device (Honor 8A) may use other addresses in the range 192.168.43.0/24.
|
||||
* This method may return true for wifi client interfaces as well, but
|
||||
* we've already checked for a wifi client connection above.
|
||||
*/
|
||||
private boolean isAndroidWifiApAddress(InterfaceAddress ifAddr) {
|
||||
private boolean isPossibleWifiApInterface(InterfaceAddress ifAddr) {
|
||||
if (ifAddr.getNetworkPrefixLength() != 24) return false;
|
||||
byte[] ip = ifAddr.getAddress().getAddress();
|
||||
return ip.length == 4
|
||||
&& ip[0] == (byte) 192
|
||||
&& ip[1] == (byte) 168
|
||||
&& ip[2] == (byte) 43;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given address belongs to a network provided by an
|
||||
* Android wifi direct legacy mode access point (including the access
|
||||
* point's own address).
|
||||
*/
|
||||
private boolean isAndroidWifiDirectApAddress(InterfaceAddress ifAddr) {
|
||||
if (ifAddr.getNetworkPrefixLength() != 24) return false;
|
||||
byte[] ip = ifAddr.getAddress().getAddress();
|
||||
return ip.length == 4
|
||||
&& ip[0] == (byte) 192
|
||||
&& ip[1] == (byte) 168
|
||||
&& ip[2] == (byte) 49;
|
||||
&& ip[1] == (byte) 168;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,7 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
|
||||
private final Application app;
|
||||
|
||||
@Inject
|
||||
public AndroidLanTcpPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
AndroidLanTcpPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||
EventBus eventBus,
|
||||
BackoffFactory backoffFactory,
|
||||
@@ -55,7 +55,7 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLatency() {
|
||||
public long getMaxLatency() {
|
||||
return MAX_LATENCY;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class AndroidTorPlugin extends TorPlugin {
|
||||
TorRendezvousCrypto torRendezvousCrypto,
|
||||
PluginCallback callback,
|
||||
String architecture,
|
||||
int maxLatency,
|
||||
long maxLatency,
|
||||
int maxIdleTime,
|
||||
File torDirectory) {
|
||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||
|
||||
@@ -58,7 +58,7 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
|
||||
private final File torDirectory;
|
||||
|
||||
@Inject
|
||||
public AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||
Application app,
|
||||
NetworkManager networkManager,
|
||||
@@ -94,7 +94,7 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLatency() {
|
||||
public long getMaxLatency() {
|
||||
return MAX_LATENCY;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,17 +10,20 @@ import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.lang.Runtime.getRuntime;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
|
||||
@@ -118,4 +121,17 @@ public class AndroidUtils {
|
||||
public static String[] getSupportedImageContentTypes() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,43 @@
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
||||
'com.android.tools.analytics-library:protos:27.1.1:protos-27.1.1.jar:13f77e73762e58ab372d140b3a6be6903aea9775b62dd14fbc62d4cc7069c9a4',
|
||||
'com.android.tools.analytics-library:shared:27.1.1:shared-27.1.1.jar:82930a52001410e97d809930b670f4de3002286975f046b9de5f6b777b06d366',
|
||||
'com.android.tools.analytics-library:tracker:27.1.1:tracker-27.1.1.jar:31bc5a00be0055bac89c9b2f34751883e987cd89e3ac1783720645c164f591d9',
|
||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||
'com.android.tools.analytics-library:protos:27.1.3:protos-27.1.3.jar:0d9e6cff60b318baac250b6f5bb076a8161103338bf2749cdf1db8a5a13a1f12',
|
||||
'com.android.tools.analytics-library:shared:27.1.3:shared-27.1.3.jar:10d2a51d8f89ff4ac849888e5a9c60b10e879c30d78545ec1da4d3df7bd56ae4',
|
||||
'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:apksig:4.1.1:apksig-4.1.1.jar:e0a69da9e5a03986d608b45bbf954ef0e6a0b3f58c1b8315bd169ec08b279e72',
|
||||
'com.android.tools.build:apkzlib:4.1.1:apkzlib-4.1.1.jar:ba4b5e419b6be0130eae7f8301c3a551ad3976f487d2e0c6852ebb175ac41127',
|
||||
'com.android.tools.build:builder-model:4.1.1:builder-model-4.1.1.jar:e95c99cc298ad67b8deb6ced99c51abc8f59afebedad044b1a10dde14646a4dd',
|
||||
'com.android.tools.build:builder-test-api:4.1.1:builder-test-api-4.1.1.jar:464f596ab261c051c3847406748e843770dea123f6fa5fee8a9390644e709b7a',
|
||||
'com.android.tools.build:builder:4.1.1:builder-4.1.1.jar:0f78d4759d2f7b57b95865522ec34596ba419b9982f3b25e3449213f9c98b80d',
|
||||
'com.android.tools.build:gradle-api:4.1.1:gradle-api-4.1.1.jar:d42e6b539e4c1353ad3546e75ec8ce11a017b97481023e8ea18577eefe374358',
|
||||
'com.android.tools.build:manifest-merger:27.1.1:manifest-merger-27.1.1.jar:7a45fa143687859bb2e5a961dcf6ee88094d3853de0cb543dc03dbcb0f4b554b',
|
||||
'com.android.tools.ddms:ddmlib:27.1.1:ddmlib-27.1.1.jar:da6e4bd834b6a85dae8019039849d8bd96933347dfbf460df74913ddade6e40a',
|
||||
'com.android.tools.external.com-intellij:intellij-core:27.1.1:intellij-core-27.1.1.jar:2591a7363c4443c59bf9f793730acafce9d6ec3076e2f46716edaf53a41b6fb6',
|
||||
'com.android.tools.external.com-intellij:kotlin-compiler:27.1.1:kotlin-compiler-27.1.1.jar:5054ae770ba788f110303c65abd6b1fa28eccf52dee1274510e201b2b81885c8',
|
||||
'com.android.tools.external.org-jetbrains:uast:27.1.1:uast-27.1.1.jar:54cd8f6886a9d2f5641659dd5c91f626629672cd48301f7f0bd6aad9bd448714',
|
||||
'com.android.tools.layoutlib:layoutlib-api:27.1.1:layoutlib-api-27.1.1.jar:8a9a22e3b309521ea83b724e5a89cfdac6076f52d675c0e17d77b05527bc0f8c',
|
||||
'com.android.tools.lint:lint-api:27.1.1:lint-api-27.1.1.jar:c1d8176094cb0478786070d40533efb578ebc53529a82f6ef5bee879bdca418b',
|
||||
'com.android.tools.lint:lint-checks:27.1.1:lint-checks-27.1.1.jar:3899c91e00bd059b40c31a9ca00cd0f8303191947608735ae1b657323693fb61',
|
||||
'com.android.tools.lint:lint-gradle-api:27.1.1:lint-gradle-api-27.1.1.jar:26aa89d38b9825cc73229daa82a68875801c8b8491f30497ce62aff1f206eb0d',
|
||||
'com.android.tools.lint:lint-gradle:27.1.1:lint-gradle-27.1.1.jar:f7355823ead869f4d28184ba28b7a0c693b507519a2d3705bb9848a0f35b3756',
|
||||
'com.android.tools.lint:lint-model:27.1.1:lint-model-27.1.1.jar:bc23c0c413bdfca59dac2cd56b870d8360d009e9ec0d365e71f774bcf127971d',
|
||||
'com.android.tools.lint:lint:27.1.1:lint-27.1.1.jar:2f6038a5398a42bd591883c3f5e5894f4ec52ca1c3683bf94fa8553c1700af81',
|
||||
'com.android.tools:annotations:27.1.1:annotations-27.1.1.jar:ff28c504d2acb9fd1a5ffbd97ae85cf59ee18c76927525aad250509bccf2cab1',
|
||||
'com.android.tools:common:27.1.1:common-27.1.1.jar:63d9a2a9ad6d278db319f3749b9f50bdf5457ef7020074a1bebe124e714b535c',
|
||||
'com.android.tools:dvlib:27.1.1:dvlib-27.1.1.jar:998a54201fc1cefee5f2399215e95c42b1f64f9e1d8f4452eb8255c68ba5440f',
|
||||
'com.android.tools:repository:27.1.1:repository-27.1.1.jar:d25b74ccabf4d876903efb375e9af6fb380d8ae0445bb74bbdcc225c1e37fa1d',
|
||||
'com.android.tools:sdk-common:27.1.1:sdk-common-27.1.1.jar:4473ae97d0ef7061ee1de61041d5aa97405ae08e44c09cf7bb278b42e4b97c7c',
|
||||
'com.android.tools:sdklib:27.1.1:sdklib-27.1.1.jar:08e6b83961ac9724b3c1e3d0eff971f13be6701292c77914b8794480f3391250',
|
||||
'com.android:signflinger:4.1.1:signflinger-4.1.1.jar:0c66825988873ec2d51057fa463f54a8f18fc7326ff4530b9da363b71e97ce60',
|
||||
'com.android:zipflinger:4.1.1:zipflinger-4.1.1.jar:0a8c3e52ac13dd031236f9fb5ba4408b1d5dcd12325a05440b36da09d8881446',
|
||||
'com.android.tools.build:apksig:4.1.3:apksig-4.1.3.jar:a851980c678ff7a6785388b9a9e8cc094788ce3c4a985ad2b19c2028fd3c631a',
|
||||
'com.android.tools.build:apkzlib:4.1.3:apkzlib-4.1.3.jar:475903065e7e83a8c1ba78d267c97a54dc5a04d768b535093850423d7b11f2c8',
|
||||
'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.3:builder-test-api-4.1.3.jar:3d2af66726b06b53b8d6d497efcee39ff9f77eb2f8d2cce38b31502383a40d2c',
|
||||
'com.android.tools.build:builder:4.1.3:builder-4.1.3.jar:a40426cd6d68f6a722ef4950058c075e4547025e8c2fd78e732ad89f15176f84',
|
||||
'com.android.tools.build:gradle-api:4.1.3:gradle-api-4.1.3.jar:11b1fb9de658bdcf9290b1c1517060d0c4d93f2b27975934989ca4ac890bc077',
|
||||
'com.android.tools.build:manifest-merger:27.1.3:manifest-merger-27.1.3.jar:ce8d4009b1f1584777a7ffa1da3b0551dc316bc8e08112e442c352af70f46f2d',
|
||||
'com.android.tools.ddms:ddmlib:27.1.3:ddmlib-27.1.3.jar:8f76e8236d2b9eebf26378746dad025c4c7c056a02e133dae4ddef47b283c710',
|
||||
'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.3:kotlin-compiler-27.1.3.jar:8d7a78d5efd213c5e467e42bd205582aad73ffc77ee5dc18eb1361c9af72f125',
|
||||
'com.android.tools.external.org-jetbrains:uast:27.1.3:uast-27.1.3.jar:aea53944a1ac6a05f12297b55290e8cbecfe54c4166260cfba4405823bfe1c78',
|
||||
'com.android.tools.layoutlib:layoutlib-api:27.1.3:layoutlib-api-27.1.3.jar:23875ce0a8429f33a4e86cc358f658faa0ba9c576f5f05760e544b453d67d04b',
|
||||
'com.android.tools.lint:lint-api:27.1.3:lint-api-27.1.3.jar:97666be32bcadacd944416ea334a9575ef8f4ad0c8f333151491ff4a7df43e1c',
|
||||
'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.3:lint-gradle-api-27.1.3.jar:e54131c287a2954e6ed78a3351e5e10e35a1da2f09ac443bf44b705c71b63a4d',
|
||||
'com.android.tools.lint:lint-gradle:27.1.3:lint-gradle-27.1.3.jar:6a79e48943649d63665db7b17dbaff7af93e94ab9b15072f1a4d90486294ee9f',
|
||||
'com.android.tools.lint:lint-model:27.1.3:lint-model-27.1.3.jar:acb9e792db7000e38e3c3ca21a9b14f2de6549d7a3fc92a97ffba3d06345e5bf',
|
||||
'com.android.tools.lint:lint:27.1.3:lint-27.1.3.jar:5a2e69d0901a3a476a5b2d5001de755868113145f5f6aa557750cfad5389a44b',
|
||||
'com.android.tools:annotations:27.1.3:annotations-27.1.3.jar:904dd771883496d5dfc86619ab2555968ea4e8a29d7a5f4f7cae6fbf5429f8f5',
|
||||
'com.android.tools:common:27.1.3:common-27.1.3.jar:17ab4728e3ea50f047dd5937f0faf35f2c5416962ed74891057087ddc328bf96',
|
||||
'com.android.tools:dvlib:27.1.3:dvlib-27.1.3.jar:cead1c0c356cbe43e6855b0330fe09ef4bec2c72e22bdb4c6e7cf7e6b1dfbc37',
|
||||
'com.android.tools:repository:27.1.3:repository-27.1.3.jar:99de1a178855b56b8cd91a56296f1e0a9399c445e6acc51f1d2927947cc472cb',
|
||||
'com.android.tools:sdk-common:27.1.3:sdk-common-27.1.3.jar:b591e2aa0f1be600795f5c9e2bf81cba9b052bee452fc86c3362b5dd9e427a14',
|
||||
'com.android.tools:sdklib:27.1.3:sdklib-27.1.3.jar:ad6c08a45fe2904d05656bdddf9f623fa5c1d16bbd7b8d6a270a0734136ae02e',
|
||||
'com.android:signflinger:4.1.3:signflinger-4.1.3.jar:f3103b55ccdc8dd9ee2517eb26af93b904d41303726594372d0df59d51156e5c',
|
||||
'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.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-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a',
|
||||
'com.google.dagger:dagger-spi:2.24:dagger-spi-2.24.jar:c038445d14dbcb4054e61bf49e05009edf26fce4fdc7ec1a9db544784f68e718',
|
||||
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64',
|
||||
'com.google.dagger:dagger-compiler:2.33:dagger-compiler-2.33.jar:aa8a0d8370c578fd6999802d0d90b9829377a46d2c1141e11b8f737970e7155e',
|
||||
'com.google.dagger:dagger-producers:2.33:dagger-producers-2.33.jar:5897f0b6eef799c2adfe3ccacc58c0fb374d58acb063c3ebe5366c38a8bce5c8',
|
||||
'com.google.dagger:dagger-spi:2.33:dagger-spi-2.33.jar:e2dcab2221b8afb9556ef0a1c83b0bd5f42552e254322a257330f754cdbbb9d4',
|
||||
'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.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',
|
||||
@@ -50,7 +51,7 @@ dependencyVerification {
|
||||
'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.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.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',
|
||||
@@ -62,21 +63,21 @@ dependencyVerification {
|
||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||
'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',
|
||||
'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.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',
|
||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||
'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:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
|
||||
'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: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: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-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',
|
||||
@@ -85,26 +86,31 @@ dependencyVerification {
|
||||
'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: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-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||
'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-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-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.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: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-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
|
||||
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
|
||||
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b',
|
||||
'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd',
|
||||
'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.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-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-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.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@ apply plugin: 'witness'
|
||||
apply from: 'witness.gradle'
|
||||
|
||||
dependencies {
|
||||
implementation "com.google.dagger:dagger:2.24"
|
||||
implementation "com.google.dagger:dagger:$dagger_version"
|
||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-legacy:$jmock_version"
|
||||
|
||||
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.briarproject.bramble.api;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface Consumer<T> {
|
||||
|
||||
void accept(T t);
|
||||
}
|
||||
@@ -9,4 +9,5 @@ public interface FeatureFlags {
|
||||
|
||||
boolean shouldEnableProfilePictures();
|
||||
|
||||
boolean shouldEnableDisappearingMessages();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.briarproject.bramble.api.cleanup;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* An interface for registering a hook with the {@link CleanupManager}
|
||||
* that will be called when a message's cleanup deadline is reached.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
public interface CleanupHook {
|
||||
|
||||
/**
|
||||
* Called when the cleanup deadlines of one or more messages are reached.
|
||||
* <p>
|
||||
* The callee is not required to delete the messages, but the hook won't be
|
||||
* called again for these messages unless another cleanup timer is set (see
|
||||
* {@link DatabaseComponent#setCleanupTimerDuration(Transaction, MessageId, long)}
|
||||
* and {@link DatabaseComponent#startCleanupTimer(Transaction, MessageId)}).
|
||||
*/
|
||||
void deleteMessages(Transaction txn, GroupId g,
|
||||
Collection<MessageId> messageIds) throws DbException;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.briarproject.bramble.api.cleanup;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.event.CleanupTimerStartedEvent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
/**
|
||||
* The CleanupManager is responsible for tracking the cleanup deadlines of
|
||||
* messages and passing them to their respective
|
||||
* {@link CleanupHook CleanupHooks} when the deadlines are reached.
|
||||
* <p>
|
||||
* The CleanupManager responds to
|
||||
* {@link CleanupTimerStartedEvent CleanupTimerStartedEvents} broadcast by the
|
||||
* {@link DatabaseComponent}.
|
||||
* <p>
|
||||
* See {@link DatabaseComponent#setCleanupTimerDuration(Transaction, MessageId, long)},
|
||||
* {@link DatabaseComponent#startCleanupTimer(Transaction, MessageId)},
|
||||
* {@link DatabaseComponent#stopCleanupTimer(Transaction, MessageId)}.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
public interface CleanupManager {
|
||||
|
||||
/**
|
||||
* When scheduling a cleanup task we overshoot the deadline by this many
|
||||
* milliseconds to reduce the number of tasks that need to be scheduled
|
||||
* when messages have cleanup deadlines that are close together.
|
||||
*/
|
||||
long BATCH_DELAY_MS = 1000;
|
||||
|
||||
/**
|
||||
* Registers a hook to be called when messages are due for cleanup.
|
||||
* This method should be called before
|
||||
* {@link LifecycleManager#startServices(SecretKey)}.
|
||||
*/
|
||||
void registerCleanupHook(ClientId c, int majorVersion,
|
||||
CleanupHook hook);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.briarproject.bramble.api.cleanup.event;
|
||||
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when a message's cleanup timer is started.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class CleanupTimerStartedEvent extends Event {
|
||||
|
||||
private final MessageId messageId;
|
||||
private final long cleanupDeadline;
|
||||
|
||||
public CleanupTimerStartedEvent(MessageId messageId,
|
||||
long cleanupDeadline) {
|
||||
this.messageId = messageId;
|
||||
this.cleanupDeadline = cleanupDeadline;
|
||||
}
|
||||
|
||||
public MessageId getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public long getCleanupDeadline() {
|
||||
return cleanupDeadline;
|
||||
}
|
||||
}
|
||||
@@ -32,28 +32,31 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return Whether or not this message should be shared
|
||||
* @throws DbException Should only be used for real database errors.
|
||||
* If this is thrown, delivery will be attempted again at next startup,
|
||||
* whereas if a FormatException is thrown, the message will be permanently
|
||||
* invalidated.
|
||||
* @throws FormatException Use this for any non-database error
|
||||
* that occurs while handling remotely created data.
|
||||
* This includes errors that occur while handling locally created data
|
||||
* 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!
|
||||
* @throws DbException if a database error occurs while delivering the
|
||||
* message. Delivery will be attempted again at next startup. Throwing
|
||||
* this exception has the same effect as returning
|
||||
* {@link DeliveryAction#DEFER}.
|
||||
* @throws FormatException if the message is invalid in the context of its
|
||||
* dependencies. The message and any dependents will be marked as invalid
|
||||
* and deleted along with their metadata. Throwing this exception has the
|
||||
* same effect as returning {@link DeliveryAction#REJECT}.
|
||||
*/
|
||||
protected abstract boolean incomingMessage(Transaction txn, Message m,
|
||||
BdfList body, BdfDictionary meta) throws DbException,
|
||||
FormatException;
|
||||
protected abstract DeliveryAction incomingMessage(Transaction txn,
|
||||
Message m, BdfList body, BdfDictionary meta)
|
||||
throws DbException, FormatException;
|
||||
|
||||
@Override
|
||||
public boolean incomingMessage(Transaction txn, Message m, Metadata meta)
|
||||
throws DbException, InvalidMessageException {
|
||||
public DeliveryAction incomingMessage(Transaction txn, Message m,
|
||||
Metadata meta) throws DbException, InvalidMessageException {
|
||||
try {
|
||||
BdfList body = clientHelper.toList(m);
|
||||
BdfDictionary metaDictionary = metadataParser.parse(meta);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.api.client;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
@@ -16,6 +17,7 @@ import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
@NotNullByDefault
|
||||
@@ -50,9 +52,11 @@ public interface ClientHelper {
|
||||
BdfDictionary getGroupMetadataAsDictionary(Transaction txn, GroupId g)
|
||||
throws DbException, FormatException;
|
||||
|
||||
Collection<MessageId> getMessageIds(Transaction txn, GroupId g,
|
||||
BdfDictionary query) throws DbException, FormatException;
|
||||
|
||||
BdfDictionary getMessageMetadataAsDictionary(MessageId m)
|
||||
throws DbException,
|
||||
FormatException;
|
||||
throws DbException, FormatException;
|
||||
|
||||
BdfDictionary getMessageMetadataAsDictionary(Transaction txn, MessageId m)
|
||||
throws DbException, FormatException;
|
||||
@@ -119,4 +123,17 @@ public interface ClientHelper {
|
||||
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
|
||||
BdfDictionary properties) throws FormatException;
|
||||
|
||||
/**
|
||||
* Retrieves the contact ID from the group metadata of the given contact
|
||||
* group.
|
||||
*/
|
||||
ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
||||
throws DbException, FormatException;
|
||||
|
||||
/**
|
||||
* Stores the given contact ID in the group metadata of the given contact
|
||||
* group.
|
||||
*/
|
||||
void setContactId(Transaction txn, GroupId contactGroupId, ContactId c)
|
||||
throws DbException;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.briarproject.bramble.api.client;
|
||||
|
||||
public interface ContactGroupConstants {
|
||||
|
||||
/**
|
||||
* Group metadata key for associating a contact ID with a contact group.
|
||||
*/
|
||||
String GROUP_KEY_CONTACT_ID = "contactId";
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.briarproject.bramble.api.contact.event;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when the alias for a contact changed.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class ContactAliasChangedEvent extends Event {
|
||||
|
||||
private final ContactId contactId;
|
||||
@Nullable
|
||||
private final String alias;
|
||||
|
||||
public ContactAliasChangedEvent(ContactId contactId,
|
||||
@Nullable String alias) {
|
||||
this.contactId = contactId;
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,19 @@
|
||||
package org.briarproject.bramble.api.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static final int LENGTH = 32;
|
||||
|
||||
private final byte[] key;
|
||||
|
||||
public SecretKey(byte[] key) {
|
||||
super(key);
|
||||
if (key.length != LENGTH) throw new IllegalArgumentException();
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,18 @@ import javax.annotation.Nullable;
|
||||
@NotNullByDefault
|
||||
public interface DatabaseComponent extends TransactionManager {
|
||||
|
||||
/**
|
||||
* Return value for {@link #getNextCleanupDeadline(Transaction)} if
|
||||
* no messages are scheduled to be deleted.
|
||||
*/
|
||||
long NO_CLEANUP_DEADLINE = -1;
|
||||
|
||||
/**
|
||||
* Return value for {@link #startCleanupTimer(Transaction, MessageId)}
|
||||
* if the cleanup timer was not started.
|
||||
*/
|
||||
long TIMER_NOT_STARTED = -1;
|
||||
|
||||
/**
|
||||
* Opens the database and returns true if the database already existed.
|
||||
*
|
||||
@@ -89,7 +101,7 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
/**
|
||||
* Stores a transport.
|
||||
*/
|
||||
void addTransport(Transaction txn, TransportId t, int maxLatency)
|
||||
void addTransport(Transaction txn, TransportId t, long maxLatency)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
@@ -106,6 +118,18 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
KeySetId addTransportKeys(Transaction txn, PendingContactId p,
|
||||
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
|
||||
* local pseudonym.
|
||||
@@ -138,6 +162,16 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
boolean containsPendingContact(Transaction txn, PendingContactId p)
|
||||
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
|
||||
* {@link #removeMessage(Transaction, MessageId)}, the message ID,
|
||||
@@ -166,7 +200,19 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
*/
|
||||
@Nullable
|
||||
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
|
||||
@@ -175,7 +221,7 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
*/
|
||||
@Nullable
|
||||
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
|
||||
@@ -194,7 +240,7 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
*/
|
||||
@Nullable
|
||||
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.
|
||||
@@ -288,6 +334,16 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
Collection<MessageId> getMessageIds(Transaction txn, GroupId g)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any delivered messages in the given group with
|
||||
* metadata that matches all entries in the given query. If the query is
|
||||
* empty, the IDs of all delivered messages are returned.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<MessageId> getMessageIds(Transaction txn, GroupId g,
|
||||
Metadata query) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any messages that need to be validated.
|
||||
* <p/>
|
||||
@@ -314,6 +370,15 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
Collection<MessageId> getMessagesToShare(Transaction txn)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any messages of any messages that are due for
|
||||
* deletion, along with their group IDs.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Map<GroupId, Collection<MessageId>> getMessagesToDelete(Transaction txn)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the metadata for all delivered messages in the given group.
|
||||
* <p/>
|
||||
@@ -395,6 +460,36 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
|
||||
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
|
||||
* message is due to be deleted, or {@link #NO_CLEANUP_DEADLINE}
|
||||
* if no messages are scheduled to be deleted.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
long getNextCleanupDeadline(Transaction txn) throws DbException;
|
||||
|
||||
/*
|
||||
* Returns the next time (in milliseconds since the Unix epoch) when a
|
||||
* message is due to be sent to the given contact. The returned value may
|
||||
@@ -443,6 +538,16 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t)
|
||||
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.
|
||||
*/
|
||||
@@ -535,6 +640,13 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the cleanup timer duration for the given message. This does not
|
||||
* start the message's cleanup timer.
|
||||
*/
|
||||
void setCleanupTimerDuration(Transaction txn, MessageId m, long duration)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given contact as verified.
|
||||
*/
|
||||
@@ -557,6 +669,12 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
*/
|
||||
void setMessagePermanent(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given message as not shared. This method is only meant for
|
||||
* testing.
|
||||
*/
|
||||
void setMessageNotShared(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given message as shared.
|
||||
*/
|
||||
@@ -599,6 +717,22 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Starts the cleanup timer for the given message, if a timer duration
|
||||
* has been set and the timer has not already been started.
|
||||
*
|
||||
* @return The cleanup deadline, or {@link #TIMER_NOT_STARTED} if no
|
||||
* timer duration has been set for this message or its timer has already
|
||||
* been started.
|
||||
*/
|
||||
long startCleanupTimer(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Stops the cleanup timer for the given message, if the timer has been
|
||||
* started.
|
||||
*/
|
||||
void stopCleanupTimer(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given transport keys, deleting any keys they have replaced.
|
||||
*/
|
||||
|
||||
@@ -57,6 +57,7 @@ public class Author implements Nameable {
|
||||
/**
|
||||
* Returns the author's name.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.briarproject.bramble.api.keyagreement.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 {
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.Wakeful;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -22,6 +23,7 @@ public interface LifecycleManager {
|
||||
*/
|
||||
enum StartResult {
|
||||
ALREADY_RUNNING,
|
||||
CLOCK_ERROR,
|
||||
DB_ERROR,
|
||||
DATA_TOO_OLD_ERROR,
|
||||
DATA_TOO_NEW_ERROR,
|
||||
@@ -65,6 +67,10 @@ public interface LifecycleManager {
|
||||
/**
|
||||
* Opens the {@link DatabaseComponent} using the given key and starts any
|
||||
* 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
|
||||
StartResult startServices(SecretKey dbKey);
|
||||
|
||||
@@ -61,7 +61,7 @@ public interface Plugin {
|
||||
/**
|
||||
* Returns the transport's maximum latency in milliseconds.
|
||||
*/
|
||||
int getMaxLatency();
|
||||
long getMaxLatency();
|
||||
|
||||
/**
|
||||
* Returns the transport's maximum idle time in milliseconds.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -15,13 +15,18 @@ public interface TransportConnectionWriter {
|
||||
/**
|
||||
* Returns the maximum latency of the transport in milliseconds.
|
||||
*/
|
||||
int getMaxLatency();
|
||||
long getMaxLatency();
|
||||
|
||||
/**
|
||||
* Returns the maximum idle time of the transport in milliseconds.
|
||||
*/
|
||||
int getMaxIdleTime();
|
||||
|
||||
/**
|
||||
* Returns true if the transport is lossy and cheap.
|
||||
*/
|
||||
boolean isLossyAndCheap();
|
||||
|
||||
/**
|
||||
* Returns an output stream for writing to the transport connection.
|
||||
*/
|
||||
|
||||
@@ -70,7 +70,7 @@ public abstract class AbstractDuplexTransportConnection
|
||||
private class Writer implements TransportConnectionWriter {
|
||||
|
||||
@Override
|
||||
public int getMaxLatency() {
|
||||
public long getMaxLatency() {
|
||||
return plugin.getMaxLatency();
|
||||
}
|
||||
|
||||
@@ -79,6 +79,11 @@ public abstract class AbstractDuplexTransportConnection
|
||||
return plugin.getMaxIdleTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLossyAndCheap() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
return AbstractDuplexTransportConnection.this.getOutputStream();
|
||||
|
||||
@@ -1,30 +1,11 @@
|
||||
package org.briarproject.bramble.api.plugin.duplex;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import org.briarproject.bramble.api.plugin.PluginFactory;
|
||||
|
||||
/**
|
||||
* Factory for creating a plugin for a duplex transport.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
public interface DuplexPluginFactory {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
public interface DuplexPluginFactory extends PluginFactory<DuplexPlugin> {
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.bramble.api.plugin;
|
||||
package org.briarproject.bramble.api.plugin.file;
|
||||
|
||||
public interface FileConstants {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.briarproject.bramble.api.plugin.file;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
public interface RemovableDriveConstants {
|
||||
|
||||
TransportId ID = new TransportId("org.briarproject.bramble.drive");
|
||||
|
||||
String PROP_PATH = "path";
|
||||
String PROP_URI = "uri";
|
||||
String PROP_SUPPORTED = "supported";
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.briarproject.bramble.api.plugin.file;
|
||||
|
||||
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.properties.TransportProperties;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface RemovableDriveManager {
|
||||
|
||||
/**
|
||||
* Returns the currently running reader task, or null if no reader task
|
||||
* is running.
|
||||
*/
|
||||
@Nullable
|
||||
RemovableDriveTask getCurrentReaderTask();
|
||||
|
||||
/**
|
||||
* Returns the currently running writer task, or null if no writer task
|
||||
* is running.
|
||||
*/
|
||||
@Nullable
|
||||
RemovableDriveTask getCurrentWriterTask();
|
||||
|
||||
/**
|
||||
* Starts and returns a reader task, reading from a stream described by
|
||||
* the given transport properties. If a reader task is already running,
|
||||
* it will be returned and the argument will be ignored.
|
||||
*/
|
||||
RemovableDriveTask startReaderTask(TransportProperties p);
|
||||
|
||||
/**
|
||||
* Starts and returns a writer task for the given contact, writing to
|
||||
* a stream described by the given transport properties. If a writer task
|
||||
* is already running, it will be returned and the arguments will be
|
||||
* ignored.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.briarproject.bramble.api.plugin.file;
|
||||
|
||||
import org.briarproject.bramble.api.Consumer;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface RemovableDriveTask extends Runnable {
|
||||
|
||||
/**
|
||||
* Returns the {@link TransportProperties} that were used for creating
|
||||
* this task.
|
||||
*/
|
||||
TransportProperties getTransportProperties();
|
||||
|
||||
/**
|
||||
* Adds an observer to the task. The observer will be notified on the
|
||||
* event thread of the current state of the task and any subsequent state
|
||||
* changes.
|
||||
*/
|
||||
void addObserver(Consumer<State> observer);
|
||||
|
||||
/**
|
||||
* Removes an observer from the task.
|
||||
*/
|
||||
void removeObserver(Consumer<State> observer);
|
||||
|
||||
class State {
|
||||
|
||||
private final long done, total;
|
||||
private final boolean finished, success;
|
||||
|
||||
public State(long done, long total, boolean finished, boolean success) {
|
||||
this.done = done;
|
||||
this.total = total;
|
||||
this.finished = finished;
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total length in bytes of the messages read or written
|
||||
* so far, or zero if the total is unknown.
|
||||
*/
|
||||
public long getDone() {
|
||||
return done;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total length in bytes of the messages that will have
|
||||
* been read or written when the task is complete, or zero if the
|
||||
* total is unknown.
|
||||
*/
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,12 @@ import javax.annotation.Nullable;
|
||||
@NotNullByDefault
|
||||
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
|
||||
* properties. Returns null if a reader cannot be created.
|
||||
|
||||
@@ -1,30 +1,11 @@
|
||||
package org.briarproject.bramble.api.plugin.simplex;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import org.briarproject.bramble.api.plugin.PluginFactory;
|
||||
|
||||
/**
|
||||
* Factory for creating a plugin for a simplex transport.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
public interface SimplexPluginFactory {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
public interface SimplexPluginFactory extends PluginFactory<SimplexPlugin> {
|
||||
}
|
||||
|
||||
@@ -21,5 +21,6 @@ public interface ReportingConstants {
|
||||
* Hidden service address for reporting crashes and feedback to the
|
||||
* developers.
|
||||
*/
|
||||
String DEV_ONION_ADDRESS = "cwqmubyvnig3wag3.onion";
|
||||
String DEV_ONION_ADDRESS =
|
||||
"b2nkt5doeamvdmjzfz7g42hk5vdtlnktlgzhel2bgjcc4v4jhnx2qrqd.onion";
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.UniqueId;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
|
||||
public interface SyncConstants {
|
||||
@@ -55,4 +56,9 @@ public interface SyncConstants {
|
||||
* connections.
|
||||
*/
|
||||
int PRIORITY_NONCE_BYTES = 16;
|
||||
|
||||
/**
|
||||
* The maximum allowed latency for any transport, in milliseconds.
|
||||
*/
|
||||
long MAX_TRANSPORT_LATENCY = DAYS.toMillis(365);
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ public interface SyncSessionFactory {
|
||||
PriorityHandler handler);
|
||||
|
||||
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
|
||||
int maxLatency, StreamWriter streamWriter);
|
||||
long maxLatency, boolean eager, StreamWriter streamWriter);
|
||||
|
||||
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
|
||||
int maxLatency, int maxIdleTime, StreamWriter streamWriter,
|
||||
long maxLatency, int maxIdleTime, StreamWriter streamWriter,
|
||||
@Nullable Priority priority);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,13 @@ public class MessagesSentEvent extends Event {
|
||||
|
||||
private final ContactId contactId;
|
||||
private final Collection<MessageId> messageIds;
|
||||
private final long totalLength;
|
||||
|
||||
public MessagesSentEvent(ContactId contactId,
|
||||
Collection<MessageId> messageIds) {
|
||||
Collection<MessageId> messageIds, long totalLength) {
|
||||
this.contactId = contactId;
|
||||
this.messageIds = messageIds;
|
||||
this.totalLength = totalLength;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
@@ -32,4 +34,8 @@ public class MessagesSentEvent extends Event {
|
||||
public Collection<MessageId> getMessageIds() {
|
||||
return messageIds;
|
||||
}
|
||||
|
||||
public long getTotalLength() {
|
||||
return totalLength;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,23 +10,54 @@ public interface IncomingMessageHook {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return Whether or not this message should be shared
|
||||
* @throws DbException Should only be used for real database errors.
|
||||
* If this is thrown, delivery will be attempted again at next startup,
|
||||
* whereas if an InvalidMessageException is thrown,
|
||||
* the message will be permanently invalidated.
|
||||
* @throws InvalidMessageException for any non-database error
|
||||
* that occurs while handling remotely created data.
|
||||
* This includes errors that occur while handling locally created data
|
||||
* 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!
|
||||
* @throws DbException if a database error occurs while delivering the
|
||||
* message. Delivery will be attempted again at next startup. Throwing
|
||||
* this exception has the same effect as returning
|
||||
* {@link DeliveryAction#DEFER}.
|
||||
* @throws InvalidMessageException if the message is invalid in the context
|
||||
* of its dependencies. The message and any dependents will be marked as
|
||||
* invalid and deleted along with their metadata. Throwing this exception
|
||||
* has the same effect as returning {@link DeliveryAction#REJECT}.
|
||||
*/
|
||||
boolean incomingMessage(Transaction txn, Message m, Metadata meta)
|
||||
DeliveryAction incomingMessage(Transaction txn, Message m, Metadata meta)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,33 @@
|
||||
package org.briarproject.bramble.api.sync.validation;
|
||||
|
||||
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -6,6 +6,22 @@ package org.briarproject.bramble.api.system;
|
||||
*/
|
||||
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()
|
||||
*/
|
||||
|
||||
@@ -22,8 +22,24 @@ public interface KeyManager {
|
||||
|
||||
/**
|
||||
* Derives and stores a set of rotation mode transport keys for
|
||||
* communicating with the given contact over each transport and returns the
|
||||
* key set IDs.
|
||||
* communicating with the given contact over the given transport and
|
||||
* 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/>
|
||||
* {@link StreamContext StreamContexts} for the contact can be created
|
||||
* after this method has returned.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.briarproject.bramble.util;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.list;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@NotNullByDefault
|
||||
public class NetworkUtils {
|
||||
|
||||
private static final Logger LOG = getLogger(NetworkUtils.class.getName());
|
||||
|
||||
public static List<NetworkInterface> getNetworkInterfaces() {
|
||||
try {
|
||||
Enumeration<NetworkInterface> ifaces =
|
||||
NetworkInterface.getNetworkInterfaces();
|
||||
// Despite what the docs say, the return value can be null
|
||||
//noinspection ConstantConditions
|
||||
return ifaces == null ? emptyList() : list(ifaces);
|
||||
} catch (SocketException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,9 @@ import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class ValidationUtils {
|
||||
|
||||
@@ -64,4 +66,9 @@ public class ValidationUtils {
|
||||
if (dictionary != null && dictionary.size() != size)
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public static void checkRange(@Nullable Long l, long min, long max)
|
||||
throws FormatException {
|
||||
if (l != null && (l < min || l > max)) throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +163,15 @@ public class TestUtils {
|
||||
|
||||
public static Message getMessage(GroupId groupId) {
|
||||
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) {
|
||||
return getMessage(groupId, bodyLength, timestamp);
|
||||
}
|
||||
|
||||
public static Message getMessage(GroupId groupId, int bodyLength,
|
||||
long timestamp) {
|
||||
MessageId id = new MessageId(getRandomId());
|
||||
byte[] body = getRandomBytes(bodyLength);
|
||||
return new Message(id, groupId, timestamp, body);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.briarproject.bramble.test;
|
||||
|
||||
public interface TimeTravel {
|
||||
|
||||
void setCurrentTimeMillis(long now) throws InterruptedException;
|
||||
|
||||
void addCurrentTimeMillis(long add) throws InterruptedException;
|
||||
}
|
||||
@@ -1,24 +1,27 @@
|
||||
dependencyVerification {
|
||||
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.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',
|
||||
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||
'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',
|
||||
'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: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.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
|
||||
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||
'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.codehaus.mojo:animal-sniffer-ant-tasks:1.20:animal-sniffer-ant-tasks-1.20.jar:bb7d2498144118311d968bb08ff6fae3fc535fb1cb9cca8b8e9ea65b189422ac',
|
||||
'org.codehaus.mojo:animal-sniffer:1.20:animal-sniffer-1.20.jar:80c422523c38db91260c6d78e5ee4b012862ab61cc55020c9e243dd7b5c62249',
|
||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
||||
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
||||
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
|
||||
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b',
|
||||
'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd',
|
||||
'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:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
||||
'org.ow2.asm:asm:9.1:asm-9.1.jar:cda4de455fab48ff0bcb7c48b4639447d4de859a7afc30a094a986f0936beba2',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -10,27 +10,36 @@ apply from: '../dagger.gradle'
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':bramble-api', configuration: 'default')
|
||||
implementation 'com.madgag.spongycastle:core:1.58.0.0'
|
||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.69'
|
||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||
implementation 'org.bitlet:weupnp:0.1.4'
|
||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
||||
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 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation 'net.jodah:concurrentunit:0.4.2'
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
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'
|
||||
}
|
||||
|
||||
animalsniffer {
|
||||
// Allow requireNonNull: Android desugaring rewrites it (so it's safe for us to use),
|
||||
// and it gets used when passing method references instead of lambdas with Java 11.
|
||||
// Note that this line allows *all* methods from java.util.Objects.
|
||||
// That's the best that we can do with the configuration options that Animal Sniffer offers.
|
||||
ignore 'java.util.Objects'
|
||||
}
|
||||
|
||||
// needed to make test output available to bramble-java
|
||||
configurations {
|
||||
testOutput.extendsFrom(testCompile)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble;
|
||||
|
||||
import org.briarproject.bramble.cleanup.CleanupModule;
|
||||
import org.briarproject.bramble.contact.ContactModule;
|
||||
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
||||
import org.briarproject.bramble.db.DatabaseExecutorModule;
|
||||
@@ -10,10 +11,13 @@ import org.briarproject.bramble.properties.PropertiesModule;
|
||||
import org.briarproject.bramble.rendezvous.RendezvousModule;
|
||||
import org.briarproject.bramble.sync.validation.ValidationModule;
|
||||
import org.briarproject.bramble.transport.TransportModule;
|
||||
import org.briarproject.bramble.transport.agreement.TransportKeyAgreementModule;
|
||||
import org.briarproject.bramble.versioning.VersioningModule;
|
||||
|
||||
public interface BrambleCoreEagerSingletons {
|
||||
|
||||
void inject(CleanupModule.EagerSingletons init);
|
||||
|
||||
void inject(ContactModule.EagerSingletons init);
|
||||
|
||||
void inject(CryptoExecutorModule.EagerSingletons init);
|
||||
@@ -30,6 +34,8 @@ public interface BrambleCoreEagerSingletons {
|
||||
|
||||
void inject(RendezvousModule.EagerSingletons init);
|
||||
|
||||
void inject(TransportKeyAgreementModule.EagerSingletons init);
|
||||
|
||||
void inject(TransportModule.EagerSingletons init);
|
||||
|
||||
void inject(ValidationModule.EagerSingletons init);
|
||||
@@ -39,6 +45,7 @@ public interface BrambleCoreEagerSingletons {
|
||||
class Helper {
|
||||
|
||||
public static void injectEagerSingletons(BrambleCoreEagerSingletons c) {
|
||||
c.inject(new CleanupModule.EagerSingletons());
|
||||
c.inject(new ContactModule.EagerSingletons());
|
||||
c.inject(new CryptoExecutorModule.EagerSingletons());
|
||||
c.inject(new DatabaseExecutorModule.EagerSingletons());
|
||||
@@ -47,6 +54,7 @@ public interface BrambleCoreEagerSingletons {
|
||||
c.inject(new RendezvousModule.EagerSingletons());
|
||||
c.inject(new PluginModule.EagerSingletons());
|
||||
c.inject(new PropertiesModule.EagerSingletons());
|
||||
c.inject(new TransportKeyAgreementModule.EagerSingletons());
|
||||
c.inject(new TransportModule.EagerSingletons());
|
||||
c.inject(new ValidationModule.EagerSingletons());
|
||||
c.inject(new VersioningModule.EagerSingletons());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble;
|
||||
|
||||
import org.briarproject.bramble.cleanup.CleanupModule;
|
||||
import org.briarproject.bramble.client.ClientModule;
|
||||
import org.briarproject.bramble.connection.ConnectionModule;
|
||||
import org.briarproject.bramble.contact.ContactModule;
|
||||
@@ -21,15 +22,15 @@ import org.briarproject.bramble.rendezvous.RendezvousModule;
|
||||
import org.briarproject.bramble.settings.SettingsModule;
|
||||
import org.briarproject.bramble.sync.SyncModule;
|
||||
import org.briarproject.bramble.sync.validation.ValidationModule;
|
||||
import org.briarproject.bramble.system.ClockModule;
|
||||
import org.briarproject.bramble.transport.TransportModule;
|
||||
import org.briarproject.bramble.transport.agreement.TransportKeyAgreementModule;
|
||||
import org.briarproject.bramble.versioning.VersioningModule;
|
||||
|
||||
import dagger.Module;
|
||||
|
||||
@Module(includes = {
|
||||
CleanupModule.class,
|
||||
ClientModule.class,
|
||||
ClockModule.class,
|
||||
ConnectionModule.class,
|
||||
ContactModule.class,
|
||||
CryptoModule.class,
|
||||
@@ -49,6 +50,7 @@ import dagger.Module;
|
||||
RendezvousModule.class,
|
||||
SettingsModule.class,
|
||||
SyncModule.class,
|
||||
TransportKeyAgreementModule.class,
|
||||
TransportModule.class,
|
||||
ValidationModule.class,
|
||||
VersioningModule.class
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package org.briarproject.bramble.cleanup;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.CleanupHook;
|
||||
import org.briarproject.bramble.api.cleanup.CleanupManager;
|
||||
import org.briarproject.bramble.api.cleanup.event.CleanupTimerStartedEvent;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.NO_CLEANUP_DEADLINE;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class CleanupManagerImpl implements CleanupManager, Service, EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(CleanupManagerImpl.class.getName());
|
||||
|
||||
private final Executor dbExecutor;
|
||||
private final DatabaseComponent db;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final Clock clock;
|
||||
private final Map<ClientMajorVersion, CleanupHook> hooks =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Object lock = new Object();
|
||||
|
||||
@GuardedBy("lock")
|
||||
private final Set<CleanupTask> pending = new HashSet<>();
|
||||
|
||||
@Inject
|
||||
CleanupManagerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
DatabaseComponent db, TaskScheduler taskScheduler, Clock clock) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.db = db;
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCleanupHook(ClientId c, int majorVersion,
|
||||
CleanupHook hook) {
|
||||
hooks.put(new ClientMajorVersion(c, majorVersion), hook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startService() {
|
||||
maybeScheduleTask(clock.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopService() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof CleanupTimerStartedEvent) {
|
||||
CleanupTimerStartedEvent a = (CleanupTimerStartedEvent) e;
|
||||
maybeScheduleTask(a.getCleanupDeadline());
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeScheduleTask(long deadline) {
|
||||
synchronized (lock) {
|
||||
for (CleanupTask task : pending) {
|
||||
if (task.deadline <= deadline) return;
|
||||
}
|
||||
CleanupTask task = new CleanupTask(deadline);
|
||||
pending.add(task);
|
||||
scheduleTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleTask(CleanupTask task) {
|
||||
long now = clock.currentTimeMillis();
|
||||
long delay = max(0, task.deadline - now + BATCH_DELAY_MS);
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Scheduling cleanup task in " + delay + " ms");
|
||||
}
|
||||
taskScheduler.schedule(() -> deleteMessagesAndScheduleNextTask(task),
|
||||
dbExecutor, delay, MILLISECONDS);
|
||||
}
|
||||
|
||||
private void deleteMessagesAndScheduleNextTask(CleanupTask task) {
|
||||
try {
|
||||
synchronized (lock) {
|
||||
pending.remove(task);
|
||||
}
|
||||
long deadline = db.transactionWithResult(false, txn -> {
|
||||
deleteMessages(txn);
|
||||
return db.getNextCleanupDeadline(txn);
|
||||
});
|
||||
if (deadline != NO_CLEANUP_DEADLINE) {
|
||||
maybeScheduleTask(deadline);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteMessages(Transaction txn) throws DbException {
|
||||
Map<GroupId, Collection<MessageId>> ids = db.getMessagesToDelete(txn);
|
||||
for (Entry<GroupId, Collection<MessageId>> e : ids.entrySet()) {
|
||||
GroupId groupId = e.getKey();
|
||||
Collection<MessageId> messageIds = e.getValue();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info(messageIds.size() + " messages to delete");
|
||||
}
|
||||
for (MessageId m : messageIds) db.stopCleanupTimer(txn, m);
|
||||
Group group = db.getGroup(txn, groupId);
|
||||
ClientMajorVersion cv = new ClientMajorVersion(group.getClientId(),
|
||||
group.getMajorVersion());
|
||||
CleanupHook hook = hooks.get(cv);
|
||||
if (hook == null) {
|
||||
throw new IllegalStateException("No cleanup hook for " + cv);
|
||||
}
|
||||
hook.deleteMessages(txn, groupId, messageIds);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CleanupTask {
|
||||
|
||||
private final long deadline;
|
||||
|
||||
private CleanupTask(long deadline) {
|
||||
this.deadline = deadline;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.briarproject.bramble.cleanup;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.CleanupManager;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class CleanupModule {
|
||||
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
CleanupManager cleanupManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
CleanupManager provideCleanupManager(LifecycleManager lifecycleManager,
|
||||
EventBus eventBus, CleanupManagerImpl cleanupManager) {
|
||||
lifecycleManager.registerService(cleanupManager);
|
||||
eventBus.addListener(cleanupManager);
|
||||
return cleanupManager;
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,13 @@ package org.briarproject.bramble.client;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
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.data.BdfEntry;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.BdfReader;
|
||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||
@@ -32,6 +34,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -39,6 +42,7 @@ import java.util.Map.Entry;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.client.ContactGroupConstants.GROUP_KEY_CONTACT_ID;
|
||||
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
@@ -151,6 +155,12 @@ class ClientHelperImpl implements ClientHelper {
|
||||
return metadataParser.parse(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageId> getMessageIds(Transaction txn, GroupId g,
|
||||
BdfDictionary query) throws DbException, FormatException {
|
||||
return db.getMessageIds(txn, g, metadataEncoder.encode(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfDictionary getMessageMetadataAsDictionary(MessageId m)
|
||||
throws DbException, FormatException {
|
||||
@@ -389,4 +399,27 @@ class ClientHelperImpl implements ClientHelper {
|
||||
return tpMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
||||
throws DbException {
|
||||
try {
|
||||
BdfDictionary meta =
|
||||
getGroupMetadataAsDictionary(txn, contactGroupId);
|
||||
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e); // Invalid group metadata
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContactId(Transaction txn, GroupId contactGroupId,
|
||||
ContactId c) throws DbException {
|
||||
BdfDictionary meta = BdfDictionary.of(
|
||||
new BdfEntry(GROUP_KEY_CONTACT_ID, c.getInt()));
|
||||
try {
|
||||
mergeGroupMetadata(txn, contactGroupId, meta);
|
||||
} catch (FormatException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ abstract class Connection {
|
||||
@Nullable
|
||||
StreamContext recogniseTag(TransportConnectionReader reader,
|
||||
TransportId transportId) {
|
||||
StreamContext ctx;
|
||||
try {
|
||||
byte[] tag = readTag(reader.getInputStream());
|
||||
return keyManager.getStreamContext(transportId, tag);
|
||||
|
||||
@@ -71,8 +71,10 @@ class OutgoingSimplexSyncConnection extends SyncConnection implements Runnable {
|
||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||
w.getOutputStream(), ctx);
|
||||
ContactId c = requireNonNull(ctx.getContactId());
|
||||
// Use eager retransmission if the transport is lossy and cheap
|
||||
return syncSessionFactory.createSimplexOutgoingSession(c,
|
||||
ctx.getTransportId(), w.getMaxLatency(), streamWriter);
|
||||
ctx.getTransportId(), w.getMaxLatency(), w.isLossyAndCheap(),
|
||||
streamWriter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
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.ContactExchangeRecordTypes.CONTACT_INFO;
|
||||
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
|
||||
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
|
||||
Contact contact = addContact(p, remoteInfo.author, localAuthor,
|
||||
|
||||
@@ -4,6 +4,9 @@ import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
||||
|
||||
import org.bouncycastle.crypto.CryptoException;
|
||||
import org.bouncycastle.crypto.Digest;
|
||||
import org.bouncycastle.crypto.digests.Blake2bDigest;
|
||||
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
@@ -20,9 +23,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.spongycastle.crypto.CryptoException;
|
||||
import org.spongycastle.crypto.Digest;
|
||||
import org.spongycastle.crypto.digests.Blake2bDigest;
|
||||
import org.whispersystems.curve25519.Curve25519;
|
||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.bouncycastle.crypto.BasicAgreement;
|
||||
import org.bouncycastle.crypto.BlockCipher;
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.CryptoException;
|
||||
import org.bouncycastle.crypto.DerivationFunction;
|
||||
import org.bouncycastle.crypto.KeyEncoder;
|
||||
import org.bouncycastle.crypto.Mac;
|
||||
import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||
import org.bouncycastle.crypto.digests.SHA256Digest;
|
||||
import org.bouncycastle.crypto.engines.AESLightEngine;
|
||||
import org.bouncycastle.crypto.engines.IESEngine;
|
||||
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
|
||||
import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator;
|
||||
import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
|
||||
import org.bouncycastle.crypto.macs.HMac;
|
||||
import org.bouncycastle.crypto.modes.CBCBlockCipher;
|
||||
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
|
||||
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.params.IESWithCipherParameters;
|
||||
import org.bouncycastle.crypto.parsers.ECIESPublicKeyParser;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||
import org.spongycastle.asn1.x9.X9ECParameters;
|
||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.spongycastle.crypto.BasicAgreement;
|
||||
import org.spongycastle.crypto.BlockCipher;
|
||||
import org.spongycastle.crypto.CipherParameters;
|
||||
import org.spongycastle.crypto.CryptoException;
|
||||
import org.spongycastle.crypto.DerivationFunction;
|
||||
import org.spongycastle.crypto.KeyEncoder;
|
||||
import org.spongycastle.crypto.Mac;
|
||||
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
||||
import org.spongycastle.crypto.engines.AESLightEngine;
|
||||
import org.spongycastle.crypto.engines.IESEngine;
|
||||
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
||||
import org.spongycastle.crypto.generators.EphemeralKeyPairGenerator;
|
||||
import org.spongycastle.crypto.generators.KDF2BytesGenerator;
|
||||
import org.spongycastle.crypto.macs.HMac;
|
||||
import org.spongycastle.crypto.modes.CBCBlockCipher;
|
||||
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
|
||||
import org.spongycastle.crypto.params.AsymmetricKeyParameter;
|
||||
import org.spongycastle.crypto.params.ECDomainParameters;
|
||||
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.spongycastle.crypto.params.IESWithCipherParameters;
|
||||
import org.spongycastle.crypto.parsers.ECIESPublicKeyParser;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.bouncycastle.crypto.generators.SCrypt;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.spongycastle.crypto.generators.SCrypt;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.math.ec.ECCurve;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.spongycastle.crypto.params.ECDomainParameters;
|
||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.spongycastle.math.ec.ECCurve;
|
||||
import org.spongycastle.math.ec.ECPoint;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.bouncycastle.crypto.Digest;
|
||||
import org.bouncycastle.crypto.digests.Blake2bDigest;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
@@ -9,8 +11,6 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.spongycastle.crypto.Digest;
|
||||
import org.spongycastle.crypto.digests.Blake2bDigest;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.bouncycastle.crypto.DataLengthException;
|
||||
import org.bouncycastle.crypto.engines.XSalsa20Engine;
|
||||
import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
|
||||
import org.bouncycastle.crypto.macs.Poly1305;
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
import org.bouncycastle.crypto.params.ParametersWithIV;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.spongycastle.crypto.DataLengthException;
|
||||
import org.spongycastle.crypto.engines.XSalsa20Engine;
|
||||
import org.spongycastle.crypto.generators.Poly1305KeyGenerator;
|
||||
import org.spongycastle.crypto.macs.Poly1305;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
import org.spongycastle.crypto.params.ParametersWithIV;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
|
||||
@@ -68,6 +68,13 @@ interface Database<T> {
|
||||
*/
|
||||
void close() throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the dirty flag was set while opening the database,
|
||||
* indicating that the database has not been shut down properly the last
|
||||
* time it was closed and some data could be lost.
|
||||
*/
|
||||
boolean wasDirtyOnInitialisation();
|
||||
|
||||
/**
|
||||
* Starts a new transaction and returns an object representing it.
|
||||
*/
|
||||
@@ -138,7 +145,7 @@ interface Database<T> {
|
||||
/**
|
||||
* Stores a transport.
|
||||
*/
|
||||
void addTransport(T txn, TransportId t, int maxLatency)
|
||||
void addTransport(T txn, TransportId t, long maxLatency)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
@@ -155,6 +162,18 @@ interface Database<T> {
|
||||
KeySetId addTransportKeys(T txn, PendingContactId p, 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(T txn, ContactId c, long maxLatency,
|
||||
boolean eager) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the database contains the given contact for the given
|
||||
* local pseudonym.
|
||||
@@ -208,6 +227,16 @@ interface Database<T> {
|
||||
*/
|
||||
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
|
||||
* shared, and the visibility of the message's group to the given contact
|
||||
@@ -454,7 +483,7 @@ interface Database<T> {
|
||||
* Read-only.
|
||||
*/
|
||||
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
|
||||
@@ -469,10 +498,36 @@ interface Database<T> {
|
||||
* Returns the IDs of some messages that are eligible to be sent to the
|
||||
* given contact, up to the given total length.
|
||||
* <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.
|
||||
*/
|
||||
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.
|
||||
@@ -497,6 +552,25 @@ interface Database<T> {
|
||||
*/
|
||||
Collection<MessageId> getMessagesToShare(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any messages of any messages that are due for
|
||||
* deletion, along with their group IDs.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Map<GroupId, Collection<MessageId>> getMessagesToDelete(T txn)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the next time (in milliseconds since the Unix epoch) when a
|
||||
* message is due to be deleted, or
|
||||
* {@link DatabaseComponent#NO_CLEANUP_DEADLINE} if no messages are
|
||||
* scheduled to be deleted.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
long getNextCleanupDeadline(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the next time (in milliseconds since the Unix epoch) when a
|
||||
* message is due to be sent to the given contact. The returned value may
|
||||
@@ -530,7 +604,7 @@ interface Database<T> {
|
||||
* Read-only.
|
||||
*/
|
||||
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.
|
||||
@@ -554,6 +628,16 @@ interface Database<T> {
|
||||
Collection<TransportKeySet> getTransportKeys(T txn, TransportId t)
|
||||
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.
|
||||
*/
|
||||
@@ -606,8 +690,10 @@ interface Database<T> {
|
||||
|
||||
/**
|
||||
* Marks a message as having been seen by the given contact.
|
||||
*
|
||||
* @return True if the message was not already marked as seen.
|
||||
*/
|
||||
void raiseSeenFlag(T txn, ContactId c, MessageId m) throws DbException;
|
||||
boolean raiseSeenFlag(T txn, ContactId c, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes a contact from the database.
|
||||
@@ -671,6 +757,13 @@ interface Database<T> {
|
||||
*/
|
||||
void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the cleanup timer duration for the given message. This does not
|
||||
* start the message's cleanup timer.
|
||||
*/
|
||||
void setCleanupTimerDuration(T txn, MessageId m, long duration)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given contact as verified.
|
||||
*/
|
||||
@@ -701,9 +794,10 @@ interface Database<T> {
|
||||
void setMessagePermanent(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given message as shared.
|
||||
* Marks the given message as shared or not.
|
||||
*/
|
||||
void setMessageShared(T txn, MessageId m) throws DbException;
|
||||
void setMessageShared(T txn, MessageId m, boolean shared)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the validation and delivery state of the given message.
|
||||
@@ -730,12 +824,28 @@ interface Database<T> {
|
||||
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Starts the cleanup timer for the given message, if a timer duration
|
||||
* has been set and the timer has not already been started.
|
||||
*
|
||||
* @return The cleanup deadline, or
|
||||
* {@link DatabaseComponent#TIMER_NOT_STARTED} if no timer duration has
|
||||
* been set for this message or its timer has already been started.
|
||||
*/
|
||||
long startCleanupTimer(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Stops the cleanup timer for the given message, if the timer has been
|
||||
* started.
|
||||
*/
|
||||
void stopCleanupTimer(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Updates the transmission count, expiry time and estimated time of arrival
|
||||
* of the given message with respect to the given contact, using the latency
|
||||
* 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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.event.CleanupTimerStartedEvent;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactAliasChangedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
|
||||
@@ -308,7 +310,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
|
||||
@Override
|
||||
public void addTransport(Transaction transaction, TransportId t,
|
||||
int maxLatency) throws DbException {
|
||||
long maxLatency) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsTransport(txn, t))
|
||||
@@ -339,6 +341,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
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
|
||||
public boolean containsContact(Transaction transaction, AuthorId remote,
|
||||
AuthorId local) throws DbException {
|
||||
@@ -369,6 +380,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
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
|
||||
public void deleteMessage(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
@@ -406,28 +424,57 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
@Nullable
|
||||
@Override
|
||||
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();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
Collection<MessageId> ids =
|
||||
db.getMessagesToSend(txn, c, maxLength, maxLatency);
|
||||
long totalLength = 0;
|
||||
List<Message> messages = new ArrayList<>(ids.size());
|
||||
for (MessageId m : ids) {
|
||||
messages.add(db.getMessage(txn, m));
|
||||
Message message = db.getMessage(txn, m);
|
||||
totalLength += message.getRawLength();
|
||||
messages.add(message);
|
||||
db.updateExpiryTimeAndEta(txn, c, m, maxLatency);
|
||||
}
|
||||
if (ids.isEmpty()) return null;
|
||||
db.lowerRequestedFlag(txn, c, ids);
|
||||
transaction.attach(new MessagesSentEvent(c, ids));
|
||||
transaction.attach(new MessagesSentEvent(c, ids, totalLength));
|
||||
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
|
||||
@Override
|
||||
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();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
@@ -458,21 +505,24 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
@Nullable
|
||||
@Override
|
||||
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();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
Collection<MessageId> ids =
|
||||
db.getRequestedMessagesToSend(txn, c, maxLength, maxLatency);
|
||||
long totalLength = 0;
|
||||
List<Message> messages = new ArrayList<>(ids.size());
|
||||
for (MessageId m : ids) {
|
||||
messages.add(db.getMessage(txn, m));
|
||||
Message message = db.getMessage(txn, m);
|
||||
totalLength += message.getRawLength();
|
||||
messages.add(message);
|
||||
db.updateExpiryTimeAndEta(txn, c, m, maxLatency);
|
||||
}
|
||||
if (ids.isEmpty()) return null;
|
||||
db.lowerRequestedFlag(txn, c, ids);
|
||||
transaction.attach(new MessagesSentEvent(c, ids));
|
||||
transaction.attach(new MessagesSentEvent(c, ids, totalLength));
|
||||
return messages;
|
||||
}
|
||||
|
||||
@@ -576,6 +626,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
return db.getMessageIds(txn, g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageId> getMessageIds(Transaction transaction,
|
||||
GroupId g, Metadata query) throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsGroup(txn, g))
|
||||
throw new NoSuchGroupException();
|
||||
return db.getMessageIds(txn, g, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageId> getMessagesToValidate(Transaction transaction)
|
||||
throws DbException {
|
||||
@@ -597,6 +656,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
return db.getMessagesToShare(txn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<GroupId, Collection<MessageId>> getMessagesToDelete(
|
||||
Transaction transaction) throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
return db.getMessagesToDelete(txn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MessageId, Metadata> getMessageMetadata(Transaction transaction,
|
||||
GroupId g) throws DbException {
|
||||
@@ -674,6 +740,25 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
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
|
||||
public Map<MessageId, MessageState> getMessageDependencies(
|
||||
Transaction transaction, MessageId m) throws DbException {
|
||||
@@ -692,6 +777,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
return db.getMessageDependents(txn, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextCleanupDeadline(Transaction transaction)
|
||||
throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
return db.getNextCleanupDeadline(txn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextSendTime(Transaction transaction, ContactId c)
|
||||
throws DbException {
|
||||
@@ -740,6 +832,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
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
|
||||
public void incrementStreamCounter(Transaction transaction, TransportId t,
|
||||
KeySetId k) throws DbException {
|
||||
@@ -795,8 +894,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
Collection<MessageId> acked = new ArrayList<>();
|
||||
for (MessageId m : a.getMessageIds()) {
|
||||
if (db.containsVisibleMessage(txn, c, m)) {
|
||||
db.raiseSeenFlag(txn, c, m);
|
||||
acked.add(m);
|
||||
if (db.raiseSeenFlag(txn, c, m)) {
|
||||
// This is the first time the message has been acked by
|
||||
// this contact. Start the cleanup timer (a no-op unless
|
||||
// a cleanup deadline has been set for this message)
|
||||
long deadline = db.startCleanupTimer(txn, m);
|
||||
if (deadline != TIMER_NOT_STARTED) {
|
||||
transaction.attach(new CleanupTimerStartedEvent(m,
|
||||
deadline));
|
||||
}
|
||||
acked.add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (acked.size() > 0) {
|
||||
@@ -952,6 +1060,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
db.removeTransportKeys(txn, t, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCleanupTimerDuration(Transaction transaction, MessageId m,
|
||||
long duration) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
db.setCleanupTimerDuration(txn, m, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContactVerified(Transaction transaction, ContactId c)
|
||||
throws DbException {
|
||||
@@ -970,6 +1088,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
transaction.attach(new ContactAliasChangedEvent(c, alias));
|
||||
db.setContactAlias(txn, c, alias);
|
||||
}
|
||||
|
||||
@@ -1001,6 +1120,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
db.setMessagePermanent(txn, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageNotShared(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
db.setMessageShared(txn, m, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageShared(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
@@ -1010,7 +1139,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
throw new NoSuchMessageException();
|
||||
if (db.getMessageState(txn, m) != DELIVERED)
|
||||
throw new IllegalArgumentException("Shared undelivered message");
|
||||
db.setMessageShared(txn, m);
|
||||
db.setMessageShared(txn, m, true);
|
||||
transaction.attach(new MessageSharedEvent(m));
|
||||
}
|
||||
|
||||
@@ -1082,6 +1211,30 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
db.setTransportKeysActive(txn, t, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startCleanupTimer(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
long deadline = db.startCleanupTimer(txn, m);
|
||||
if (deadline != TIMER_NOT_STARTED) {
|
||||
transaction.attach(new CleanupTimerStartedEvent(m, deadline));
|
||||
}
|
||||
return deadline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopCleanupTimer(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
db.stopCleanupTimer(txn, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTransportKeys(Transaction transaction,
|
||||
Collection<TransportKeySet> keys) throws DbException {
|
||||
|
||||
@@ -37,4 +37,10 @@ interface DatabaseConstants {
|
||||
* has passed since the last compaction.
|
||||
*/
|
||||
long MAX_COMPACTION_INTERVAL_MS = DAYS.toMillis(30);
|
||||
|
||||
/**
|
||||
* The {@link Settings} key under which the flag is stored indicating
|
||||
* whether the database is marked as dirty.
|
||||
*/
|
||||
String DIRTY_KEY = "dirty";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_TRANSPORT_LATENCY;
|
||||
|
||||
class ExponentialBackoff {
|
||||
|
||||
/**
|
||||
@@ -11,9 +13,11 @@ class ExponentialBackoff {
|
||||
* transmissions increases exponentially. If the expiry time would
|
||||
* 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 (maxLatency <= 0) throw new IllegalArgumentException();
|
||||
if (maxLatency <= 0 || maxLatency > MAX_TRANSPORT_LATENCY) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (txCount < 0) throw new IllegalArgumentException();
|
||||
// The maximum round-trip time is twice the maximum latency
|
||||
long roundTrip = maxLatency * 2L;
|
||||
|
||||
@@ -84,9 +84,14 @@ class H2Database extends JdbcDatabase {
|
||||
@Override
|
||||
public void close() throws DbException {
|
||||
// H2 will close the database when the last connection closes
|
||||
Connection c = null;
|
||||
try {
|
||||
c = createConnection();
|
||||
super.closeAllConnections();
|
||||
setDirty(c, false);
|
||||
c.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(c, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
try {
|
||||
super.closeAllConnections();
|
||||
c = createConnection();
|
||||
setDirty(c, false);
|
||||
s = c.createStatement();
|
||||
s.executeQuery("SHUTDOWN");
|
||||
s.close();
|
||||
|
||||
@@ -51,6 +51,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -72,6 +73,8 @@ import static java.util.Arrays.asList;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.NO_CLEANUP_DEADLINE;
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.TIMER_NOT_STARTED;
|
||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||
@@ -81,6 +84,7 @@ import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERE
|
||||
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
||||
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.DIRTY_KEY;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
||||
@@ -98,7 +102,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
||||
abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
// Package access for testing
|
||||
static final int CODE_SCHEMA_VERSION = 47;
|
||||
static final int CODE_SCHEMA_VERSION = 49;
|
||||
|
||||
// Time period offsets for incoming transport keys
|
||||
private static final int OFFSET_PREV = -1;
|
||||
@@ -180,6 +184,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " state INT NOT NULL,"
|
||||
+ " shared BOOLEAN NOT NULL,"
|
||||
+ " temporary BOOLEAN NOT NULL,"
|
||||
// Null if no timer duration has been set
|
||||
+ " cleanupTimerDuration BIGINT,"
|
||||
// Null if no timer duration has been set or the timer
|
||||
// hasn't started
|
||||
+ " cleanupDeadline BIGINT,"
|
||||
+ " length INT NOT NULL,"
|
||||
+ " raw BLOB," // Null if message has been deleted
|
||||
+ " PRIMARY KEY (messageId),"
|
||||
@@ -258,7 +267,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
private static final String CREATE_TRANSPORTS =
|
||||
"CREATE TABLE transports"
|
||||
+ " (transportId _STRING NOT NULL,"
|
||||
+ " maxLatency INT NOT NULL,"
|
||||
+ " maxLatency BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (transportId))";
|
||||
|
||||
private static final String CREATE_PENDING_CONTACTS =
|
||||
@@ -336,6 +345,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
"CREATE INDEX IF NOT EXISTS statusesByContactIdTimestamp"
|
||||
+ " 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 =
|
||||
"CREATE INDEX IF NOT EXISTS messagesByCleanupDeadline"
|
||||
+ " ON messages (cleanupDeadline)";
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(JdbcDatabase.class.getName());
|
||||
|
||||
@@ -354,9 +372,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
@GuardedBy("connectionsLock")
|
||||
private boolean closed = false;
|
||||
|
||||
private volatile boolean wasDirtyOnInitialisation = false;
|
||||
|
||||
protected abstract Connection createConnection()
|
||||
throws DbException, SQLException;
|
||||
|
||||
// Used exclusively during open to compact the database after schema
|
||||
// migrations or after DatabaseConstants#MAX_COMPACTION_INTERVAL_MS has
|
||||
// elapsed
|
||||
protected abstract void compactAndClose() throws DbException;
|
||||
|
||||
JdbcDatabase(DatabaseTypes databaseTypes, MessageFactory messageFactory,
|
||||
@@ -381,13 +404,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
try {
|
||||
if (reopen) {
|
||||
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||
wasDirtyOnInitialisation = isDirty(s);
|
||||
compact = migrateSchema(txn, s, listener) || isCompactionDue(s);
|
||||
} else {
|
||||
wasDirtyOnInitialisation = false;
|
||||
createTables(txn);
|
||||
initialiseSettings(txn);
|
||||
compact = false;
|
||||
}
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("db dirty? " + wasDirtyOnInitialisation);
|
||||
}
|
||||
createIndexes(txn);
|
||||
setDirty(txn, true);
|
||||
commitTransaction(txn);
|
||||
} catch (DbException e) {
|
||||
abortTransaction(txn);
|
||||
@@ -414,6 +443,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasDirtyOnInitialisation() {
|
||||
return wasDirtyOnInitialisation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the schema version stored in the database with the schema
|
||||
* version used by the current code and applies any suitable migrations to
|
||||
@@ -463,7 +497,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
new Migration43_44(dbTypes),
|
||||
new Migration44_45(),
|
||||
new Migration45_46(),
|
||||
new Migration46_47(dbTypes)
|
||||
new Migration46_47(dbTypes),
|
||||
new Migration47_48(),
|
||||
new Migration48_49()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -488,6 +524,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
||||
}
|
||||
|
||||
private boolean isDirty(Settings s) {
|
||||
return s.getBoolean(DIRTY_KEY, false);
|
||||
}
|
||||
|
||||
protected void setDirty(Connection txn, boolean dirty) throws DbException {
|
||||
Settings s = new Settings();
|
||||
s.putBoolean(DIRTY_KEY, dirty);
|
||||
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
||||
}
|
||||
|
||||
private void initialiseSettings(Connection txn) throws DbException {
|
||||
Settings s = new Settings();
|
||||
s.putInt(SCHEMA_VERSION_KEY, CODE_SCHEMA_VERSION);
|
||||
@@ -531,6 +577,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
s.executeUpdate(INDEX_MESSAGE_DEPENDENCIES_BY_DEPENDENCY_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_TX_COUNT_TIMESTAMP);
|
||||
s.executeUpdate(INDEX_MESSAGES_BY_CLEANUP_DEADLINE);
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
@@ -959,7 +1007,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTransport(Connection txn, TransportId t, int maxLatency)
|
||||
public void addTransport(Connection txn, TransportId t, long maxLatency)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
@@ -1080,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
|
||||
public boolean containsContact(Connection txn, AuthorId remote,
|
||||
AuthorId local) throws DbException {
|
||||
@@ -1237,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
|
||||
public boolean containsVisibleMessage(Connection txn, ContactId c,
|
||||
MessageId m) throws DbException {
|
||||
@@ -1290,7 +1410,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
public void deleteMessage(Connection txn, MessageId m) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE messages SET raw = NULL WHERE messageId = ?";
|
||||
String sql = "UPDATE messages"
|
||||
+ " SET raw = NULL, cleanupDeadline = NULL"
|
||||
+ " WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
@@ -1769,7 +1891,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
// Return early if there are no matches
|
||||
if (intersection.isEmpty()) return Collections.emptySet();
|
||||
}
|
||||
if (intersection == null) throw new AssertionError();
|
||||
return intersection;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
@@ -2068,7 +2189,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
@Override
|
||||
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 eta = now + maxLatency;
|
||||
PreparedStatement ps = null;
|
||||
@@ -2127,7 +2248,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
@Override
|
||||
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 eta = now + maxLatency;
|
||||
PreparedStatement ps = null;
|
||||
@@ -2164,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.getLong(1);
|
||||
rs.close();
|
||||
ps.close();
|
||||
return total;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageId> getMessagesToValidate(Connection txn)
|
||||
throws DbException {
|
||||
@@ -2226,6 +2404,39 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<GroupId, Collection<MessageId>> getMessagesToDelete(
|
||||
Connection txn) throws DbException {
|
||||
long now = clock.currentTimeMillis();
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT messageId, groupId FROM messages"
|
||||
+ " WHERE cleanupDeadline <= ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, now);
|
||||
rs = ps.executeQuery();
|
||||
Map<GroupId, Collection<MessageId>> ids = new HashMap<>();
|
||||
while (rs.next()) {
|
||||
MessageId m = new MessageId(rs.getBytes(1));
|
||||
GroupId g = new GroupId(rs.getBytes(2));
|
||||
Collection<MessageId> messageIds = ids.get(g);
|
||||
if (messageIds == null) {
|
||||
messageIds = new ArrayList<>();
|
||||
ids.put(g, messageIds);
|
||||
}
|
||||
messageIds.add(m);
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
return ids;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextSendTime(Connection txn, ContactId c)
|
||||
throws DbException {
|
||||
@@ -2256,6 +2467,31 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextCleanupDeadline(Connection txn) throws DbException {
|
||||
Statement s = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT cleanupDeadline FROM messages"
|
||||
+ " WHERE cleanupDeadline IS NOT NULL"
|
||||
+ " ORDER BY cleanupDeadline LIMIT 1";
|
||||
s = txn.createStatement();
|
||||
rs = s.executeQuery(sql);
|
||||
long nextDeadline = NO_CLEANUP_DEADLINE;
|
||||
if (rs.next()) {
|
||||
nextDeadline = rs.getLong(1);
|
||||
if (rs.next()) throw new AssertionError();
|
||||
}
|
||||
rs.close();
|
||||
s.close();
|
||||
return nextDeadline;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(s, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingContact getPendingContact(Connection txn, PendingContactId p)
|
||||
throws DbException {
|
||||
@@ -2311,7 +2547,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
@Override
|
||||
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 eta = now + maxLatency;
|
||||
PreparedStatement ps = null;
|
||||
@@ -2475,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
|
||||
public void incrementStreamCounter(Connection txn, TransportId t,
|
||||
KeySetId k) throws DbException {
|
||||
@@ -2776,7 +3044,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void raiseSeenFlag(Connection txn, ContactId c, MessageId m)
|
||||
public boolean raiseSeenFlag(Connection txn, ContactId c, MessageId m)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
@@ -2788,6 +3056,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
return affected == 1;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
@@ -3021,6 +3290,25 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCleanupTimerDuration(Connection txn, MessageId m,
|
||||
long duration) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE messages SET cleanupTimerDuration = ?"
|
||||
+ " WHERE messageId = ? AND cleanupTimerDuration IS NULL";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, duration);
|
||||
ps.setBytes(2, m.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContactVerified(Connection txn, ContactId c)
|
||||
throws DbException {
|
||||
@@ -3128,22 +3416,24 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageShared(Connection txn, MessageId m)
|
||||
public void setMessageShared(Connection txn, MessageId m, boolean shared)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE messages SET shared = TRUE"
|
||||
String sql = "UPDATE messages SET shared = ?"
|
||||
+ " WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
ps.setBoolean(1, shared);
|
||||
ps.setBytes(2, m.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
// Update denormalised column in statuses
|
||||
sql = "UPDATE statuses SET messageShared = TRUE"
|
||||
sql = "UPDATE statuses SET messageShared = ?"
|
||||
+ " WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
ps.setBoolean(1, shared);
|
||||
ps.setBytes(2, m.getBytes());
|
||||
affected = ps.executeUpdate();
|
||||
if (affected < 0) throw new DbStateException();
|
||||
ps.close();
|
||||
@@ -3272,9 +3562,63 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startCleanupTimer(Connection txn, MessageId m)
|
||||
throws DbException {
|
||||
long now = clock.currentTimeMillis();
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "UPDATE messages"
|
||||
+ " SET cleanupDeadline = ? + cleanupTimerDuration"
|
||||
+ " WHERE messageId = ?"
|
||||
+ " AND cleanupTimerDuration IS NOT NULL"
|
||||
+ " AND cleanupDeadline IS NULL";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, now);
|
||||
ps.setBytes(2, m.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
if (affected == 0) return TIMER_NOT_STARTED;
|
||||
sql = "SELECT cleanupDeadline FROM messages WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
rs = ps.executeQuery();
|
||||
if (!rs.next()) throw new DbStateException();
|
||||
long deadline = rs.getLong(1);
|
||||
if (rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.close();
|
||||
return deadline;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopCleanupTimer(Connection txn, MessageId m)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE messages SET cleanupDeadline = NULL"
|
||||
+ " WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m,
|
||||
int maxLatency) throws DbException {
|
||||
long maxLatency) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
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 Migration47_48 implements Migration<Connection> {
|
||||
|
||||
private static final Logger LOG = getLogger(Migration47_48.class.getName());
|
||||
|
||||
@Override
|
||||
public int getStartVersion() {
|
||||
return 47;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndVersion() {
|
||||
return 48;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(Connection txn) throws DbException {
|
||||
Statement s = null;
|
||||
try {
|
||||
s = txn.createStatement();
|
||||
// Null if no timer duration has been set
|
||||
s.execute("ALTER TABLE messages"
|
||||
+ " ADD COLUMN cleanupTimerDuration BIGINT");
|
||||
// Null if no timer duration has been set or the timer
|
||||
// hasn't started
|
||||
s.execute("ALTER TABLE messages"
|
||||
+ " ADD COLUMN cleanupDeadline BIGINT");
|
||||
s.execute("CREATE INDEX IF NOT EXISTS messagesByCleanupDeadline"
|
||||
+ " ON messages (cleanupDeadline)");
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,12 +34,12 @@ class KeyAgreementTransport {
|
||||
Logger.getLogger(KeyAgreementTransport.class.getName());
|
||||
|
||||
// Accept records with current protocol version, known record type
|
||||
private static Predicate<Record> ACCEPT = r ->
|
||||
private static final Predicate<Record> ACCEPT = r ->
|
||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||
isKnownRecordType(r.getRecordType());
|
||||
|
||||
// Ignore records with current protocol version, unknown record type
|
||||
private static Predicate<Record> IGNORE = r ->
|
||||
private static final Predicate<Record> IGNORE = r ->
|
||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||
!isKnownRecordType(r.getRecordType());
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.util.List;
|
||||
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.STOPPING;
|
||||
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_OLD_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.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.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
@@ -52,6 +56,7 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final EventBus eventBus;
|
||||
private final Clock clock;
|
||||
private final List<Service> services;
|
||||
private final List<OpenDatabaseHook> openDatabaseHooks;
|
||||
private final List<ExecutorService> executors;
|
||||
@@ -63,9 +68,11 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
private volatile LifecycleState state = STARTING;
|
||||
|
||||
@Inject
|
||||
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus) {
|
||||
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
|
||||
Clock clock) {
|
||||
this.db = db;
|
||||
this.eventBus = eventBus;
|
||||
this.clock = clock;
|
||||
services = new CopyOnWriteArrayList<>();
|
||||
openDatabaseHooks = new CopyOnWriteArrayList<>();
|
||||
executors = new CopyOnWriteArrayList<>();
|
||||
@@ -99,6 +106,13 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
LOG.info("Already starting or stopping");
|
||||
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 {
|
||||
LOG.info("Opening database");
|
||||
long start = now();
|
||||
|
||||
@@ -0,0 +1,634 @@
|
||||
package org.briarproject.bramble.plugin.bluetooth;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Multiset;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
|
||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStoppedListeningEvent;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.Backoff;
|
||||
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||
import org.briarproject.bramble.api.plugin.PluginException;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.bramble.api.properties.event.RemoteTransportPropertiesUpdatedEvent;
|
||||
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
||||
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_ADDRESS_IS_REFLECTED;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_EVER_CONNECTED;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_PLUGIN_ENABLE;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_ADDRESS_IS_REFLECTED;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_EVER_CONNECTED;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
||||
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.REFLECTED_PROPERTY_PREFIX;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
import static org.briarproject.bramble.util.StringUtils.macToBytes;
|
||||
import static org.briarproject.bramble.util.StringUtils.macToString;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
|
||||
EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(AbstractBluetoothPlugin.class.getName());
|
||||
|
||||
private final BluetoothConnectionLimiter connectionLimiter;
|
||||
final BluetoothConnectionFactory<S> connectionFactory;
|
||||
|
||||
private final Executor ioExecutor, wakefulIoExecutor;
|
||||
private final SecureRandom secureRandom;
|
||||
private final Backoff backoff;
|
||||
private final PluginCallback callback;
|
||||
private final long maxLatency;
|
||||
private final int maxIdleTime;
|
||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||
private final AtomicBoolean everConnected = new AtomicBoolean(false);
|
||||
|
||||
protected final PluginState state = new PluginState();
|
||||
protected final Semaphore discoverSemaphore = new Semaphore(1);
|
||||
|
||||
private volatile String contactConnectionsUuid = null;
|
||||
|
||||
abstract void initialiseAdapter() throws IOException;
|
||||
|
||||
abstract boolean isAdapterEnabled();
|
||||
|
||||
/**
|
||||
* Returns the local Bluetooth address, or null if no valid address can
|
||||
* be found.
|
||||
*/
|
||||
@Nullable
|
||||
abstract String getBluetoothAddress();
|
||||
|
||||
abstract SS openServerSocket(String uuid) throws IOException;
|
||||
|
||||
abstract void tryToClose(@Nullable SS ss);
|
||||
|
||||
abstract DuplexTransportConnection acceptConnection(SS ss)
|
||||
throws IOException;
|
||||
|
||||
abstract boolean isValidAddress(String address);
|
||||
|
||||
abstract DuplexTransportConnection connectTo(String address, String uuid)
|
||||
throws IOException;
|
||||
|
||||
@Nullable
|
||||
abstract DuplexTransportConnection discoverAndConnect(String uuid);
|
||||
|
||||
AbstractBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
||||
BluetoothConnectionFactory<S> connectionFactory,
|
||||
Executor ioExecutor,
|
||||
Executor wakefulIoExecutor,
|
||||
SecureRandom secureRandom,
|
||||
Backoff backoff,
|
||||
PluginCallback callback,
|
||||
long maxLatency,
|
||||
int maxIdleTime) {
|
||||
this.connectionLimiter = connectionLimiter;
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.wakefulIoExecutor = wakefulIoExecutor;
|
||||
this.secureRandom = secureRandom;
|
||||
this.backoff = backoff;
|
||||
this.callback = callback;
|
||||
this.maxLatency = maxLatency;
|
||||
this.maxIdleTime = maxIdleTime;
|
||||
}
|
||||
|
||||
void onAdapterEnabled() {
|
||||
LOG.info("Bluetooth enabled");
|
||||
// We may not have been able to get the local address before
|
||||
ioExecutor.execute(this::updateProperties);
|
||||
if (getState() == INACTIVE) bind();
|
||||
}
|
||||
|
||||
void onAdapterDisabled() {
|
||||
LOG.info("Bluetooth disabled");
|
||||
connectionLimiter.allConnectionsClosed();
|
||||
// The server socket may not have been closed automatically
|
||||
SS ss = state.clearServerSocket();
|
||||
if (ss != null) {
|
||||
LOG.info("Closing server socket");
|
||||
tryToClose(ss);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportId getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxLatency() {
|
||||
return maxLatency;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxIdleTime() {
|
||||
return maxIdleTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws PluginException {
|
||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||
Settings settings = callback.getSettings();
|
||||
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE,
|
||||
DEFAULT_PREF_PLUGIN_ENABLE);
|
||||
everConnected.set(settings.getBoolean(PREF_EVER_CONNECTED,
|
||||
DEFAULT_PREF_EVER_CONNECTED));
|
||||
state.setStarted(enabledByUser);
|
||||
try {
|
||||
initialiseAdapter();
|
||||
} catch (IOException e) {
|
||||
throw new PluginException(e);
|
||||
}
|
||||
updateProperties();
|
||||
if (enabledByUser && isAdapterEnabled()) bind();
|
||||
}
|
||||
|
||||
private void bind() {
|
||||
ioExecutor.execute(() -> {
|
||||
if (getState() != INACTIVE) return;
|
||||
// Bind a server socket to accept connections from contacts
|
||||
SS ss;
|
||||
try {
|
||||
ss = openServerSocket(contactConnectionsUuid);
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return;
|
||||
}
|
||||
if (!state.setServerSocket(ss)) {
|
||||
LOG.info("Closing redundant server socket");
|
||||
tryToClose(ss);
|
||||
return;
|
||||
}
|
||||
backoff.reset();
|
||||
acceptContactConnections(ss);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateProperties() {
|
||||
TransportProperties p = callback.getLocalProperties();
|
||||
String address = p.get(PROP_ADDRESS);
|
||||
String uuid = p.get(PROP_UUID);
|
||||
Settings s = callback.getSettings();
|
||||
boolean isReflected = s.getBoolean(PREF_ADDRESS_IS_REFLECTED,
|
||||
DEFAULT_PREF_ADDRESS_IS_REFLECTED);
|
||||
boolean changed = false;
|
||||
if (address == null || isReflected) {
|
||||
address = getBluetoothAddress();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Local address " + scrubMacAddress(address));
|
||||
}
|
||||
if (address == null) {
|
||||
if (everConnected.get()) {
|
||||
address = getReflectedAddress();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Reflected address " +
|
||||
scrubMacAddress(address));
|
||||
}
|
||||
if (address != null) {
|
||||
changed = true;
|
||||
isReflected = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
changed = true;
|
||||
isReflected = false;
|
||||
}
|
||||
}
|
||||
if (uuid == null) {
|
||||
byte[] random = new byte[UUID_BYTES];
|
||||
secureRandom.nextBytes(random);
|
||||
uuid = UUID.nameUUIDFromBytes(random).toString();
|
||||
changed = true;
|
||||
}
|
||||
contactConnectionsUuid = uuid;
|
||||
if (changed) {
|
||||
p = new TransportProperties();
|
||||
// If we previously used a reflected address and there's no longer
|
||||
// a reflected address with enough votes to be used, we'll continue
|
||||
// to use the old reflected address until there's a new winner
|
||||
if (address != null) p.put(PROP_ADDRESS, address);
|
||||
p.put(PROP_UUID, uuid);
|
||||
callback.mergeLocalProperties(p);
|
||||
s = new Settings();
|
||||
s.putBoolean(PREF_ADDRESS_IS_REFLECTED, isReflected);
|
||||
callback.mergeSettings(s);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getReflectedAddress() {
|
||||
// Count the number of votes for each reflected address
|
||||
String key = REFLECTED_PROPERTY_PREFIX + PROP_ADDRESS;
|
||||
Multiset<String> votes = new Multiset<>();
|
||||
for (TransportProperties p : callback.getRemoteProperties()) {
|
||||
String address = p.get(key);
|
||||
if (address != null && isValidAddress(address)) votes.add(address);
|
||||
}
|
||||
// If an address gets more than half of the votes, accept it
|
||||
int total = votes.getTotal();
|
||||
for (String address : votes.keySet()) {
|
||||
if (votes.getCount(address) * 2 > total) return address;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void acceptContactConnections(SS ss) {
|
||||
while (true) {
|
||||
DuplexTransportConnection conn;
|
||||
try {
|
||||
conn = acceptConnection(ss);
|
||||
} catch (IOException e) {
|
||||
// This is expected when the server socket is closed
|
||||
LOG.info("Server socket closed");
|
||||
state.clearServerSocket();
|
||||
return;
|
||||
}
|
||||
LOG.info("Connection received");
|
||||
connectionLimiter.connectionOpened(conn);
|
||||
backoff.reset();
|
||||
setEverConnected();
|
||||
callback.handleConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
private void setEverConnected() {
|
||||
if (!everConnected.getAndSet(true)) {
|
||||
ioExecutor.execute(() -> {
|
||||
Settings s = new Settings();
|
||||
s.putBoolean(PREF_EVER_CONNECTED, true);
|
||||
callback.mergeSettings(s);
|
||||
// Contacts may already have sent a reflected address
|
||||
updateProperties();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
SS ss = state.setStopped();
|
||||
tryToClose(ss);
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return state.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReasonsDisabled() {
|
||||
return state.getReasonsDisabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPoll() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPollingInterval() {
|
||||
return backoff.getPollingInterval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||
properties) {
|
||||
if (getState() != ACTIVE) return;
|
||||
backoff.increment();
|
||||
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
||||
connect(p.getFirst(), p.getSecond());
|
||||
}
|
||||
}
|
||||
|
||||
private void connect(TransportProperties p, ConnectionHandler h) {
|
||||
String address = p.get(PROP_ADDRESS);
|
||||
if (isNullOrEmpty(address)) return;
|
||||
String uuid = p.get(PROP_UUID);
|
||||
if (isNullOrEmpty(uuid)) return;
|
||||
wakefulIoExecutor.execute(() -> {
|
||||
DuplexTransportConnection d = createConnection(p);
|
||||
if (d != null) {
|
||||
backoff.reset();
|
||||
setEverConnected();
|
||||
h.handleConnection(d);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DuplexTransportConnection connect(String address, String uuid) {
|
||||
// Validate the address
|
||||
if (!isValidAddress(address)) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
// Not scrubbing here to be able to figure out the problem
|
||||
LOG.warning("Invalid address " + address);
|
||||
return null;
|
||||
}
|
||||
// Validate the UUID
|
||||
try {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
UUID.fromString(uuid);
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.warning("Invalid UUID " + uuid);
|
||||
return null;
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connecting to " + scrubMacAddress(address));
|
||||
try {
|
||||
DuplexTransportConnection conn = connectTo(address, uuid);
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connected to " + scrubMacAddress(address));
|
||||
return conn;
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Could not connect to " + scrubMacAddress(address));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DuplexTransportConnection createConnection(TransportProperties p) {
|
||||
if (getState() != ACTIVE) return null;
|
||||
if (!connectionLimiter.canOpenContactConnection()) return null;
|
||||
String address = p.get(PROP_ADDRESS);
|
||||
if (isNullOrEmpty(address)) return null;
|
||||
String uuid = p.get(PROP_UUID);
|
||||
if (isNullOrEmpty(uuid)) return null;
|
||||
DuplexTransportConnection conn = connect(address, uuid);
|
||||
if (conn != null) connectionLimiter.connectionOpened(conn);
|
||||
return conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsKeyAgreement() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
|
||||
if (getState() != ACTIVE) return null;
|
||||
// No truncation necessary because COMMIT_LENGTH = 16
|
||||
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
|
||||
// Bind a server socket for receiving key agreement connections
|
||||
SS ss;
|
||||
try {
|
||||
ss = openServerSocket(uuid);
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return null;
|
||||
}
|
||||
if (getState() != ACTIVE) {
|
||||
tryToClose(ss);
|
||||
return null;
|
||||
}
|
||||
BdfList descriptor = new BdfList();
|
||||
descriptor.add(TRANSPORT_ID_BLUETOOTH);
|
||||
String address = getBluetoothAddress();
|
||||
if (address != null) descriptor.add(macToBytes(address));
|
||||
return new BluetoothKeyAgreementListener(descriptor, ss);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DuplexTransportConnection createKeyAgreementConnection(
|
||||
byte[] commitment, BdfList descriptor) {
|
||||
if (getState() != ACTIVE) return null;
|
||||
// No truncation necessary because COMMIT_LENGTH = 16
|
||||
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
||||
DuplexTransportConnection conn;
|
||||
if (descriptor.size() == 1) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Discovering address for key agreement UUID " +
|
||||
uuid);
|
||||
}
|
||||
conn = discoverAndConnect(uuid);
|
||||
} else {
|
||||
String address;
|
||||
try {
|
||||
address = parseAddress(descriptor);
|
||||
} catch (FormatException e) {
|
||||
LOG.info("Invalid address in key agreement descriptor");
|
||||
return null;
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connecting to key agreement UUID " + uuid);
|
||||
conn = connect(address, uuid);
|
||||
}
|
||||
if (conn != null) {
|
||||
connectionLimiter.connectionOpened(conn);
|
||||
setEverConnected();
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
private String parseAddress(BdfList descriptor) throws FormatException {
|
||||
byte[] mac = descriptor.getRaw(1);
|
||||
if (mac.length != 6) throw new FormatException();
|
||||
return macToString(mac);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDiscovering() {
|
||||
return discoverSemaphore.availablePermits() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePolling() {
|
||||
connectionLimiter.startLimiting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enablePolling() {
|
||||
connectionLimiter.endLimiting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DuplexTransportConnection discoverAndConnectForSetup(String uuid) {
|
||||
DuplexTransportConnection conn = discoverAndConnect(uuid);
|
||||
if (conn != null) {
|
||||
connectionLimiter.connectionOpened(conn);
|
||||
setEverConnected();
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRendezvous() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RendezvousEndpoint createRendezvousEndpoint(KeyMaterialSource k,
|
||||
boolean alice, ConnectionHandler incoming) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof SettingsUpdatedEvent) {
|
||||
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
||||
if (s.getNamespace().equals(ID.getString()))
|
||||
ioExecutor.execute(() -> onSettingsUpdated(s.getSettings()));
|
||||
} else if (e instanceof KeyAgreementListeningEvent) {
|
||||
connectionLimiter.startLimiting();
|
||||
} else if (e instanceof KeyAgreementStoppedListeningEvent) {
|
||||
connectionLimiter.endLimiting();
|
||||
} else if (e instanceof RemoteTransportPropertiesUpdatedEvent) {
|
||||
RemoteTransportPropertiesUpdatedEvent r =
|
||||
(RemoteTransportPropertiesUpdatedEvent) e;
|
||||
if (r.getTransportId().equals(ID)) {
|
||||
ioExecutor.execute(this::updateProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
private void onSettingsUpdated(Settings settings) {
|
||||
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE,
|
||||
DEFAULT_PREF_PLUGIN_ENABLE);
|
||||
SS ss = state.setEnabledByUser(enabledByUser);
|
||||
State s = getState();
|
||||
if (ss != null) {
|
||||
LOG.info("Disabled by user, closing server socket");
|
||||
tryToClose(ss);
|
||||
} else if (s == INACTIVE) {
|
||||
if (isAdapterEnabled()) {
|
||||
LOG.info("Enabled by user, opening server socket");
|
||||
bind();
|
||||
} else {
|
||||
LOG.info("Enabled by user but adapter is disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
|
||||
|
||||
private final SS ss;
|
||||
|
||||
private BluetoothKeyAgreementListener(BdfList descriptor, SS ss) {
|
||||
super(descriptor);
|
||||
this.ss = ss;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyAgreementConnection accept() throws IOException {
|
||||
DuplexTransportConnection conn = acceptConnection(ss);
|
||||
if (LOG.isLoggable(INFO)) LOG.info(ID + ": Incoming connection");
|
||||
connectionLimiter.connectionOpened(conn);
|
||||
return new KeyAgreementConnection(conn, ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
tryToClose(ss);
|
||||
}
|
||||
}
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
private class PluginState {
|
||||
|
||||
@GuardedBy("this")
|
||||
private boolean started = false,
|
||||
stopped = false,
|
||||
enabledByUser = false;
|
||||
|
||||
@GuardedBy("this")
|
||||
@Nullable
|
||||
private SS serverSocket = null;
|
||||
|
||||
private synchronized void setStarted(boolean enabledByUser) {
|
||||
started = true;
|
||||
this.enabledByUser = enabledByUser;
|
||||
callback.pluginStateChanged(getState());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private synchronized SS setStopped() {
|
||||
stopped = true;
|
||||
SS ss = serverSocket;
|
||||
serverSocket = null;
|
||||
callback.pluginStateChanged(getState());
|
||||
return ss;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private synchronized SS setEnabledByUser(boolean enabledByUser) {
|
||||
this.enabledByUser = enabledByUser;
|
||||
SS ss = null;
|
||||
if (!enabledByUser) {
|
||||
ss = serverSocket;
|
||||
serverSocket = null;
|
||||
}
|
||||
callback.pluginStateChanged(getState());
|
||||
return ss;
|
||||
}
|
||||
|
||||
private synchronized boolean setServerSocket(SS ss) {
|
||||
if (stopped || serverSocket != null) return false;
|
||||
serverSocket = ss;
|
||||
callback.pluginStateChanged(getState());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private synchronized SS clearServerSocket() {
|
||||
SS ss = serverSocket;
|
||||
serverSocket = null;
|
||||
callback.pluginStateChanged(getState());
|
||||
return ss;
|
||||
}
|
||||
|
||||
private synchronized State getState() {
|
||||
if (!started || stopped) return STARTING_STOPPING;
|
||||
if (!enabledByUser) return DISABLED;
|
||||
return serverSocket == null ? INACTIVE : ACTIVE;
|
||||
}
|
||||
|
||||
private synchronized int getReasonsDisabled() {
|
||||
return getState() == DISABLED ? REASON_USER : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,14 +7,15 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
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
|
||||
|
||||
@@ -30,34 +30,37 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
|
||||
private final List<DuplexTransportConnection> connections =
|
||||
new LinkedList<>();
|
||||
@GuardedBy("lock")
|
||||
private boolean keyAgreementInProgress = false;
|
||||
private int limitingInProgress = 0;
|
||||
|
||||
BluetoothConnectionLimiterImpl(EventBus eventBus) {
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyAgreementStarted() {
|
||||
public void startLimiting() {
|
||||
synchronized (lock) {
|
||||
keyAgreementInProgress = true;
|
||||
limitingInProgress++;
|
||||
}
|
||||
LOG.info("Key agreement started");
|
||||
LOG.info("Limiting started");
|
||||
eventBus.broadcast(new CloseSyncConnectionsEvent(ID));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyAgreementEnded() {
|
||||
public void endLimiting() {
|
||||
synchronized (lock) {
|
||||
keyAgreementInProgress = false;
|
||||
limitingInProgress--;
|
||||
if (limitingInProgress < 0) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
LOG.info("Key agreement ended");
|
||||
LOG.info("Limiting ended");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOpenContactConnection() {
|
||||
synchronized (lock) {
|
||||
if (keyAgreementInProgress) {
|
||||
LOG.info("Can't open contact connection during key agreement");
|
||||
if (limitingInProgress > 0) {
|
||||
LOG.info("Can't open contact connection while limiting");
|
||||
return false;
|
||||
} else {
|
||||
LOG.info("Can open contact connection");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user