mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
556 Commits
limit-in-m
...
alpha-1.3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
62cca1335f | ||
|
|
11a18859fb | ||
|
|
1116a7e125 | ||
|
|
415b315292 | ||
|
|
9818ec2b66 | ||
|
|
95ef061a34 | ||
|
|
aaaf8aa66f | ||
|
|
29965e38d0 | ||
|
|
371d49a213 | ||
|
|
6ed95e145e | ||
|
|
8c025c1173 | ||
|
|
9ce541cc31 | ||
|
|
aa57a4c123 | ||
|
|
58d9deb3b8 | ||
|
|
f0685c4a43 | ||
|
|
484817db08 | ||
|
|
670bf15d31 | ||
|
|
6df1e0fd77 | ||
|
|
ec910cb80f | ||
|
|
372516646d | ||
|
|
72e721b0d3 | ||
|
|
6599093611 | ||
|
|
dceeecf1fe | ||
|
|
ace0b9a3d8 | ||
|
|
7c45c90de9 | ||
|
|
c2a4b5e26a | ||
|
|
feac0ad802 | ||
|
|
60478eba3f | ||
|
|
3639952612 | ||
|
|
c4a654b267 | ||
|
|
ecb31a4d32 | ||
|
|
76f201bb2f | ||
|
|
1d44305e34 | ||
|
|
a37af592cd | ||
|
|
87799b743c | ||
|
|
b898a7c370 | ||
|
|
7f486eef4c | ||
|
|
f3210e3af2 | ||
|
|
225fd6fd49 | ||
|
|
400d259a60 | ||
|
|
4074ac8578 | ||
|
|
b2e6dd4138 | ||
|
|
b608b42174 | ||
|
|
f603254153 | ||
|
|
c851dd228b | ||
|
|
e97478a21a | ||
|
|
726ebcea3f | ||
|
|
2f969775d8 | ||
|
|
d3b855318c | ||
|
|
95104d3383 | ||
|
|
6860a04e8b | ||
|
|
33c24f8655 | ||
|
|
1fa4b78474 | ||
|
|
b678de7529 | ||
|
|
ab1ed0ff5a | ||
|
|
ad20e5230a | ||
|
|
bcc0442add | ||
|
|
700f6e05bf | ||
|
|
d8327d6de2 | ||
|
|
5a55b3d7e3 | ||
|
|
bed87ed439 | ||
|
|
6d1f1c7852 | ||
|
|
f6b3bde724 | ||
|
|
94ec22bef8 | ||
|
|
ae923e5777 | ||
|
|
46b4204805 | ||
|
|
2257c005b3 | ||
|
|
eb9ff9c954 | ||
|
|
4f08f81779 | ||
|
|
2b0815aaac | ||
|
|
a9e83491d3 | ||
|
|
ee967c5d8f | ||
|
|
43740777d4 | ||
|
|
d5b0556ea2 | ||
|
|
227f00c10c | ||
|
|
8b4ff2dc8a | ||
|
|
4be2afb915 | ||
|
|
74447b8ec3 | ||
|
|
d95242bd7e | ||
|
|
51794424ce | ||
|
|
5db099bae6 | ||
|
|
a2faa3bd3b | ||
|
|
a3fb7b5680 | ||
|
|
264d110dbd | ||
|
|
839b871a45 | ||
|
|
2fb4825b8f | ||
|
|
3f9a66b1b6 | ||
|
|
d796916387 | ||
|
|
fe07b760ea | ||
|
|
b4a5fe6772 | ||
|
|
e21e6267d7 | ||
|
|
d7afbdf690 | ||
|
|
c5d2661c1d | ||
|
|
b738bdd14e | ||
|
|
629cff20a3 | ||
|
|
6cfb70db95 | ||
|
|
737ecfb620 | ||
|
|
5a424b178e | ||
|
|
59f4e7c34a | ||
|
|
2480824d69 | ||
|
|
a6c2000d81 | ||
|
|
a38a3139d9 | ||
|
|
4c8adaa02b | ||
|
|
8a534b4503 | ||
|
|
e5b2275c82 | ||
|
|
5159593825 | ||
|
|
a546fecc01 | ||
|
|
3e7e37f5f6 | ||
|
|
d095ba0b15 | ||
|
|
7fab97d26c | ||
|
|
6fbc82ee27 | ||
|
|
885b03cfd7 | ||
|
|
f81bfcafeb | ||
|
|
f36f1cf3d4 | ||
|
|
7d6a63d866 | ||
|
|
15ebdf8dd5 | ||
|
|
db2c235283 | ||
|
|
6b61725c6a | ||
|
|
e5bd43469e | ||
|
|
9366c184d8 | ||
|
|
73d2c964d4 | ||
|
|
fb2b4209cf | ||
|
|
a04b512497 | ||
|
|
3d9515e308 | ||
|
|
1b19b331b1 | ||
|
|
d151a2d7f7 | ||
|
|
9712a4b849 | ||
|
|
cf1ac5e3e5 | ||
|
|
cb859e998d | ||
|
|
0b9345f867 | ||
|
|
12988120d1 | ||
|
|
8d6c866e62 | ||
|
|
8f82cf3c73 | ||
|
|
21112ce092 | ||
|
|
21ee3ea00d | ||
|
|
bb964101b3 | ||
|
|
d796eff0f6 | ||
|
|
700ea2b387 | ||
|
|
e4a66615a7 | ||
|
|
6e3a7d8d0c | ||
|
|
166b5d4add | ||
|
|
0fd59a26f6 | ||
|
|
4162bf990a | ||
|
|
09cfadbf7e | ||
|
|
3fb27dbb12 | ||
|
|
ae4a04bada | ||
|
|
831c65b647 | ||
|
|
afcd38b84c | ||
|
|
d670179e30 | ||
|
|
998c435b13 | ||
|
|
4a0327a62b | ||
|
|
70532732c8 | ||
|
|
d69406dfe3 | ||
|
|
98619df867 | ||
|
|
f2eca0fdb6 | ||
|
|
c62a57e8b2 | ||
|
|
239c4a27ad | ||
|
|
e5d78a858d | ||
|
|
5c1bcdeb9d | ||
|
|
6c1f5450cb | ||
|
|
0d070cf422 | ||
|
|
d34d66c691 | ||
|
|
6005d156eb | ||
|
|
635008fb60 | ||
|
|
b78569119a | ||
|
|
8372bb01b2 | ||
|
|
766718e75c | ||
|
|
1c107a851b | ||
|
|
db53e79d1d | ||
|
|
21e56284fb | ||
|
|
d393b79ced | ||
|
|
6611d7c02e | ||
|
|
ab43dd4986 | ||
|
|
36a9174781 | ||
|
|
94dd75f24b | ||
|
|
c93e5441b0 | ||
|
|
8ec8cc927b | ||
|
|
4663e727eb | ||
|
|
e2acd19ffd | ||
|
|
0befa6a823 | ||
|
|
01083f47ea | ||
|
|
a349bd146c | ||
|
|
4ffa9e191c | ||
|
|
e616fc3da7 | ||
|
|
aed5ac5bb4 | ||
|
|
cae53a9fcc | ||
|
|
6660625ba6 | ||
|
|
bf9ba13b68 | ||
|
|
a52c97ecf7 | ||
|
|
a2174e7677 | ||
|
|
d3cf3d680e | ||
|
|
cbb87aa00c | ||
|
|
53d985161f | ||
|
|
86002b0402 | ||
|
|
f75e789493 | ||
|
|
b22f302fdd | ||
|
|
c4a42760c8 | ||
|
|
8d92f36522 | ||
|
|
6c86873ea7 | ||
|
|
4fa9d654b5 | ||
|
|
3d303ccad5 | ||
|
|
b0d99a9f33 | ||
|
|
1a5e789bec | ||
|
|
97040c6299 | ||
|
|
301085c685 | ||
|
|
946c79d918 | ||
|
|
20418cfc7f | ||
|
|
7b09f0f98d | ||
|
|
97a7c8824b | ||
|
|
423684a14f | ||
|
|
09d91b522f | ||
|
|
64c0e9e9e4 | ||
|
|
15021bffef | ||
|
|
43c6ae4258 | ||
|
|
f819930570 | ||
|
|
aa00ba7220 | ||
|
|
19db58ee19 | ||
|
|
05f4d63356 | ||
|
|
6e5af2d3d3 | ||
|
|
00bf1eac0a | ||
|
|
8a10f16861 | ||
|
|
9bd7214d1d | ||
|
|
fce1247aa6 | ||
|
|
990f983ea9 | ||
|
|
6e57d7bb42 | ||
|
|
1b0cb532de | ||
|
|
fe7121b4ec | ||
|
|
5aa041f9e1 | ||
|
|
6939d8d230 | ||
|
|
c3cea37641 | ||
|
|
d0d2e0ed82 | ||
|
|
cf8f5c989f | ||
|
|
8b45e01c42 | ||
|
|
ec972e8a1d | ||
|
|
100791c3f3 | ||
|
|
83ac866cc1 | ||
|
|
ef9b22670d | ||
|
|
186ac30f37 | ||
|
|
5aa24414c6 | ||
|
|
dd6d72ed30 | ||
|
|
4344be2ca0 | ||
|
|
1e94af3ef3 | ||
|
|
cb69340749 | ||
|
|
f3d068414b | ||
|
|
5fdc7e7cc4 | ||
|
|
7569d5ffb3 | ||
|
|
deca5d56cc | ||
|
|
3d6b48bb34 | ||
|
|
0dc631b7a8 | ||
|
|
921e952b05 | ||
|
|
3b02797639 | ||
|
|
e2e67edbbe | ||
|
|
a9cd40faeb | ||
|
|
dd3c19aba2 | ||
|
|
e8ede55422 | ||
|
|
04517e942e | ||
|
|
9a25ad892d | ||
|
|
3457d8f9ab | ||
|
|
5fb2624ffa | ||
|
|
ed9a7bec2c | ||
|
|
ff70315d5c | ||
|
|
f197243273 | ||
|
|
6409a3b179 | ||
|
|
f882e46b33 | ||
|
|
efa63c306a | ||
|
|
205b4f77b2 | ||
|
|
015ecb1d99 | ||
|
|
fd86b73626 | ||
|
|
9048392d4e | ||
|
|
480aaaa35e | ||
|
|
002feb8e29 | ||
|
|
c6ba2b037a | ||
|
|
98788c7c80 | ||
|
|
e6f66ebc95 | ||
|
|
04485e58da | ||
|
|
97118fd92b | ||
|
|
ac4fbf202f | ||
|
|
b81495eac1 | ||
|
|
db90f75d2e | ||
|
|
bed3abfd40 | ||
|
|
0967f6c48e | ||
|
|
f9a8fcb207 | ||
|
|
eb3c2a3566 | ||
|
|
8d735b3023 | ||
|
|
b24a0e4bc3 | ||
|
|
07da91a6f5 | ||
|
|
e4e0e712dc | ||
|
|
9294794448 | ||
|
|
5a9958793d | ||
|
|
651d2ca377 | ||
|
|
ecd64f08cd | ||
|
|
f3bffb6aa6 | ||
|
|
33331dee3e | ||
|
|
641525fa74 | ||
|
|
4b82079e33 | ||
|
|
caa55ffa14 | ||
|
|
47ae594921 | ||
|
|
a17b154024 | ||
|
|
02ee678bab | ||
|
|
f6bdbb1b80 | ||
|
|
64e1975cf1 | ||
|
|
993502add0 | ||
|
|
54893d2716 | ||
|
|
84657127b8 | ||
|
|
01a146ba71 | ||
|
|
a30e5b672e | ||
|
|
edb584dc3b | ||
|
|
12a8907c8b | ||
|
|
e0f381a973 | ||
|
|
61d3d133e8 | ||
|
|
0caa522f07 | ||
|
|
948212103c | ||
|
|
ce1a57c2b4 | ||
|
|
922a52bf83 | ||
|
|
8cbb38ee68 | ||
|
|
1c4cf7d771 | ||
|
|
090a1bd84e | ||
|
|
44f6f5d416 | ||
|
|
b88f012880 | ||
|
|
93f434e54b | ||
|
|
92f4a3a404 | ||
|
|
c017a813b0 | ||
|
|
6c6dbfd357 | ||
|
|
1f246637e2 | ||
|
|
1ac17cf859 | ||
|
|
0a3ff41feb | ||
|
|
9738dd2838 | ||
|
|
be0e21d39b | ||
|
|
6a2c2bed0f | ||
|
|
de9c6d4447 | ||
|
|
37a2d9f990 | ||
|
|
0e1fb406b5 | ||
|
|
b72e8fa490 | ||
|
|
f3157e5276 | ||
|
|
e2124ff3c9 | ||
|
|
66cc9d25e7 | ||
|
|
e9cdec95e0 | ||
|
|
63d3a78dda | ||
|
|
ccbe6d4bb8 | ||
|
|
54b852db70 | ||
|
|
8d55ea3f6f | ||
|
|
cf8241e79c | ||
|
|
61d3fe9055 | ||
|
|
bded1edb2b | ||
|
|
4d27828712 | ||
|
|
0f6f52c37a | ||
|
|
c1cf6f61b9 | ||
|
|
7c22016b81 | ||
|
|
31f42d44af | ||
|
|
a1cf485ecc | ||
|
|
b7d3cd7990 | ||
|
|
4122e0852a | ||
|
|
41411b0e2e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,6 +18,7 @@ local.properties
|
|||||||
|
|
||||||
# Android Studio
|
# Android Studio
|
||||||
.idea/*
|
.idea/*
|
||||||
|
!.idea/inspectionProfiles/
|
||||||
!.idea/runConfigurations/
|
!.idea/runConfigurations/
|
||||||
!.idea/codeStyleSettings.xml
|
!.idea/codeStyleSettings.xml
|
||||||
!.idea/codeStyles
|
!.idea/codeStyles
|
||||||
|
|||||||
@@ -1,29 +1,70 @@
|
|||||||
image: briar/ci-image-android:latest
|
image: briar/ci-image-android:latest
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
- check_reproducibility
|
- optional_tests
|
||||||
|
- check_reproducibility
|
||||||
|
|
||||||
test:
|
.base-test:
|
||||||
stage: test
|
|
||||||
before_script:
|
before_script:
|
||||||
- set -e
|
- set -e
|
||||||
- export GRADLE_USER_HOME=$PWD/.gradle
|
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
|
key: "$CI_COMMIT_REF_SLUG"
|
||||||
paths:
|
paths:
|
||||||
- .gradle/wrapper
|
- .gradle/wrapper
|
||||||
- .gradle/caches
|
- .gradle/caches
|
||||||
|
|
||||||
script:
|
|
||||||
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
|
|
||||||
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom check compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources
|
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
# these file change every time but should not be cached
|
# these file change every time and should not be cached
|
||||||
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
|
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
|
||||||
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
|
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
|
||||||
|
|
||||||
|
test:
|
||||||
|
extends: .base-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
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
|
when: always
|
||||||
|
- when: always
|
||||||
|
|
||||||
|
android test:
|
||||||
|
extends: .base-test
|
||||||
|
stage: optional_tests
|
||||||
|
image: briar/ci-image-android-emulator:latest
|
||||||
|
script:
|
||||||
|
# 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.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
|
||||||
|
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
|
||||||
|
|
||||||
test_reproducible:
|
test_reproducible:
|
||||||
stage: check_reproducibility
|
stage: check_reproducibility
|
||||||
@@ -31,3 +72,38 @@ test_reproducible:
|
|||||||
- "curl -X POST -F token=${RELEASE_CHECK_TOKEN} -F ref=master -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/61/trigger/pipeline"
|
- "curl -X POST -F token=${RELEASE_CHECK_TOKEN} -F ref=master -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/61/trigger/pipeline"
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
|
|
||||||
|
.optional_tests:
|
||||||
|
stage: optional_tests
|
||||||
|
before_script:
|
||||||
|
- set -e
|
||||||
|
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||||
|
|
||||||
|
cache:
|
||||||
|
key: "$CI_COMMIT_REF_SLUG"
|
||||||
|
paths:
|
||||||
|
- .gradle/wrapper
|
||||||
|
- .gradle/caches
|
||||||
|
|
||||||
|
script:
|
||||||
|
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
# these file change every time but should not be cached
|
||||||
|
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
|
||||||
|
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
|
||||||
|
|
||||||
|
bridge test:
|
||||||
|
extends: .optional_tests
|
||||||
|
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
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
|||||||
14
.idea/codeStyles/Project.xml
generated
14
.idea/codeStyles/Project.xml
generated
@@ -28,6 +28,20 @@
|
|||||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||||
</JavaCodeStyleSettings>
|
</JavaCodeStyleSettings>
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
|
<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" />
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
</JetCodeStyleSettings>
|
</JetCodeStyleSettings>
|
||||||
<XML>
|
<XML>
|
||||||
|
|||||||
15
.idea/dictionaries/briar.xml
generated
Normal file
15
.idea/dictionaries/briar.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="briar">
|
||||||
|
<words>
|
||||||
|
<w>briar</w>
|
||||||
|
<w>briarproject</w>
|
||||||
|
<w>emoji</w>
|
||||||
|
<w>encrypter</w>
|
||||||
|
<w>identicon</w>
|
||||||
|
<w>introducee</w>
|
||||||
|
<w>introducees</w>
|
||||||
|
<w>introducer</w>
|
||||||
|
<w>onboarding</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
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>
|
||||||
3
.idea/runConfigurations/All_tests.xml
generated
3
.idea/runConfigurations/All_tests.xml
generated
@@ -1,6 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<module name="briar-android" />
|
<module name="briar.briar-android" />
|
||||||
<option name="PACKAGE_NAME" value="" />
|
<option name="PACKAGE_NAME" value="" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
<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-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-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 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-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 briar-headless" run_configuration_type="AndroidJUnit" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@@ -1,20 +1,14 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in bramble-android" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in bramble-android" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<module name="bramble-android" />
|
<module name="briar.bramble-android" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
|
||||||
<option name="PACKAGE_NAME" value="" />
|
<option name="PACKAGE_NAME" value="" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
<option name="TEST_OBJECT" value="package" />
|
<option name="TEST_OBJECT" value="package" />
|
||||||
<option name="VM_PARAMETERS" value="-ea" />
|
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-android" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-android" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<method v="2">
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
<value defaultName="singleModule" />
|
</method>
|
||||||
</option>
|
|
||||||
<patterns />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
14
.idea/runConfigurations/All_tests_in_bramble_api.xml
generated
14
.idea/runConfigurations/All_tests_in_bramble_api.xml
generated
@@ -1,20 +1,14 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<module name="bramble-api" />
|
<module name="briar.bramble-api" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
|
||||||
<option name="PACKAGE_NAME" value="" />
|
<option name="PACKAGE_NAME" value="" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
<option name="TEST_OBJECT" value="package" />
|
<option name="TEST_OBJECT" value="package" />
|
||||||
<option name="VM_PARAMETERS" value="-ea" />
|
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<method v="2">
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
<value defaultName="singleModule" />
|
</method>
|
||||||
</option>
|
|
||||||
<patterns />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
@@ -1,20 +1,14 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in bramble-core" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in bramble-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<module name="bramble-core" />
|
<module name="briar.bramble-core" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
|
||||||
<option name="PACKAGE_NAME" value="" />
|
<option name="PACKAGE_NAME" value="" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
<option name="TEST_OBJECT" value="package" />
|
<option name="TEST_OBJECT" value="package" />
|
||||||
<option name="VM_PARAMETERS" value="-ea" />
|
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<method v="2">
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
<value defaultName="singleModule" />
|
</method>
|
||||||
</option>
|
|
||||||
<patterns />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in bramble-java" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in bramble-java" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<module name="bramble-java" />
|
<module name="briar.bramble-java" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
|
||||||
<option name="PACKAGE_NAME" value="" />
|
<option name="PACKAGE_NAME" value="" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
@@ -10,11 +8,8 @@
|
|||||||
<option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
|
<option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-java" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-java" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<method v="2">
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
<value defaultName="singleModule" />
|
</method>
|
||||||
</option>
|
|
||||||
<patterns />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
@@ -1,20 +1,14 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<module name="briar-android" />
|
<module name="briar.briar-android" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
|
||||||
<option name="PACKAGE_NAME" value="" />
|
<option name="PACKAGE_NAME" value="" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
<option name="TEST_OBJECT" value="package" />
|
<option name="TEST_OBJECT" value="package" />
|
||||||
<option name="VM_PARAMETERS" value="-ea" />
|
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<method v="2">
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
<value defaultName="singleModule" />
|
</method>
|
||||||
</option>
|
|
||||||
<patterns />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
14
.idea/runConfigurations/All_tests_in_briar_api.xml
generated
Normal file
14
.idea/runConfigurations/All_tests_in_briar_api.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<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>
|
||||||
|
</component>
|
||||||
14
.idea/runConfigurations/All_tests_in_briar_core.xml
generated
14
.idea/runConfigurations/All_tests_in_briar_core.xml
generated
@@ -1,20 +1,14 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<module name="briar-core" />
|
<module name="briar.briar-core" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
|
||||||
<option name="PACKAGE_NAME" value="" />
|
<option name="PACKAGE_NAME" value="" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
<option name="TEST_OBJECT" value="package" />
|
<option name="TEST_OBJECT" value="package" />
|
||||||
<option name="VM_PARAMETERS" value="-ea" />
|
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<method v="2">
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
<value defaultName="singleModule" />
|
</method>
|
||||||
</option>
|
|
||||||
<patterns />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in briar-headless" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in briar-headless" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<module name="briar-headless" />
|
<module name="briar.briar-headless" />
|
||||||
<option name="PACKAGE_NAME" value="org.briarproject.briar.headless" />
|
<option name="PACKAGE_NAME" value="org.briarproject.briar.headless" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
|
|||||||
24
.idea/runConfigurations/BridgeTest.xml
generated
Normal file
24
.idea/runConfigurations/BridgeTest.xml
generated
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<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>
|
||||||
|
</component>
|
||||||
14
.idea/runConfigurations/H2_Performance_Test.xml
generated
14
.idea/runConfigurations/H2_Performance_Test.xml
generated
@@ -1,20 +1,14 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="H2 Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="H2 Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<module name="bramble-core" />
|
<module name="briar.bramble-core" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
|
||||||
<option name="PACKAGE_NAME" value="org.briarproject.bramble.db" />
|
<option name="PACKAGE_NAME" value="org.briarproject.bramble.db" />
|
||||||
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.db.H2DatabasePerformanceTest" />
|
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.db.H2DatabasePerformanceTest" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
<option name="TEST_OBJECT" value="class" />
|
<option name="TEST_OBJECT" value="class" />
|
||||||
<option name="VM_PARAMETERS" value="-ea" />
|
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="" />
|
<option name="WORKING_DIRECTORY" value="" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<method v="2">
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
<value defaultName="singleModule" />
|
</method>
|
||||||
</option>
|
|
||||||
<patterns />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
@@ -1,20 +1,14 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="HyperSQL Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="HyperSQL Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<module name="bramble-core" />
|
<module name="briar.bramble-core" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
|
||||||
<option name="PACKAGE_NAME" value="org.briarproject.bramble.db" />
|
<option name="PACKAGE_NAME" value="org.briarproject.bramble.db" />
|
||||||
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.db.HyperSqlDatabasePerformanceTest" />
|
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.db.HyperSqlDatabasePerformanceTest" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
<option name="TEST_OBJECT" value="class" />
|
<option name="TEST_OBJECT" value="class" />
|
||||||
<option name="VM_PARAMETERS" value="-ea" />
|
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="" />
|
<option name="WORKING_DIRECTORY" value="" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<method v="2">
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
<value defaultName="singleModule" />
|
</method>
|
||||||
</option>
|
|
||||||
<patterns />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
51
.idea/runConfigurations/Instrumentation_Tests__destroys_DB_.xml
generated
Normal file
51
.idea/runConfigurations/Instrumentation_Tests__destroys_DB_.xml
generated
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Instrumentation Tests (destroys DB)" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
|
||||||
|
<module name="briar.briar-android" />
|
||||||
|
<option name="TESTING_TYPE" value="1" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="CLASS_NAME" value="" />
|
||||||
|
<option name="PACKAGE_NAME" value="org.briarproject.briar.android" />
|
||||||
|
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
|
||||||
|
<option name="EXTRA_OPTIONS" value="-e notAnnotation androidx.test.filters.LargeTest" />
|
||||||
|
<option name="INCLUDE_GRADLE_EXTRA_OPTIONS" value="true" />
|
||||||
|
<option name="CLEAR_LOGCAT" value="false" />
|
||||||
|
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||||
|
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
|
||||||
|
<option name="FORCE_STOP_RUNNING_APP" value="true" />
|
||||||
|
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||||
|
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||||
|
<Auto>
|
||||||
|
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||||
|
<option name="SHOW_STATIC_VARS" value="true" />
|
||||||
|
<option name="WORKING_DIR" value="" />
|
||||||
|
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||||
|
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||||
|
</Auto>
|
||||||
|
<Hybrid>
|
||||||
|
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||||
|
<option name="SHOW_STATIC_VARS" value="true" />
|
||||||
|
<option name="WORKING_DIR" value="" />
|
||||||
|
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||||
|
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||||
|
</Hybrid>
|
||||||
|
<Java />
|
||||||
|
<Native>
|
||||||
|
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||||
|
<option name="SHOW_STATIC_VARS" value="true" />
|
||||||
|
<option name="WORKING_DIR" value="" />
|
||||||
|
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||||
|
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||||
|
</Native>
|
||||||
|
<Profilers>
|
||||||
|
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||||
|
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||||
|
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
||||||
|
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
|
||||||
|
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
||||||
|
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
||||||
|
</Profilers>
|
||||||
|
<method v="2">
|
||||||
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
9
.idea/runConfigurations/briar_headless.xml
generated
9
.idea/runConfigurations/briar_headless.xml
generated
@@ -1,6 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="briar-headless" type="JetRunConfigurationType" factoryName="Kotlin" singleton="true">
|
<configuration default="false" name="briar-headless" type="JetRunConfigurationType" singleton="true">
|
||||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
<module name="briar.briar-headless" />
|
||||||
<option name="VM_PARAMETERS" value="" />
|
<option name="VM_PARAMETERS" value="" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="-v" />
|
<option name="PROGRAM_PARAMETERS" value="-v" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
@@ -8,9 +8,8 @@
|
|||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="MAIN_CLASS_NAME" value="org.briarproject.briar.headless.MainKt" />
|
<option name="MAIN_CLASS_NAME" value="org.briarproject.briar.headless.MainKt" />
|
||||||
<option name="WORKING_DIRECTORY" value="" />
|
<option name="WORKING_DIRECTORY" value="" />
|
||||||
<module name="briar-headless" />
|
<method v="2">
|
||||||
<envs />
|
<option name="Make" enabled="true" />
|
||||||
<method>
|
|
||||||
<option name="Gradle.BeforeRunTask" enabled="true" tasks="jar" externalProjectPath="$PROJECT_DIR$/briar-headless" vmOptions="" scriptParameters="" />
|
<option name="Gradle.BeforeRunTask" enabled="true" tasks="jar" externalProjectPath="$PROJECT_DIR$/briar-headless" vmOptions="" scriptParameters="" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
1
bramble-android/.gitignore
vendored
1
bramble-android/.gitignore
vendored
@@ -3,3 +3,4 @@ gen
|
|||||||
build
|
build
|
||||||
.settings
|
.settings
|
||||||
src/main/res/raw/*.zip
|
src/main/res/raw/*.zip
|
||||||
|
src/main/jniLibs
|
||||||
@@ -5,14 +5,18 @@ apply plugin: 'witness'
|
|||||||
apply from: 'witness.gradle'
|
apply from: 'witness.gradle'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 30
|
||||||
buildToolsVersion '29.0.2'
|
buildToolsVersion '30.0.2'
|
||||||
|
|
||||||
|
packagingOptions {
|
||||||
|
doNotStrip '**/*.so'
|
||||||
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 28
|
targetSdkVersion 29
|
||||||
versionCode 10209
|
versionCode 10302
|
||||||
versionName "1.2.9"
|
versionName "1.3.2"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
@@ -38,8 +42,8 @@ configurations {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
implementation project(path: ':bramble-core', configuration: 'default')
|
||||||
tor 'org.briarproject:tor-android:0.3.5.10@zip'
|
tor 'org.briarproject:tor-android:0.3.5.13@zip'
|
||||||
tor 'org.briarproject:obfs4proxy-android:0.0.11-2@zip'
|
tor 'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a@zip'
|
||||||
|
|
||||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
|
||||||
|
|
||||||
@@ -53,10 +57,12 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def torBinariesDir = 'src/main/res/raw'
|
def torBinariesDir = 'src/main/res/raw'
|
||||||
|
def torLibsDir = 'src/main/jniLibs'
|
||||||
|
|
||||||
task cleanTorBinaries {
|
task cleanTorBinaries {
|
||||||
doLast {
|
doLast {
|
||||||
delete fileTree(torBinariesDir) { include '*.zip' }
|
delete fileTree(torBinariesDir) { include '*.zip' }
|
||||||
|
delete fileTree(torLibsDir) { include '**/*.so' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +73,36 @@ task unpackTorBinaries {
|
|||||||
copy {
|
copy {
|
||||||
from configurations.tor.collect { zipTree(it) }
|
from configurations.tor.collect { zipTree(it) }
|
||||||
into torBinariesDir
|
into torBinariesDir
|
||||||
// TODO: Remove after next Tor upgrade, which won't include non-PIE binaries
|
include 'geoip.zip'
|
||||||
include 'geoip.zip', '*_pie.zip'
|
}
|
||||||
|
configurations.tor.each { outer ->
|
||||||
|
zipTree(outer).each { inner ->
|
||||||
|
if (inner.name.endsWith('_arm_pie.zip')) {
|
||||||
|
copy {
|
||||||
|
from zipTree(inner)
|
||||||
|
into torLibsDir
|
||||||
|
rename '(.*)', 'armeabi-v7a/lib$1.so'
|
||||||
|
}
|
||||||
|
} else if (inner.name.endsWith('_arm64_pie.zip')) {
|
||||||
|
copy {
|
||||||
|
from zipTree(inner)
|
||||||
|
into torLibsDir
|
||||||
|
rename '(.*)', 'arm64-v8a/lib$1.so'
|
||||||
|
}
|
||||||
|
} else if (inner.name.endsWith('_x86_pie.zip')) {
|
||||||
|
copy {
|
||||||
|
from zipTree(inner)
|
||||||
|
into torLibsDir
|
||||||
|
rename '(.*)', 'x86/lib$1.so'
|
||||||
|
}
|
||||||
|
} else if (inner.name.endsWith('_x86_64_pie.zip')) {
|
||||||
|
copy {
|
||||||
|
from zipTree(inner)
|
||||||
|
into torLibsDir
|
||||||
|
rename '(.*)', 'x86_64/lib$1.so'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependsOn cleanTorBinaries
|
dependsOn cleanTorBinaries
|
||||||
@@ -76,5 +110,6 @@ task unpackTorBinaries {
|
|||||||
|
|
||||||
tasks.withType(MergeResources) {
|
tasks.withType(MergeResources) {
|
||||||
inputs.dir torBinariesDir
|
inputs.dir torBinariesDir
|
||||||
|
inputs.dir torLibsDir
|
||||||
dependsOn unpackTorBinaries
|
dependsOn unpackTorBinaries
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package org.briarproject.bramble.network;
|
package org.briarproject.bramble.network;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.LinkAddress;
|
||||||
|
import android.net.LinkProperties;
|
||||||
|
import android.net.Network;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
@@ -19,6 +23,11 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||||
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
|
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
|
||||||
|
|
||||||
|
import java.net.Inet4Address;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@@ -36,16 +45,22 @@ import static android.net.ConnectivityManager.TYPE_WIFI;
|
|||||||
import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION;
|
import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
|
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
|
||||||
|
import static java.net.NetworkInterface.getNetworkInterfaces;
|
||||||
|
import static java.util.Collections.list;
|
||||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class AndroidNetworkManager implements NetworkManager, Service {
|
class AndroidNetworkManager implements NetworkManager, Service {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(AndroidNetworkManager.class.getName());
|
getLogger(AndroidNetworkManager.class.getName());
|
||||||
|
|
||||||
// See android.net.wifi.WifiManager
|
// See android.net.wifi.WifiManager
|
||||||
private static final String WIFI_AP_STATE_CHANGED_ACTION =
|
private static final String WIFI_AP_STATE_CHANGED_ACTION =
|
||||||
@@ -54,7 +69,8 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
|||||||
private final TaskScheduler scheduler;
|
private final TaskScheduler scheduler;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final Executor eventExecutor;
|
private final Executor eventExecutor;
|
||||||
private final Context appContext;
|
private final Application app;
|
||||||
|
private final ConnectivityManager connectivityManager;
|
||||||
private final AtomicReference<Cancellable> connectivityCheck =
|
private final AtomicReference<Cancellable> connectivityCheck =
|
||||||
new AtomicReference<>();
|
new AtomicReference<>();
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
@@ -67,7 +83,9 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
|||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.eventExecutor = eventExecutor;
|
this.eventExecutor = eventExecutor;
|
||||||
this.appContext = app.getApplicationContext();
|
this.app = app;
|
||||||
|
connectivityManager = (ConnectivityManager)
|
||||||
|
requireNonNull(app.getSystemService(CONNECTIVITY_SERVICE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,24 +100,82 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
|||||||
filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
|
filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
|
||||||
filter.addAction(WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
|
filter.addAction(WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
|
||||||
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
|
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
|
||||||
appContext.registerReceiver(networkStateReceiver, filter);
|
app.registerReceiver(networkStateReceiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopService() {
|
public void stopService() {
|
||||||
if (networkStateReceiver != null)
|
if (networkStateReceiver != null)
|
||||||
appContext.unregisterReceiver(networkStateReceiver);
|
app.unregisterReceiver(networkStateReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetworkStatus getNetworkStatus() {
|
public NetworkStatus getNetworkStatus() {
|
||||||
ConnectivityManager cm = (ConnectivityManager)
|
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
|
||||||
appContext.getSystemService(CONNECTIVITY_SERVICE);
|
|
||||||
if (cm == null) throw new AssertionError();
|
|
||||||
NetworkInfo net = cm.getActiveNetworkInfo();
|
|
||||||
boolean connected = net != null && net.isConnected();
|
boolean connected = net != null && net.isConnected();
|
||||||
boolean wifi = connected && net.getType() == TYPE_WIFI;
|
boolean wifi = false, ipv6Only = false;
|
||||||
return new NetworkStatus(connected, wifi);
|
if (connected) {
|
||||||
|
wifi = net.getType() == TYPE_WIFI;
|
||||||
|
if (SDK_INT >= 23) ipv6Only = isActiveNetworkIpv6Only();
|
||||||
|
else ipv6Only = areAllAvailableNetworksIpv6Only();
|
||||||
|
}
|
||||||
|
return new NetworkStatus(connected, wifi, ipv6Only);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the
|
||||||
|
* {@link ConnectivityManager#getActiveNetwork() active network} has an
|
||||||
|
* IPv6 unicast address and no IPv4 addresses. The active network is
|
||||||
|
* assumed not to be a loopback interface.
|
||||||
|
*/
|
||||||
|
@TargetApi(23)
|
||||||
|
private boolean isActiveNetworkIpv6Only() {
|
||||||
|
Network net = connectivityManager.getActiveNetwork();
|
||||||
|
if (net == null) {
|
||||||
|
LOG.info("No active network");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LinkProperties props = connectivityManager.getLinkProperties(net);
|
||||||
|
if (props == null) {
|
||||||
|
LOG.info("No link properties for active network");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean hasIpv6Unicast = false;
|
||||||
|
for (LinkAddress linkAddress : props.getLinkAddresses()) {
|
||||||
|
InetAddress addr = linkAddress.getAddress();
|
||||||
|
if (addr instanceof Inet4Address) return false;
|
||||||
|
if (!addr.isMulticastAddress()) hasIpv6Unicast = true;
|
||||||
|
}
|
||||||
|
return hasIpv6Unicast;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the device has at least one network interface with an
|
||||||
|
* IPv6 unicast address and no interfaces with IPv4 addresses, excluding
|
||||||
|
* loopback interfaces and interfaces that are
|
||||||
|
* {@link NetworkInterface#isUp() down}. If this method returns true and
|
||||||
|
* the device has internet access then it's via IPv6 only.
|
||||||
|
*/
|
||||||
|
private boolean areAllAvailableNetworksIpv6Only() {
|
||||||
|
try {
|
||||||
|
Enumeration<NetworkInterface> interfaces = getNetworkInterfaces();
|
||||||
|
if (interfaces == null) {
|
||||||
|
LOG.info("No network interfaces");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean hasIpv6Unicast = false;
|
||||||
|
for (NetworkInterface i : list(interfaces)) {
|
||||||
|
if (i.isLoopback() || !i.isUp()) continue;
|
||||||
|
for (InetAddress addr : list(i.getInetAddresses())) {
|
||||||
|
if (addr instanceof Inet4Address) return false;
|
||||||
|
if (!addr.isMulticastAddress()) hasIpv6Unicast = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasIpv6Unicast;
|
||||||
|
} catch (SocketException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateConnectionStatus() {
|
private void updateConnectionStatus() {
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ class AndroidBluetoothPlugin
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
String getBluetoothAddress() {
|
String getBluetoothAddress() {
|
||||||
|
if (adapter == null) return null;
|
||||||
String address = AndroidUtils.getBluetoothAddress(app, adapter);
|
String address = AndroidUtils.getBluetoothAddress(app, adapter);
|
||||||
return address.isEmpty() ? null : address;
|
return address.isEmpty() ? null : address;
|
||||||
}
|
}
|
||||||
@@ -174,6 +175,11 @@ class AndroidBluetoothPlugin
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
IoUtils.tryToClose(s, LOG, WARNING);
|
IoUtils.tryToClose(s, LOG, WARNING);
|
||||||
throw e;
|
throw e;
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
// BluetoothSocket#connect() may throw an NPE under unknown
|
||||||
|
// circumstances
|
||||||
|
IoUtils.tryToClose(s, LOG, WARNING);
|
||||||
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,19 +16,42 @@ import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class AndroidTorPlugin extends TorPlugin {
|
class AndroidTorPlugin extends TorPlugin {
|
||||||
|
|
||||||
|
private static final List<String> LIBRARY_ARCHITECTURES =
|
||||||
|
asList("armeabi-v7a", "arm64-v8a", "x86", "x86_64");
|
||||||
|
|
||||||
|
private static final String TOR_LIB_NAME = "libtor.so";
|
||||||
|
private static final String OBFS4_LIB_NAME = "libobfs4proxy.so";
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(AndroidTorPlugin.class.getName());
|
||||||
|
|
||||||
private final Application app;
|
private final Application app;
|
||||||
private final AndroidWakeLock wakeLock;
|
private final AndroidWakeLock wakeLock;
|
||||||
|
private final File torLib, obfs4Lib;
|
||||||
|
|
||||||
AndroidTorPlugin(Executor ioExecutor,
|
AndroidTorPlugin(Executor ioExecutor,
|
||||||
Executor wakefulIoExecutor,
|
Executor wakefulIoExecutor,
|
||||||
@@ -55,6 +78,9 @@ class AndroidTorPlugin extends TorPlugin {
|
|||||||
maxIdleTime, torDirectory);
|
maxIdleTime, torDirectory);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
wakeLock = wakeLockManager.createWakeLock("TorPlugin");
|
wakeLock = wakeLockManager.createWakeLock("TorPlugin");
|
||||||
|
String nativeLibDir = app.getApplicationInfo().nativeLibraryDir;
|
||||||
|
torLib = new File(nativeLibDir, TOR_LIB_NAME);
|
||||||
|
obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -85,4 +111,112 @@ class AndroidTorPlugin extends TorPlugin {
|
|||||||
super.stop();
|
super.stop();
|
||||||
wakeLock.release();
|
wakeLock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected File getTorExecutableFile() {
|
||||||
|
return torLib.exists() ? torLib : super.getTorExecutableFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected File getObfs4ExecutableFile() {
|
||||||
|
return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installTorExecutable() throws IOException {
|
||||||
|
File extracted = super.getTorExecutableFile();
|
||||||
|
if (torLib.exists()) {
|
||||||
|
// If an older version left behind a Tor binary, delete it
|
||||||
|
if (extracted.exists()) {
|
||||||
|
if (extracted.delete()) LOG.info("Deleted Tor binary");
|
||||||
|
else LOG.info("Failed to delete Tor binary");
|
||||||
|
}
|
||||||
|
} else if (SDK_INT < 29) {
|
||||||
|
// The binary wasn't extracted at install time. Try to extract it
|
||||||
|
extractLibraryFromApk(TOR_LIB_NAME, extracted);
|
||||||
|
} else {
|
||||||
|
// No point extracting the binary, we won't be allowed to execute it
|
||||||
|
throw new FileNotFoundException(torLib.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installObfs4Executable() throws IOException {
|
||||||
|
File extracted = super.getObfs4ExecutableFile();
|
||||||
|
if (obfs4Lib.exists()) {
|
||||||
|
// If an older version left behind an obfs4 binary, delete it
|
||||||
|
if (extracted.exists()) {
|
||||||
|
if (extracted.delete()) LOG.info("Deleted obfs4 binary");
|
||||||
|
else LOG.info("Failed to delete obfs4 binary");
|
||||||
|
}
|
||||||
|
} else if (SDK_INT < 29) {
|
||||||
|
// The binary wasn't extracted at install time. Try to extract it
|
||||||
|
extractLibraryFromApk(OBFS4_LIB_NAME, extracted);
|
||||||
|
} else {
|
||||||
|
// No point extracting the binary, we won't be allowed to execute it
|
||||||
|
throw new FileNotFoundException(obfs4Lib.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractLibraryFromApk(String libName, File dest)
|
||||||
|
throws IOException {
|
||||||
|
File sourceDir = new File(app.getApplicationInfo().sourceDir);
|
||||||
|
if (sourceDir.isFile()) {
|
||||||
|
// Look for other APK files in the same directory, if we're allowed
|
||||||
|
File parent = sourceDir.getParentFile();
|
||||||
|
if (parent != null) sourceDir = parent;
|
||||||
|
}
|
||||||
|
List<String> libPaths = getSupportedLibraryPaths(libName);
|
||||||
|
for (File apk : findApkFiles(sourceDir)) {
|
||||||
|
ZipInputStream zin = new ZipInputStream(new FileInputStream(apk));
|
||||||
|
for (ZipEntry e = zin.getNextEntry(); e != null;
|
||||||
|
e = zin.getNextEntry()) {
|
||||||
|
if (libPaths.contains(e.getName())) {
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Extracting " + e.getName()
|
||||||
|
+ " from " + apk.getAbsolutePath());
|
||||||
|
}
|
||||||
|
extract(zin, dest); // Zip input stream will be closed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zin.close();
|
||||||
|
}
|
||||||
|
throw new FileNotFoundException(libName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all files with the extension .apk or .APK under the given root.
|
||||||
|
*/
|
||||||
|
private List<File> findApkFiles(File root) {
|
||||||
|
List<File> files = new ArrayList<>();
|
||||||
|
findApkFiles(root, files);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findApkFiles(File f, List<File> files) {
|
||||||
|
if (f.isFile() && f.getName().toLowerCase().endsWith(".apk")) {
|
||||||
|
files.add(f);
|
||||||
|
} else if (f.isDirectory()) {
|
||||||
|
File[] children = f.listFiles();
|
||||||
|
if (children != null) {
|
||||||
|
for (File child : children) findApkFiles(child, files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the paths at which libraries with the given name would be found
|
||||||
|
* inside an APK file, for all architectures supported by the device, in
|
||||||
|
* order of preference.
|
||||||
|
*/
|
||||||
|
private List<String> getSupportedLibraryPaths(String libName) {
|
||||||
|
List<String> architectures = new ArrayList<>();
|
||||||
|
for (String abi : AndroidUtils.getSupportedArchitectures()) {
|
||||||
|
if (LIBRARY_ARCHITECTURES.contains(abi)) {
|
||||||
|
architectures.add("lib/" + abi + "/" + libName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return architectures;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import android.bluetooth.BluetoothAdapter;
|
|||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.wifi.WifiConfiguration;
|
|
||||||
import android.net.wifi.WifiManager;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
@@ -17,12 +15,10 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
|
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.content.Context.WIFI_SERVICE;
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.provider.Settings.Secure.ANDROID_ID;
|
import static android.provider.Settings.Secure.ANDROID_ID;
|
||||||
|
|
||||||
@@ -52,15 +48,6 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
|||||||
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
||||||
if (id != null) out.writeUTF(id);
|
if (id != null) out.writeUTF(id);
|
||||||
Parcel parcel = Parcel.obtain();
|
Parcel parcel = Parcel.obtain();
|
||||||
WifiManager wm = (WifiManager) appContext.getApplicationContext()
|
|
||||||
.getSystemService(WIFI_SERVICE);
|
|
||||||
if (wm != null) {
|
|
||||||
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
|
|
||||||
if (configs != null) {
|
|
||||||
for (WifiConfiguration config : configs)
|
|
||||||
parcel.writeParcelable(config, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
if (bt != null) {
|
if (bt != null) {
|
||||||
for (BluetoothDevice device : bt.getBondedDevices())
|
for (BluetoothDevice device : bt.getBondedDevices())
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.briarproject.bramble.system;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
|
||||||
@@ -106,14 +105,21 @@ class AndroidWakeLockManagerImpl implements AndroidWakeLockManager {
|
|||||||
|
|
||||||
private String getWakeLockTag(Context ctx) {
|
private String getWakeLockTag(Context ctx) {
|
||||||
PackageManager pm = ctx.getPackageManager();
|
PackageManager pm = ctx.getPackageManager();
|
||||||
for (PackageInfo info : pm.getInstalledPackages(0)) {
|
if (isInstalled(pm, "com.huawei.powergenie")) {
|
||||||
String name = info.packageName.toLowerCase();
|
|
||||||
if (name.startsWith("com.huawei.powergenie")) {
|
|
||||||
return "LocationManagerService";
|
return "LocationManagerService";
|
||||||
} else if (name.startsWith("com.evenwell.powermonitor")) {
|
} else if (isInstalled(pm, "com.evenwell.PowerMonitor")) {
|
||||||
return "AudioIn";
|
return "AudioIn";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return ctx.getPackageName();
|
return ctx.getPackageName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isInstalled(PackageManager pm, String packageName) {
|
||||||
|
try {
|
||||||
|
pm.getPackageInfo(packageName, 0);
|
||||||
|
return true;
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class AndroidUtils {
|
|||||||
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
|
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
|
||||||
|
|
||||||
private static final String STORED_REPORTS = "dev-reports";
|
private static final String STORED_REPORTS = "dev-reports";
|
||||||
|
private static final String STORED_LOGCAT = "dev-logcat";
|
||||||
|
|
||||||
public static Collection<String> getSupportedArchitectures() {
|
public static Collection<String> getSupportedArchitectures() {
|
||||||
List<String> abis = new ArrayList<>();
|
List<String> abis = new ArrayList<>();
|
||||||
@@ -107,14 +108,14 @@ public class AndroidUtils {
|
|||||||
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
|
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static File getLogcatFile(Context ctx) {
|
||||||
|
return new File(ctx.getFilesDir(), STORED_LOGCAT);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of supported content types for image attachments.
|
* Returns an array of supported content types for image attachments.
|
||||||
* GIFs can't be compressed on API < 24 so they're not supported.
|
|
||||||
* <p>
|
|
||||||
* TODO: Remove this restriction when large message support is added
|
|
||||||
*/
|
*/
|
||||||
public static String[] getSupportedImageContentTypes() {
|
public static String[] getSupportedImageContentTypes() {
|
||||||
if (SDK_INT < 24) return new String[] {"image/jpeg", "image/png"};
|
return new String[] {"image/jpeg", "image/png", "image/gif"};
|
||||||
else return new String[] {"image/jpeg", "image/png", "image/gif"};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,36 @@
|
|||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
||||||
'com.android.tools.analytics-library:protos:26.5.1:protos-26.5.1.jar:8dde1130725461fe827f2a343d353f2b51e8870661fc860d7d5ebddb097ead4e',
|
'com.android.tools.analytics-library:protos:27.1.1:protos-27.1.1.jar:13f77e73762e58ab372d140b3a6be6903aea9775b62dd14fbc62d4cc7069c9a4',
|
||||||
'com.android.tools.analytics-library:shared:26.5.1:shared-26.5.1.jar:ccc2f3b00ec17b11401610ba68553544fc8fc517120e84439ac6eb86b875e18d',
|
'com.android.tools.analytics-library:shared:27.1.1:shared-27.1.1.jar:82930a52001410e97d809930b670f4de3002286975f046b9de5f6b777b06d366',
|
||||||
'com.android.tools.analytics-library:tracker:26.5.1:tracker-26.5.1.jar:3a76984c0fe2e847ca7a8b35b4780ef0447a9d1666946cb8e60466318e0ab5ae',
|
'com.android.tools.analytics-library:tracker:27.1.1:tracker-27.1.1.jar:31bc5a00be0055bac89c9b2f34751883e987cd89e3ac1783720645c164f591d9',
|
||||||
'com.android.tools.build:aapt2-proto:0.4.0:aapt2-proto-0.4.0.jar:fac0435e08898f89eeeb9ca236bea707155ff816c12205ced285ad53604133ca',
|
'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:3.5.1:apksig-3.5.1.jar:1fd33e7f009a2a0da766cfeec4211a09f548034b015c289a66d75dd8a9302f4a',
|
'com.android.tools.build:apksig:4.1.1:apksig-4.1.1.jar:e0a69da9e5a03986d608b45bbf954ef0e6a0b3f58c1b8315bd169ec08b279e72',
|
||||||
'com.android.tools.build:apkzlib:3.5.1:apkzlib-3.5.1.jar:9f330167cbe973b7db407692f74f4f6453b7ffa5f2048934b06280c2ceee60fa',
|
'com.android.tools.build:apkzlib:4.1.1:apkzlib-4.1.1.jar:ba4b5e419b6be0130eae7f8301c3a551ad3976f487d2e0c6852ebb175ac41127',
|
||||||
'com.android.tools.build:builder-model:3.5.1:builder-model-3.5.1.jar:39ea3c82b76b6e0c9f9fa88d93e0edc1dd4a0f1dfae0ef6fbf2d451da47e5450',
|
'com.android.tools.build:builder-model:4.1.1:builder-model-4.1.1.jar:e95c99cc298ad67b8deb6ced99c51abc8f59afebedad044b1a10dde14646a4dd',
|
||||||
'com.android.tools.build:builder-test-api:3.5.1:builder-test-api-3.5.1.jar:a1b59305584cbcaa078fdc9cfb80871012755b822dd32e8da19add6f7bbcb762',
|
'com.android.tools.build:builder-test-api:4.1.1:builder-test-api-4.1.1.jar:464f596ab261c051c3847406748e843770dea123f6fa5fee8a9390644e709b7a',
|
||||||
'com.android.tools.build:builder:3.5.1:builder-3.5.1.jar:e3a8d382434c5f60990730c4719fc814e85a898a33a1e96c1df8d627d3c6eea6',
|
'com.android.tools.build:builder:4.1.1:builder-4.1.1.jar:0f78d4759d2f7b57b95865522ec34596ba419b9982f3b25e3449213f9c98b80d',
|
||||||
'com.android.tools.build:gradle-api:3.5.1:gradle-api-3.5.1.jar:be9b41859bace11998f66b04ed944f87e413f3ad6da3c4665587699da125addc',
|
'com.android.tools.build:gradle-api:4.1.1:gradle-api-4.1.1.jar:d42e6b539e4c1353ad3546e75ec8ce11a017b97481023e8ea18577eefe374358',
|
||||||
'com.android.tools.build:manifest-merger:26.5.1:manifest-merger-26.5.1.jar:dcad9ecb967251f4d750f55a4204a2b400e8fbfe5cb930a1d0d5dbe10ae8bdfc',
|
'com.android.tools.build:manifest-merger:27.1.1:manifest-merger-27.1.1.jar:7a45fa143687859bb2e5a961dcf6ee88094d3853de0cb543dc03dbcb0f4b554b',
|
||||||
'com.android.tools.ddms:ddmlib:26.5.1:ddmlib-26.5.1.jar:b081aef2a4ed3f4d47cae4cdb128469735f25a114e026d37123bf9ffdec742a8',
|
'com.android.tools.ddms:ddmlib:27.1.1:ddmlib-27.1.1.jar:da6e4bd834b6a85dae8019039849d8bd96933347dfbf460df74913ddade6e40a',
|
||||||
'com.android.tools.external.com-intellij:intellij-core:26.5.1:intellij-core-26.5.1.jar:20eced30adc124805bd93488d9cd9d3e33e6bf7b48e9fe5a703d4983f894d450',
|
'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:26.5.1:kotlin-compiler-26.5.1.jar:5aed762dd54875b77ae7018d97c05756ff0c5b9fd02ec595dd396ccd14cc22cb',
|
'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:26.5.1:uast-26.5.1.jar:4bc8653d6c0943f40fee963a149e36c6baa45683d2530968a13f5007e3c40740',
|
'com.android.tools.external.org-jetbrains:uast:27.1.1:uast-27.1.1.jar:54cd8f6886a9d2f5641659dd5c91f626629672cd48301f7f0bd6aad9bd448714',
|
||||||
'com.android.tools.layoutlib:layoutlib-api:26.5.1:layoutlib-api-26.5.1.jar:88732f11396c427273e515d23042e35633f4fe4295528a99b866aa2adf0efd9c',
|
'com.android.tools.layoutlib:layoutlib-api:27.1.1:layoutlib-api-27.1.1.jar:8a9a22e3b309521ea83b724e5a89cfdac6076f52d675c0e17d77b05527bc0f8c',
|
||||||
'com.android.tools.lint:lint-api:26.5.1:lint-api-26.5.1.jar:ec33fcd72bfaf70dd841e03fbfd93f109c2e575aec146067c606689c3972f0de',
|
'com.android.tools.lint:lint-api:27.1.1:lint-api-27.1.1.jar:c1d8176094cb0478786070d40533efb578ebc53529a82f6ef5bee879bdca418b',
|
||||||
'com.android.tools.lint:lint-checks:26.5.1:lint-checks-26.5.1.jar:a1b9607d484aaae7a71dcecdc76f8003d8239af226c776894a2cf63f9e6c60d7',
|
'com.android.tools.lint:lint-checks:27.1.1:lint-checks-27.1.1.jar:3899c91e00bd059b40c31a9ca00cd0f8303191947608735ae1b657323693fb61',
|
||||||
'com.android.tools.lint:lint-gradle-api:26.5.1:lint-gradle-api-26.5.1.jar:82453fd98a8394cc84ed995c04d2cd744abd1d6589403427ba7eef53115406f3',
|
'com.android.tools.lint:lint-gradle-api:27.1.1:lint-gradle-api-27.1.1.jar:26aa89d38b9825cc73229daa82a68875801c8b8491f30497ce62aff1f206eb0d',
|
||||||
'com.android.tools.lint:lint-gradle:26.5.1:lint-gradle-26.5.1.jar:59465b56cf7db77c656d5f8195d721c3d48b6bdd0502d774de335bfe4baff00b',
|
'com.android.tools.lint:lint-gradle:27.1.1:lint-gradle-27.1.1.jar:f7355823ead869f4d28184ba28b7a0c693b507519a2d3705bb9848a0f35b3756',
|
||||||
'com.android.tools.lint:lint:26.5.1:lint-26.5.1.jar:336e4b04ec6f8b0f25879131b7a7862d77df83a1879ee5b71be26128755f8e2e',
|
'com.android.tools.lint:lint-model:27.1.1:lint-model-27.1.1.jar:bc23c0c413bdfca59dac2cd56b870d8360d009e9ec0d365e71f774bcf127971d',
|
||||||
'com.android.tools:annotations:26.5.1:annotations-26.5.1.jar:2c43c82f8c59d8f7a61e3239e1a2dc9f69dc342ec09af9b7c9f69b25337c0b6e',
|
'com.android.tools.lint:lint:27.1.1:lint-27.1.1.jar:2f6038a5398a42bd591883c3f5e5894f4ec52ca1c3683bf94fa8553c1700af81',
|
||||||
'com.android.tools:common:26.5.1:common-26.5.1.jar:eccfa54486ed54c4e3123cc42195d023bd0dd21bcd2f0e4868e8c6fc70f8ef6b',
|
'com.android.tools:annotations:27.1.1:annotations-27.1.1.jar:ff28c504d2acb9fd1a5ffbd97ae85cf59ee18c76927525aad250509bccf2cab1',
|
||||||
'com.android.tools:dvlib:26.5.1:dvlib-26.5.1.jar:46f93ad498b4756e7d867d2fe38c38890a80e7407a4ae459e4a8c8d5c5aeacfe',
|
'com.android.tools:common:27.1.1:common-27.1.1.jar:63d9a2a9ad6d278db319f3749b9f50bdf5457ef7020074a1bebe124e714b535c',
|
||||||
'com.android.tools:repository:26.5.1:repository-26.5.1.jar:2b3ee791aa4c3e8ce60498c161a27ca7228816fc630eed4d9f25f2f36a106dce',
|
'com.android.tools:dvlib:27.1.1:dvlib-27.1.1.jar:998a54201fc1cefee5f2399215e95c42b1f64f9e1d8f4452eb8255c68ba5440f',
|
||||||
'com.android.tools:sdk-common:26.5.1:sdk-common-26.5.1.jar:365f749676c3574676fd465177c8a492f340816db2b520d6ed114d3b6e77bea7',
|
'com.android.tools:repository:27.1.1:repository-27.1.1.jar:d25b74ccabf4d876903efb375e9af6fb380d8ae0445bb74bbdcc225c1e37fa1d',
|
||||||
'com.android.tools:sdklib:26.5.1:sdklib-26.5.1.jar:007da104afb27c8c682a1628023fe9ec438249c8d15ef0fd6624c5bb8e23b696',
|
'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.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
'com.google.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81',
|
'com.google.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81',
|
||||||
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f',
|
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f',
|
||||||
@@ -35,27 +38,30 @@ dependencyVerification {
|
|||||||
'com.google.dagger:dagger-spi:2.24:dagger-spi-2.24.jar:c038445d14dbcb4054e61bf49e05009edf26fce4fdc7ec1a9db544784f68e718',
|
'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:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64',
|
||||||
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a',
|
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a',
|
||||||
|
'com.google.errorprone:error_prone_annotations:2.3.2:error_prone_annotations-2.3.2.jar:357cd6cfb067c969226c442451502aee13800a24e950fdfde77bcdb4565a668d',
|
||||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
||||||
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
|
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
|
||||||
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26',
|
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26',
|
||||||
'com.google.guava:guava:27.0.1-jre:guava-27.0.1-jre.jar:e1c814fd04492a27c38e0317eabeaa1b3e950ec8010239e400fe90ad6c9107b4',
|
|
||||||
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
|
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
|
||||||
|
'com.google.guava:guava:28.1-jre:guava-28.1-jre.jar:30beb8b8527bd07c6e747e77f1a92122c2f29d57ce347461a4a55eb26e382da4',
|
||||||
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
||||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
|
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
|
||||||
|
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||||
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
|
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
|
||||||
'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
|
'com.google.protobuf:protobuf-java:3.10.0:protobuf-java-3.10.0.jar:161d7d61a8cb3970891c299578702fd079646e032329d6c2cabf998d191437c9',
|
||||||
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
|
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
|
||||||
'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90',
|
'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90',
|
||||||
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
|
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
|
||||||
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
|
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
|
||||||
'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4',
|
'com.sun.istack:istack-commons-runtime:3.0.7:istack-commons-runtime-3.0.7.jar:6443e10ba2e259fb821d9b6becf10db5316285fc30c53cec9d7b19a3877e7fdf',
|
||||||
'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
|
'com.sun.xml.fastinfoset:FastInfoset:1.2.15:FastInfoset-1.2.15.jar:785861db11ca1bd0d1956682b974ad73eb19cd3e01a4b3fa82d62eca97210aec',
|
||||||
'commons-codec:commons-codec:1.10:commons-codec-1.10.jar:4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569',
|
'commons-codec:commons-codec:1.10:commons-codec-1.10.jar:4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569',
|
||||||
'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
|
'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
|
||||||
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
|
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
|
||||||
|
'javax.activation:javax.activation-api:1.2.0:javax.activation-api-1.2.0.jar:43fdef0b5b6ceb31b0424b208b930c74ab58fac2ceeb7b3f6fd3aeb8b5ca4393',
|
||||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
|
'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.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||||
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
|
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
|
||||||
@@ -69,35 +75,36 @@ dependencyVerification {
|
|||||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||||
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||||
'org.briarproject:obfs4proxy-android:0.0.11-2:obfs4proxy-android-0.0.11-2.zip:57e55cbe87aa2aac210fdbb6cd8cdeafe15f825406a08ebf77a8b787aa2c6a8a',
|
'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.10:tor-android-0.3.5.10.zip:edd83bf557fcff2105eaa0bdb3f607a6852ebe7360920929ae3039dd5f4774c5',
|
'org.briarproject:tor-android:0.3.5.13:tor-android-0.3.5.13.zip:e0978db136731dae07774b722970cdae1e462fb5adc82845dd80a7e2d87f356c',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||||
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||||
|
'org.checkerframework:checker-qual:2.8.1:checker-qual-2.8.1.jar:9103499008bcecd4e948da29b17864abb64304e15706444ae209d17ebe0575df',
|
||||||
'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0',
|
'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0',
|
||||||
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||||
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
|
'org.codehaus.mojo:animal-sniffer-annotations:1.18:animal-sniffer-annotations-1.18.jar:47f05852b48ee9baefef80fa3d8cea60efa4753c0013121dd7fe5eef2e5c729d',
|
||||||
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
|
'org.glassfish.jaxb:jaxb-runtime:2.3.1:jaxb-runtime-2.3.1.jar:45fecfa5c8217ce1f3652ab95179790ec8cc0dec0384bca51cbeb94a293d9f2f',
|
||||||
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
|
'org.glassfish.jaxb:txw2:2.3.1:txw2-2.3.1.jar:34975dde1c6920f1a39791142235689bc3cd357e24d05edd8ff93b885bd68d60',
|
||||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||||
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||||
'org.jetbrains.kotlin:kotlin-reflect:1.3.50:kotlin-reflect-1.3.50.jar:64583199ea5a54aefd1bd1595288925f784226ee562d1dd279011c6075b3d7a4',
|
'org.jetbrains.kotlin:kotlin-reflect:1.3.72:kotlin-reflect-1.3.72.jar:a188d9367de1c4ee9479db630985c0597b20709c83161b1430d24edb27e38c40',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50:kotlin-stdlib-common-1.3.50.jar:8ce678e88e4ba018b66dacecf952471e4d7dfee156a8a819760a5a5ff29d323c',
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72:kotlin-stdlib-common-1.3.72.jar:5e7d1552863e480c1628b1cc39ce230ef829f5b7230106215a05acda5172203a',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50:kotlin-stdlib-jdk7-1.3.50.jar:9a026639e76212f8d57b86d55b075394c2e009f1979110751d34c05c5f75d57b',
|
'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.50:kotlin-stdlib-jdk8-1.3.50.jar:1b351fb6e09c14b55525c74c1f4cf48942eae43c348b7bc764a5e6e423d4da0c',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72:kotlin-stdlib-jdk8-1.3.72.jar:133da70cfc07b56094282eac5c59bccd59f167ee2ead22e5282876d8bc10bf95',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib:1.3.50:kotlin-stdlib-1.3.50.jar:e6f05746ee0366d0b52825a090fac474dcf44082c9083bbb205bd16976488d6c',
|
'org.jetbrains.kotlin:kotlin-stdlib:1.3.72:kotlin-stdlib-1.3.72.jar:3856a7349ebacd6d1be6802b2fed9c4dc2c5a564ea92b6b945ac988243d4b16b',
|
||||||
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
|
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
|
||||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||||
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
'org.jmock:jmock-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-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-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:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||||
'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
|
'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:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||||
'org.ow2.asm:asm-analysis:6.0:asm-analysis-6.0.jar:2f1a6387219c3a6cc4856481f221b03bd9f2408a326d416af09af5d6f608c1f4',
|
'org.ow2.asm:asm-analysis:7.0:asm-analysis-7.0.jar:e981f8f650c4d900bb033650b18e122fa6b161eadd5f88978d08751f72ee8474',
|
||||||
'org.ow2.asm:asm-commons:6.0:asm-commons-6.0.jar:f1bce5c648a96a017bdcd01fe5d59af9845297fd7b79b81c015a6fbbd9719abf',
|
'org.ow2.asm:asm-commons:7.0:asm-commons-7.0.jar:fed348ef05958e3e846a3ac074a12af5f7936ef3d21ce44a62c4fa08a771927d',
|
||||||
'org.ow2.asm:asm-tree:6.0:asm-tree-6.0.jar:887998fb69727c8759e4d253f856822801e33f9fd4caa566b3ac58ee92106215',
|
'org.ow2.asm:asm-tree:7.0:asm-tree-7.0.jar:cfd7a0874f9de36a999c127feeadfbfe6e04d4a71ee954d7af3d853f0be48a6c',
|
||||||
'org.ow2.asm:asm-util:6.0:asm-util-6.0.jar:356afebdb0f870175262e5188f8709a3b17aa2a5a6a4b0340b04d4b449bca5f6',
|
'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:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||||
'org.ow2.asm:asm:6.0:asm-6.0.jar:dd8971c74a4e697899a8e95caae4ea8760ea6c486dc6b97b1795e75760420461',
|
'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,10 @@ package org.briarproject.bramble.api;
|
|||||||
public interface FeatureFlags {
|
public interface FeatureFlags {
|
||||||
|
|
||||||
boolean shouldEnableImageAttachments();
|
boolean shouldEnableImageAttachments();
|
||||||
|
|
||||||
|
boolean shouldEnableProfilePictures();
|
||||||
|
|
||||||
|
boolean shouldEnableDisappearingMessages();
|
||||||
|
|
||||||
|
boolean shouldEnableConnectViaBluetooth();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.api.client;
|
package org.briarproject.bramble.api.client;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
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.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
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 org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -50,9 +52,11 @@ public interface ClientHelper {
|
|||||||
BdfDictionary getGroupMetadataAsDictionary(Transaction txn, GroupId g)
|
BdfDictionary getGroupMetadataAsDictionary(Transaction txn, GroupId g)
|
||||||
throws DbException, FormatException;
|
throws DbException, FormatException;
|
||||||
|
|
||||||
|
Collection<MessageId> getMessageIds(Transaction txn, GroupId g,
|
||||||
|
BdfDictionary query) throws DbException, FormatException;
|
||||||
|
|
||||||
BdfDictionary getMessageMetadataAsDictionary(MessageId m)
|
BdfDictionary getMessageMetadataAsDictionary(MessageId m)
|
||||||
throws DbException,
|
throws DbException, FormatException;
|
||||||
FormatException;
|
|
||||||
|
|
||||||
BdfDictionary getMessageMetadataAsDictionary(Transaction txn, MessageId m)
|
BdfDictionary getMessageMetadataAsDictionary(Transaction txn, MessageId m)
|
||||||
throws DbException, FormatException;
|
throws DbException, FormatException;
|
||||||
@@ -119,4 +123,17 @@ public interface ClientHelper {
|
|||||||
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
|
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
|
||||||
BdfDictionary properties) throws FormatException;
|
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";
|
||||||
|
}
|
||||||
@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.db.PendingContactExistsException;
|
|||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.AuthorInfo;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
@@ -179,6 +178,11 @@ public interface ContactManager {
|
|||||||
*/
|
*/
|
||||||
Collection<Contact> getContacts() throws DbException;
|
Collection<Contact> getContacts() throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all contacts.
|
||||||
|
*/
|
||||||
|
Collection<Contact> getContacts(Transaction txn) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a contact and all associated state.
|
* Removes a contact and all associated state.
|
||||||
*/
|
*/
|
||||||
@@ -215,16 +219,6 @@ public interface ContactManager {
|
|||||||
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
|
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link AuthorInfo} for the given author.
|
|
||||||
*/
|
|
||||||
AuthorInfo getAuthorInfo(AuthorId a) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link AuthorInfo} for the given author.
|
|
||||||
*/
|
|
||||||
AuthorInfo getAuthorInfo(Transaction txn, AuthorId a) throws DbException;
|
|
||||||
|
|
||||||
interface ContactHook {
|
interface ContactHook {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,4 +19,10 @@ public interface StreamDecrypterFactory {
|
|||||||
*/
|
*/
|
||||||
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link StreamDecrypter} for decrypting a log stream.
|
||||||
|
*/
|
||||||
|
StreamDecrypter createLogStreamDecrypter(InputStream in,
|
||||||
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ public interface StreamEncrypterFactory {
|
|||||||
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
|
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
|
||||||
* stream.
|
* stream.
|
||||||
*/
|
*/
|
||||||
StreamEncrypter createContactExchangeStreamDecrypter(OutputStream out,
|
StreamEncrypter createContactExchangeStreamEncrypter(OutputStream out,
|
||||||
|
SecretKey headerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link StreamEncrypter} for encrypting a log stream.
|
||||||
|
*/
|
||||||
|
StreamEncrypter createLogStreamEncrypter(OutputStream out,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,18 @@ import javax.annotation.Nullable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface DatabaseComponent extends TransactionManager {
|
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.
|
* Opens the database and returns true if the database already existed.
|
||||||
*
|
*
|
||||||
@@ -288,6 +300,16 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
Collection<MessageId> getMessageIds(Transaction txn, GroupId g)
|
Collection<MessageId> getMessageIds(Transaction txn, GroupId g)
|
||||||
throws DbException;
|
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.
|
* Returns the IDs of any messages that need to be validated.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -314,6 +336,15 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
Collection<MessageId> getMessagesToShare(Transaction txn)
|
Collection<MessageId> getMessagesToShare(Transaction txn)
|
||||||
throws DbException;
|
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.
|
* Returns the metadata for all delivered messages in the given group.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -395,6 +426,15 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
|
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the 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
|
* 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
|
* message is due to be sent to the given contact. The returned value may
|
||||||
@@ -535,6 +575,13 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
|
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
|
||||||
throws DbException;
|
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.
|
* Marks the given contact as verified.
|
||||||
*/
|
*/
|
||||||
@@ -557,6 +604,12 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
*/
|
*/
|
||||||
void setMessagePermanent(Transaction txn, MessageId m) throws DbException;
|
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.
|
* Marks the given message as shared.
|
||||||
*/
|
*/
|
||||||
@@ -599,6 +652,22 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
|
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
|
||||||
throws DbException;
|
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.
|
* 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.
|
* Returns the author's name.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class NetworkStatus {
|
public class NetworkStatus {
|
||||||
|
|
||||||
private final boolean connected, wifi;
|
private final boolean connected, wifi, ipv6Only;
|
||||||
|
|
||||||
public NetworkStatus(boolean connected, boolean wifi) {
|
public NetworkStatus(boolean connected, boolean wifi, boolean ipv6Only) {
|
||||||
this.connected = connected;
|
this.connected = connected;
|
||||||
this.wifi = wifi;
|
this.wifi = wifi;
|
||||||
|
this.ipv6Only = ipv6Only;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
@@ -22,4 +23,8 @@ public class NetworkStatus {
|
|||||||
public boolean isWifi() {
|
public boolean isWifi() {
|
||||||
return wifi;
|
return wifi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isIpv6Only() {
|
||||||
|
return ipv6Only;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,4 +29,12 @@ public class NullSafety {
|
|||||||
public static void requireNull(@Nullable Object o) {
|
public static void requireNull(@Nullable Object o) {
|
||||||
if (o != null) throw new AssertionError();
|
if (o != null) throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stand-in for {@code Objects.equals()}.
|
||||||
|
*/
|
||||||
|
public static boolean equals(@Nullable Object a, @Nullable Object b) {
|
||||||
|
return (a == b) || (a != null && a.equals(b));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,4 +13,6 @@ public interface DevConfig {
|
|||||||
String getDevOnionAddress();
|
String getDevOnionAddress();
|
||||||
|
|
||||||
File getReportDir();
|
File getReportDir();
|
||||||
|
|
||||||
|
File getLogcatFile();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ public interface DevReporter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends any reports previously stored on disk.
|
* Sends any reports previously stored on disk.
|
||||||
|
*
|
||||||
|
* @return The number of reports that were sent.
|
||||||
*/
|
*/
|
||||||
void sendReports();
|
int sendReports();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,5 +21,6 @@ public interface ReportingConstants {
|
|||||||
* Hidden service address for reporting crashes and feedback to the
|
* Hidden service address for reporting crashes and feedback to the
|
||||||
* developers.
|
* developers.
|
||||||
*/
|
*/
|
||||||
String DEV_ONION_ADDRESS = "cwqmubyvnig3wag3.onion";
|
String DEV_ONION_ADDRESS =
|
||||||
|
"b2nkt5doeamvdmjzfz7g42hk5vdtlnktlgzhel2bgjcc4v4jhnx2qrqd.onion";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,13 @@ public interface StreamReaderFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link InputStream InputStream} for reading from a contact
|
* Creates an {@link InputStream InputStream} for reading from a contact
|
||||||
* exchangestream.
|
* exchange stream.
|
||||||
*/
|
*/
|
||||||
InputStream createContactExchangeStreamReader(InputStream in,
|
InputStream createContactExchangeStreamReader(InputStream in,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link InputStream} for reading from a log stream.
|
||||||
|
*/
|
||||||
|
InputStream createLogStreamReader(InputStream in, SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,17 +7,19 @@ import java.io.OutputStream;
|
|||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface StreamWriterFactory {
|
public interface StreamWriterFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link OutputStream OutputStream} for writing to a
|
* Creates a {@link StreamWriter} for writing to a transport stream.
|
||||||
* transport stream
|
|
||||||
*/
|
*/
|
||||||
StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
|
StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link OutputStream OutputStream} for writing to a contact
|
* Creates a {@link StreamWriter} for writing to a contact exchange stream.
|
||||||
* exchange stream.
|
|
||||||
*/
|
*/
|
||||||
StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link StreamWriter} for writing to a log stream.
|
||||||
|
*/
|
||||||
|
StreamWriter createLogStreamWriter(OutputStream out, SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import org.briarproject.bramble.api.data.BdfList;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ValidationUtils {
|
public class ValidationUtils {
|
||||||
|
|
||||||
@@ -64,4 +66,9 @@ public class ValidationUtils {
|
|||||||
if (dictionary != null && dictionary.size() != size)
|
if (dictionary != null && dictionary.size() != size)
|
||||||
throw new FormatException();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.identity;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
|
|
||||||
public class AuthorInfoTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEquals() {
|
|
||||||
assertEquals(
|
|
||||||
new AuthorInfo(NONE),
|
|
||||||
new AuthorInfo(NONE, null)
|
|
||||||
);
|
|
||||||
assertEquals(
|
|
||||||
new AuthorInfo(NONE, "test"),
|
|
||||||
new AuthorInfo(NONE, "test")
|
|
||||||
);
|
|
||||||
|
|
||||||
assertNotEquals(
|
|
||||||
new AuthorInfo(NONE),
|
|
||||||
new AuthorInfo(VERIFIED)
|
|
||||||
);
|
|
||||||
assertNotEquals(
|
|
||||||
new AuthorInfo(NONE, "test"),
|
|
||||||
new AuthorInfo(NONE)
|
|
||||||
);
|
|
||||||
assertNotEquals(
|
|
||||||
new AuthorInfo(NONE),
|
|
||||||
new AuthorInfo(NONE, "test")
|
|
||||||
);
|
|
||||||
assertNotEquals(
|
|
||||||
new AuthorInfo(NONE, "a"),
|
|
||||||
new AuthorInfo(NONE, "b")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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,5 +1,6 @@
|
|||||||
package org.briarproject.bramble;
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.cleanup.CleanupModule;
|
||||||
import org.briarproject.bramble.contact.ContactModule;
|
import org.briarproject.bramble.contact.ContactModule;
|
||||||
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
||||||
import org.briarproject.bramble.db.DatabaseExecutorModule;
|
import org.briarproject.bramble.db.DatabaseExecutorModule;
|
||||||
@@ -14,6 +15,8 @@ import org.briarproject.bramble.versioning.VersioningModule;
|
|||||||
|
|
||||||
public interface BrambleCoreEagerSingletons {
|
public interface BrambleCoreEagerSingletons {
|
||||||
|
|
||||||
|
void inject(CleanupModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(ContactModule.EagerSingletons init);
|
void inject(ContactModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(CryptoExecutorModule.EagerSingletons init);
|
void inject(CryptoExecutorModule.EagerSingletons init);
|
||||||
@@ -39,6 +42,7 @@ public interface BrambleCoreEagerSingletons {
|
|||||||
class Helper {
|
class Helper {
|
||||||
|
|
||||||
public static void injectEagerSingletons(BrambleCoreEagerSingletons c) {
|
public static void injectEagerSingletons(BrambleCoreEagerSingletons c) {
|
||||||
|
c.inject(new CleanupModule.EagerSingletons());
|
||||||
c.inject(new ContactModule.EagerSingletons());
|
c.inject(new ContactModule.EagerSingletons());
|
||||||
c.inject(new CryptoExecutorModule.EagerSingletons());
|
c.inject(new CryptoExecutorModule.EagerSingletons());
|
||||||
c.inject(new DatabaseExecutorModule.EagerSingletons());
|
c.inject(new DatabaseExecutorModule.EagerSingletons());
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble;
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.cleanup.CleanupModule;
|
||||||
import org.briarproject.bramble.client.ClientModule;
|
import org.briarproject.bramble.client.ClientModule;
|
||||||
import org.briarproject.bramble.connection.ConnectionModule;
|
import org.briarproject.bramble.connection.ConnectionModule;
|
||||||
import org.briarproject.bramble.contact.ContactModule;
|
import org.briarproject.bramble.contact.ContactModule;
|
||||||
@@ -21,15 +22,14 @@ import org.briarproject.bramble.rendezvous.RendezvousModule;
|
|||||||
import org.briarproject.bramble.settings.SettingsModule;
|
import org.briarproject.bramble.settings.SettingsModule;
|
||||||
import org.briarproject.bramble.sync.SyncModule;
|
import org.briarproject.bramble.sync.SyncModule;
|
||||||
import org.briarproject.bramble.sync.validation.ValidationModule;
|
import org.briarproject.bramble.sync.validation.ValidationModule;
|
||||||
import org.briarproject.bramble.system.ClockModule;
|
|
||||||
import org.briarproject.bramble.transport.TransportModule;
|
import org.briarproject.bramble.transport.TransportModule;
|
||||||
import org.briarproject.bramble.versioning.VersioningModule;
|
import org.briarproject.bramble.versioning.VersioningModule;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
|
|
||||||
@Module(includes = {
|
@Module(includes = {
|
||||||
|
CleanupModule.class,
|
||||||
ClientModule.class,
|
ClientModule.class,
|
||||||
ClockModule.class,
|
|
||||||
ConnectionModule.class,
|
ConnectionModule.class,
|
||||||
ContactModule.class,
|
ContactModule.class,
|
||||||
CryptoModule.class,
|
CryptoModule.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.FormatException;
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
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.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
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.BdfList;
|
||||||
import org.briarproject.bramble.api.data.BdfReader;
|
import org.briarproject.bramble.api.data.BdfReader;
|
||||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||||
@@ -32,6 +34,7 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@@ -39,6 +42,7 @@ import java.util.Map.Entry;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
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.Author.FORMAT_VERSION;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_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);
|
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
|
@Override
|
||||||
public BdfDictionary getMessageMetadataAsDictionary(MessageId m)
|
public BdfDictionary getMessageMetadataAsDictionary(MessageId m)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
@@ -389,4 +399,27 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
return tpMap;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ import org.briarproject.bramble.api.event.Event;
|
|||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.AuthorInfo;
|
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
|
|
||||||
@@ -40,10 +38,6 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@@ -213,6 +207,11 @@ class ContactManagerImpl implements ContactManager, EventListener {
|
|||||||
return db.transactionWithResult(true, db::getContacts);
|
return db.transactionWithResult(true, db::getContacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Contact> getContacts(Transaction txn) throws DbException {
|
||||||
|
return db.getContacts(txn);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeContact(ContactId c) throws DbException {
|
public void removeContact(ContactId c) throws DbException {
|
||||||
db.transaction(false, txn -> removeContact(txn, c));
|
db.transaction(false, txn -> removeContact(txn, c));
|
||||||
@@ -256,25 +255,6 @@ class ContactManagerImpl implements ContactManager, EventListener {
|
|||||||
db.removeContact(txn, c);
|
db.removeContact(txn, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public AuthorInfo getAuthorInfo(AuthorId a) throws DbException {
|
|
||||||
return db.transactionWithResult(true, txn -> getAuthorInfo(txn, a));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AuthorInfo getAuthorInfo(Transaction txn, AuthorId authorId)
|
|
||||||
throws DbException {
|
|
||||||
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
|
|
||||||
if (localAuthor.getId().equals(authorId))
|
|
||||||
return new AuthorInfo(OURSELVES);
|
|
||||||
Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId);
|
|
||||||
if (contacts.isEmpty()) return new AuthorInfo(UNKNOWN);
|
|
||||||
if (contacts.size() > 1) throw new AssertionError();
|
|
||||||
Contact c = contacts.iterator().next();
|
|
||||||
if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias());
|
|
||||||
else return new AuthorInfo(UNVERIFIED, c.getAlias());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof PendingContactStateChangedEvent) {
|
if (e instanceof PendingContactStateChangedEvent) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.system.Clock;
|
|||||||
import org.briarproject.bramble.util.Base32;
|
import org.briarproject.bramble.util.Base32;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -52,7 +53,7 @@ class PendingContactFactoryImpl implements PendingContactFactory {
|
|||||||
byte[] raw = new byte[RAW_LINK_BYTES];
|
byte[] raw = new byte[RAW_LINK_BYTES];
|
||||||
raw[0] = FORMAT_VERSION;
|
raw[0] = FORMAT_VERSION;
|
||||||
arraycopy(encoded, 0, raw, 1, encoded.length);
|
arraycopy(encoded, 0, raw, 1, encoded.length);
|
||||||
return "briar://" + Base32.encode(raw).toLowerCase();
|
return "briar://" + Base32.encode(raw).toLowerCase(Locale.US);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PublicKey parseHandshakeLink(String link) throws FormatException {
|
private PublicKey parseHandshakeLink(String link) throws FormatException {
|
||||||
|
|||||||
@@ -9,14 +9,16 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.lang.Math.min;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
|
|
||||||
class ScryptKdf implements PasswordBasedKdf {
|
class ScryptKdf implements PasswordBasedKdf {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ScryptKdf.class.getName());
|
getLogger(ScryptKdf.class.getName());
|
||||||
|
|
||||||
private static final int MIN_COST = 256; // Min parameter N
|
private static final int MIN_COST = 256; // Min parameter N
|
||||||
private static final int MAX_COST = 1024 * 1024; // Max parameter N
|
private static final int MAX_COST = 1024 * 1024; // Max parameter N
|
||||||
@@ -33,10 +35,20 @@ class ScryptKdf implements PasswordBasedKdf {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int chooseCostParameter() {
|
public int chooseCostParameter() {
|
||||||
|
// Scrypt uses at least 128 * N * r bytes of memory. Don't use more
|
||||||
|
// than half of the JVM's max heap size or we may run out of memory.
|
||||||
|
// https://blog.filippo.io/the-scrypt-parameters/
|
||||||
|
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||||
|
long maxCost = min(MAX_COST, maxMemory / BLOCK_SIZE / 256);
|
||||||
|
if (LOG.isLoggable(INFO) && maxCost < MAX_COST) {
|
||||||
|
LOG.info("Max cost capped at " + maxCost
|
||||||
|
+ " due to max heap size " + maxMemory);
|
||||||
|
}
|
||||||
// Increase the cost from min to max while measuring performance
|
// Increase the cost from min to max while measuring performance
|
||||||
int cost = MIN_COST;
|
int cost = MIN_COST;
|
||||||
while (cost * 2 <= MAX_COST && measureDuration(cost) * 2 <= TARGET_MS)
|
while (cost * 2 <= maxCost && measureDuration(cost) * 2 <= TARGET_MS) {
|
||||||
cost *= 2;
|
cost *= 2;
|
||||||
|
}
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("KDF cost parameter " + cost);
|
LOG.info("KDF cost parameter " + cost);
|
||||||
return cost;
|
return cost;
|
||||||
|
|||||||
@@ -36,4 +36,10 @@ class StreamDecrypterFactoryImpl implements StreamDecrypterFactory {
|
|||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamDecrypter createLogStreamDecrypter(InputStream in,
|
||||||
|
SecretKey headerKey) {
|
||||||
|
return createContactExchangeStreamDecrypter(in, headerKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamEncrypter createContactExchangeStreamDecrypter(
|
public StreamEncrypter createContactExchangeStreamEncrypter(
|
||||||
OutputStream out, SecretKey headerKey) {
|
OutputStream out, SecretKey headerKey) {
|
||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
@@ -60,4 +60,10 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
|
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
|
||||||
headerKey, frameKey);
|
headerKey, frameKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamEncrypter createLogStreamEncrypter(OutputStream out,
|
||||||
|
SecretKey headerKey) {
|
||||||
|
return createContactExchangeStreamEncrypter(out, headerKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,13 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
void close() throws DbException;
|
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.
|
* Starts a new transaction and returns an object representing it.
|
||||||
*/
|
*/
|
||||||
@@ -497,6 +504,25 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
Collection<MessageId> getMessagesToShare(T txn) throws DbException;
|
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
|
* 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
|
* message is due to be sent to the given contact. The returned value may
|
||||||
@@ -606,8 +632,10 @@ interface Database<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks a message as having been seen by the given contact.
|
* 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.
|
* Removes a contact from the database.
|
||||||
@@ -671,6 +699,13 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException;
|
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.
|
* Marks the given contact as verified.
|
||||||
*/
|
*/
|
||||||
@@ -701,9 +736,10 @@ interface Database<T> {
|
|||||||
void setMessagePermanent(T txn, MessageId m) throws DbException;
|
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.
|
* Sets the validation and delivery state of the given message.
|
||||||
@@ -730,6 +766,22 @@ interface Database<T> {
|
|||||||
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
||||||
throws DbException;
|
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
|
* 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 given message with respect to the given contact, using the latency
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package org.briarproject.bramble.db;
|
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.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
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.ContactRemovedEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
|
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
|
||||||
@@ -576,6 +578,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return db.getMessageIds(txn, g);
|
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
|
@Override
|
||||||
public Collection<MessageId> getMessagesToValidate(Transaction transaction)
|
public Collection<MessageId> getMessagesToValidate(Transaction transaction)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -597,6 +608,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return db.getMessagesToShare(txn);
|
return db.getMessagesToShare(txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<GroupId, Collection<MessageId>> getMessagesToDelete(
|
||||||
|
Transaction transaction) throws DbException {
|
||||||
|
T txn = unbox(transaction);
|
||||||
|
return db.getMessagesToDelete(txn);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<MessageId, Metadata> getMessageMetadata(Transaction transaction,
|
public Map<MessageId, Metadata> getMessageMetadata(Transaction transaction,
|
||||||
GroupId g) throws DbException {
|
GroupId g) throws DbException {
|
||||||
@@ -692,6 +710,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return db.getMessageDependents(txn, m);
|
return db.getMessageDependents(txn, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getNextCleanupDeadline(Transaction transaction)
|
||||||
|
throws DbException {
|
||||||
|
T txn = unbox(transaction);
|
||||||
|
return db.getNextCleanupDeadline(txn);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getNextSendTime(Transaction transaction, ContactId c)
|
public long getNextSendTime(Transaction transaction, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -795,10 +820,19 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
Collection<MessageId> acked = new ArrayList<>();
|
Collection<MessageId> acked = new ArrayList<>();
|
||||||
for (MessageId m : a.getMessageIds()) {
|
for (MessageId m : a.getMessageIds()) {
|
||||||
if (db.containsVisibleMessage(txn, c, m)) {
|
if (db.containsVisibleMessage(txn, c, m)) {
|
||||||
db.raiseSeenFlag(txn, c, 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);
|
acked.add(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (acked.size() > 0) {
|
if (acked.size() > 0) {
|
||||||
transaction.attach(new MessagesAckedEvent(c, acked));
|
transaction.attach(new MessagesAckedEvent(c, acked));
|
||||||
}
|
}
|
||||||
@@ -952,6 +986,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
db.removeTransportKeys(txn, t, k);
|
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
|
@Override
|
||||||
public void setContactVerified(Transaction transaction, ContactId c)
|
public void setContactVerified(Transaction transaction, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -970,6 +1014,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsContact(txn, c))
|
if (!db.containsContact(txn, c))
|
||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
|
transaction.attach(new ContactAliasChangedEvent(c, alias));
|
||||||
db.setContactAlias(txn, c, alias);
|
db.setContactAlias(txn, c, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1001,6 +1046,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
db.setMessagePermanent(txn, m);
|
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
|
@Override
|
||||||
public void setMessageShared(Transaction transaction, MessageId m)
|
public void setMessageShared(Transaction transaction, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -1010,7 +1065,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
throw new NoSuchMessageException();
|
throw new NoSuchMessageException();
|
||||||
if (db.getMessageState(txn, m) != DELIVERED)
|
if (db.getMessageState(txn, m) != DELIVERED)
|
||||||
throw new IllegalArgumentException("Shared undelivered message");
|
throw new IllegalArgumentException("Shared undelivered message");
|
||||||
db.setMessageShared(txn, m);
|
db.setMessageShared(txn, m, true);
|
||||||
transaction.attach(new MessageSharedEvent(m));
|
transaction.attach(new MessageSharedEvent(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1082,6 +1137,30 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
db.setTransportKeysActive(txn, t, k);
|
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
|
@Override
|
||||||
public void updateTransportKeys(Transaction transaction,
|
public void updateTransportKeys(Transaction transaction,
|
||||||
Collection<TransportKeySet> keys) throws DbException {
|
Collection<TransportKeySet> keys) throws DbException {
|
||||||
|
|||||||
@@ -37,4 +37,10 @@ interface DatabaseConstants {
|
|||||||
* has passed since the last compaction.
|
* has passed since the last compaction.
|
||||||
*/
|
*/
|
||||||
long MAX_COMPACTION_INTERVAL_MS = DAYS.toMillis(30);
|
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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,9 +84,14 @@ class H2Database extends JdbcDatabase {
|
|||||||
@Override
|
@Override
|
||||||
public void close() throws DbException {
|
public void close() throws DbException {
|
||||||
// H2 will close the database when the last connection closes
|
// H2 will close the database when the last connection closes
|
||||||
|
Connection c = null;
|
||||||
try {
|
try {
|
||||||
|
c = createConnection();
|
||||||
super.closeAllConnections();
|
super.closeAllConnections();
|
||||||
|
setDirty(c, false);
|
||||||
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
tryToClose(c, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
try {
|
try {
|
||||||
super.closeAllConnections();
|
super.closeAllConnections();
|
||||||
c = createConnection();
|
c = createConnection();
|
||||||
|
setDirty(c, false);
|
||||||
s = c.createStatement();
|
s = c.createStatement();
|
||||||
s.executeQuery("SHUTDOWN");
|
s.executeQuery("SHUTDOWN");
|
||||||
s.close();
|
s.close();
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ import static java.util.Arrays.asList;
|
|||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.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.db.Metadata.REMOVE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
@@ -81,6 +83,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.PENDING;
|
||||||
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
|
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.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.LAST_COMPACTED_KEY;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
|
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
||||||
@@ -98,7 +101,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
|||||||
abstract class JdbcDatabase implements Database<Connection> {
|
abstract class JdbcDatabase implements Database<Connection> {
|
||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
static final int CODE_SCHEMA_VERSION = 47;
|
static final int CODE_SCHEMA_VERSION = 48;
|
||||||
|
|
||||||
// Time period offsets for incoming transport keys
|
// Time period offsets for incoming transport keys
|
||||||
private static final int OFFSET_PREV = -1;
|
private static final int OFFSET_PREV = -1;
|
||||||
@@ -180,6 +183,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " state INT NOT NULL,"
|
+ " state INT NOT NULL,"
|
||||||
+ " shared BOOLEAN NOT NULL,"
|
+ " shared BOOLEAN NOT NULL,"
|
||||||
+ " temporary 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,"
|
+ " length INT NOT NULL,"
|
||||||
+ " raw BLOB," // Null if message has been deleted
|
+ " raw BLOB," // Null if message has been deleted
|
||||||
+ " PRIMARY KEY (messageId),"
|
+ " PRIMARY KEY (messageId),"
|
||||||
@@ -336,6 +344,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
"CREATE INDEX IF NOT EXISTS statusesByContactIdTimestamp"
|
"CREATE INDEX IF NOT EXISTS statusesByContactIdTimestamp"
|
||||||
+ " ON statuses (contactId, timestamp)";
|
+ " ON statuses (contactId, timestamp)";
|
||||||
|
|
||||||
|
private static final String INDEX_MESSAGES_BY_CLEANUP_DEADLINE =
|
||||||
|
"CREATE INDEX IF NOT EXISTS messagesByCleanupDeadline"
|
||||||
|
+ " ON messages (cleanupDeadline)";
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(JdbcDatabase.class.getName());
|
getLogger(JdbcDatabase.class.getName());
|
||||||
|
|
||||||
@@ -354,9 +366,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
@GuardedBy("connectionsLock")
|
@GuardedBy("connectionsLock")
|
||||||
private boolean closed = false;
|
private boolean closed = false;
|
||||||
|
|
||||||
|
private volatile boolean wasDirtyOnInitialisation = false;
|
||||||
|
|
||||||
protected abstract Connection createConnection()
|
protected abstract Connection createConnection()
|
||||||
throws DbException, SQLException;
|
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;
|
protected abstract void compactAndClose() throws DbException;
|
||||||
|
|
||||||
JdbcDatabase(DatabaseTypes databaseTypes, MessageFactory messageFactory,
|
JdbcDatabase(DatabaseTypes databaseTypes, MessageFactory messageFactory,
|
||||||
@@ -381,13 +398,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
try {
|
try {
|
||||||
if (reopen) {
|
if (reopen) {
|
||||||
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||||
|
wasDirtyOnInitialisation = isDirty(s);
|
||||||
compact = migrateSchema(txn, s, listener) || isCompactionDue(s);
|
compact = migrateSchema(txn, s, listener) || isCompactionDue(s);
|
||||||
} else {
|
} else {
|
||||||
|
wasDirtyOnInitialisation = false;
|
||||||
createTables(txn);
|
createTables(txn);
|
||||||
initialiseSettings(txn);
|
initialiseSettings(txn);
|
||||||
compact = false;
|
compact = false;
|
||||||
}
|
}
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("db dirty? " + wasDirtyOnInitialisation);
|
||||||
|
}
|
||||||
createIndexes(txn);
|
createIndexes(txn);
|
||||||
|
setDirty(txn, true);
|
||||||
commitTransaction(txn);
|
commitTransaction(txn);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
abortTransaction(txn);
|
abortTransaction(txn);
|
||||||
@@ -414,6 +437,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean wasDirtyOnInitialisation() {
|
||||||
|
return wasDirtyOnInitialisation;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares the schema version stored in the database with the schema
|
* Compares the schema version stored in the database with the schema
|
||||||
* version used by the current code and applies any suitable migrations to
|
* version used by the current code and applies any suitable migrations to
|
||||||
@@ -463,7 +491,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
new Migration43_44(dbTypes),
|
new Migration43_44(dbTypes),
|
||||||
new Migration44_45(),
|
new Migration44_45(),
|
||||||
new Migration45_46(),
|
new Migration45_46(),
|
||||||
new Migration46_47(dbTypes)
|
new Migration46_47(dbTypes),
|
||||||
|
new Migration47_48()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,6 +517,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
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 {
|
private void initialiseSettings(Connection txn) throws DbException {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.putInt(SCHEMA_VERSION_KEY, CODE_SCHEMA_VERSION);
|
s.putInt(SCHEMA_VERSION_KEY, CODE_SCHEMA_VERSION);
|
||||||
@@ -531,6 +570,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
s.executeUpdate(INDEX_MESSAGE_DEPENDENCIES_BY_DEPENDENCY_ID);
|
s.executeUpdate(INDEX_MESSAGE_DEPENDENCIES_BY_DEPENDENCY_ID);
|
||||||
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_GROUP_ID);
|
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_GROUP_ID);
|
||||||
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
|
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
|
||||||
|
s.executeUpdate(INDEX_MESSAGES_BY_CLEANUP_DEADLINE);
|
||||||
s.close();
|
s.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s, LOG, WARNING);
|
tryToClose(s, LOG, WARNING);
|
||||||
@@ -1290,7 +1330,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
public void deleteMessage(Connection txn, MessageId m) throws DbException {
|
public void deleteMessage(Connection txn, MessageId m) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
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 = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, m.getBytes());
|
||||||
int affected = ps.executeUpdate();
|
int affected = ps.executeUpdate();
|
||||||
@@ -1769,7 +1811,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
// Return early if there are no matches
|
// Return early if there are no matches
|
||||||
if (intersection.isEmpty()) return Collections.emptySet();
|
if (intersection.isEmpty()) return Collections.emptySet();
|
||||||
}
|
}
|
||||||
if (intersection == null) throw new AssertionError();
|
|
||||||
return intersection;
|
return intersection;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs, LOG, WARNING);
|
||||||
@@ -2226,6 +2267,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
|
@Override
|
||||||
public long getNextSendTime(Connection txn, ContactId c)
|
public long getNextSendTime(Connection txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -2256,6 +2330,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
|
@Override
|
||||||
public PendingContact getPendingContact(Connection txn, PendingContactId p)
|
public PendingContact getPendingContact(Connection txn, PendingContactId p)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -2776,7 +2875,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void raiseSeenFlag(Connection txn, ContactId c, MessageId m)
|
public boolean raiseSeenFlag(Connection txn, ContactId c, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
@@ -2788,6 +2887,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
int affected = ps.executeUpdate();
|
int affected = ps.executeUpdate();
|
||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
|
return affected == 1;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
@@ -3021,6 +3121,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
|
@Override
|
||||||
public void setContactVerified(Connection txn, ContactId c)
|
public void setContactVerified(Connection txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -3128,22 +3247,24 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMessageShared(Connection txn, MessageId m)
|
public void setMessageShared(Connection txn, MessageId m, boolean shared)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
String sql = "UPDATE messages SET shared = TRUE"
|
String sql = "UPDATE messages SET shared = ?"
|
||||||
+ " WHERE messageId = ?";
|
+ " WHERE messageId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBoolean(1, shared);
|
||||||
|
ps.setBytes(2, m.getBytes());
|
||||||
int affected = ps.executeUpdate();
|
int affected = ps.executeUpdate();
|
||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
// Update denormalised column in statuses
|
// Update denormalised column in statuses
|
||||||
sql = "UPDATE statuses SET messageShared = TRUE"
|
sql = "UPDATE statuses SET messageShared = ?"
|
||||||
+ " WHERE messageId = ?";
|
+ " WHERE messageId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBoolean(1, shared);
|
||||||
|
ps.setBytes(2, m.getBytes());
|
||||||
affected = ps.executeUpdate();
|
affected = ps.executeUpdate();
|
||||||
if (affected < 0) throw new DbStateException();
|
if (affected < 0) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -3272,6 +3393,60 @@ 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
|
@Override
|
||||||
public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m,
|
public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m,
|
||||||
int maxLatency) throws DbException {
|
int maxLatency) throws DbException {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
@@ -8,6 +9,8 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
|||||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||||
import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
|
import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||||
|
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
@@ -19,7 +22,9 @@ import org.briarproject.bramble.api.record.RecordWriterFactory;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
@@ -28,8 +33,10 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.CONNECTION_TIMEOUT;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.CONNECTION_TIMEOUT;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@@ -41,7 +48,10 @@ class KeyAgreementConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(KeyAgreementConnector.class.getName());
|
getLogger(KeyAgreementConnector.class.getName());
|
||||||
|
|
||||||
|
private static final List<TransportId> PREFERRED_TRANSPORTS =
|
||||||
|
asList(BluetoothConstants.ID, LanTcpConstants.ID);
|
||||||
|
|
||||||
private final Callbacks callbacks;
|
private final Callbacks callbacks;
|
||||||
private final KeyAgreementCrypto keyAgreementCrypto;
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
@@ -105,22 +115,33 @@ class KeyAgreementConnector {
|
|||||||
this.alice = alice;
|
this.alice = alice;
|
||||||
aliceLatch.countDown();
|
aliceLatch.countDown();
|
||||||
|
|
||||||
// Start connecting over supported transports
|
// Start connecting over supported transports in order of preference
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Starting outgoing BQP connections as "
|
LOG.info("Starting outgoing BQP connections as "
|
||||||
+ (alice ? "Alice" : "Bob"));
|
+ (alice ? "Alice" : "Bob"));
|
||||||
}
|
}
|
||||||
|
Map<TransportId, TransportDescriptor> descriptors = new HashMap<>();
|
||||||
for (TransportDescriptor d : remotePayload.getTransportDescriptors()) {
|
for (TransportDescriptor d : remotePayload.getTransportDescriptors()) {
|
||||||
Plugin p = pluginManager.getPlugin(d.getId());
|
descriptors.put(d.getId(), d);
|
||||||
if (p instanceof DuplexPlugin) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Connecting via " + d.getId());
|
|
||||||
DuplexPlugin plugin = (DuplexPlugin) p;
|
|
||||||
byte[] commitment = remotePayload.getCommitment();
|
|
||||||
BdfList descriptor = d.getDescriptor();
|
|
||||||
connectionChooser.submit(new ReadableTask(
|
|
||||||
new ConnectorTask(plugin, commitment, descriptor)));
|
|
||||||
}
|
}
|
||||||
|
List<Pair<DuplexPlugin, BdfList>> transports = new ArrayList<>();
|
||||||
|
for (TransportId id : PREFERRED_TRANSPORTS) {
|
||||||
|
TransportDescriptor d = descriptors.get(id);
|
||||||
|
Plugin p = pluginManager.getPlugin(id);
|
||||||
|
if (d != null && p instanceof DuplexPlugin) {
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Connecting via " + id);
|
||||||
|
transports.add(new Pair<>((DuplexPlugin) p, d.getDescriptor()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: If we don't have any transports in common with the peer,
|
||||||
|
// warn the user and give up (#1224)
|
||||||
|
|
||||||
|
if (!transports.isEmpty()) {
|
||||||
|
byte[] commitment = remotePayload.getCommitment();
|
||||||
|
connectionChooser.submit(new ReadableTask(new ConnectorTask(
|
||||||
|
transports, commitment)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get chosen connection
|
// Get chosen connection
|
||||||
@@ -148,15 +169,13 @@ class KeyAgreementConnector {
|
|||||||
|
|
||||||
private class ConnectorTask implements Callable<KeyAgreementConnection> {
|
private class ConnectorTask implements Callable<KeyAgreementConnection> {
|
||||||
|
|
||||||
|
private final List<Pair<DuplexPlugin, BdfList>> transports;
|
||||||
private final byte[] commitment;
|
private final byte[] commitment;
|
||||||
private final BdfList descriptor;
|
|
||||||
private final DuplexPlugin plugin;
|
|
||||||
|
|
||||||
private ConnectorTask(DuplexPlugin plugin, byte[] commitment,
|
private ConnectorTask(List<Pair<DuplexPlugin, BdfList>> transports,
|
||||||
BdfList descriptor) {
|
byte[] commitment) {
|
||||||
this.plugin = plugin;
|
this.transports = transports;
|
||||||
this.commitment = commitment;
|
this.commitment = commitment;
|
||||||
this.descriptor = descriptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -164,6 +183,10 @@ class KeyAgreementConnector {
|
|||||||
public KeyAgreementConnection call() throws Exception {
|
public KeyAgreementConnection call() throws Exception {
|
||||||
// Repeat attempts until we connect, get stopped, or get interrupted
|
// Repeat attempts until we connect, get stopped, or get interrupted
|
||||||
while (!stopped) {
|
while (!stopped) {
|
||||||
|
for (Pair<DuplexPlugin, BdfList> pair : transports) {
|
||||||
|
if (stopped) return null;
|
||||||
|
DuplexPlugin plugin = pair.getFirst();
|
||||||
|
BdfList descriptor = pair.getSecond();
|
||||||
DuplexTransportConnection conn =
|
DuplexTransportConnection conn =
|
||||||
plugin.createKeyAgreementConnection(commitment,
|
plugin.createKeyAgreementConnection(commitment,
|
||||||
descriptor);
|
descriptor);
|
||||||
@@ -172,6 +195,7 @@ class KeyAgreementConnector {
|
|||||||
LOG.info(plugin.getId() + ": Outgoing connection");
|
LOG.info(plugin.getId() + ": Outgoing connection");
|
||||||
return new KeyAgreementConnection(conn, plugin.getId());
|
return new KeyAgreementConnection(conn, plugin.getId());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Wait 2s before retry (to circumvent transient failures)
|
// Wait 2s before retry (to circumvent transient failures)
|
||||||
Thread.sleep(2000);
|
Thread.sleep(2000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ class KeyAgreementTransport {
|
|||||||
Logger.getLogger(KeyAgreementTransport.class.getName());
|
Logger.getLogger(KeyAgreementTransport.class.getName());
|
||||||
|
|
||||||
// Accept records with current protocol version, known record type
|
// 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 &&
|
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||||
isKnownRecordType(r.getRecordType());
|
isKnownRecordType(r.getRecordType());
|
||||||
|
|
||||||
// Ignore records with current protocol version, unknown record type
|
// 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 &&
|
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||||
!isKnownRecordType(r.getRecordType());
|
!isKnownRecordType(r.getRecordType());
|
||||||
|
|
||||||
|
|||||||
@@ -436,8 +436,10 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
|
|||||||
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
||||||
DuplexTransportConnection conn;
|
DuplexTransportConnection conn;
|
||||||
if (descriptor.size() == 1) {
|
if (descriptor.size() == 1) {
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Discovering address for key agreement UUID " + uuid);
|
LOG.info("Discovering address for key agreement UUID " +
|
||||||
|
uuid);
|
||||||
|
}
|
||||||
conn = discoverAndConnect(uuid);
|
conn = discoverAndConnect(uuid);
|
||||||
} else {
|
} else {
|
||||||
String address;
|
String address;
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private final CircumventionProvider circumventionProvider;
|
private final CircumventionProvider circumventionProvider;
|
||||||
private final ResourceProvider resourceProvider;
|
private final ResourceProvider resourceProvider;
|
||||||
private final int maxLatency, maxIdleTime, socketTimeout;
|
private final int maxLatency, maxIdleTime, socketTimeout;
|
||||||
private final File torDirectory, torFile, geoIpFile, obfs4File, configFile;
|
private final File torDirectory, geoIpFile, configFile;
|
||||||
private final File doneFile, cookieFile;
|
private final File doneFile, cookieFile;
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
@@ -181,9 +181,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
socketTimeout = Integer.MAX_VALUE;
|
socketTimeout = Integer.MAX_VALUE;
|
||||||
else socketTimeout = maxIdleTime * 2;
|
else socketTimeout = maxIdleTime * 2;
|
||||||
this.torDirectory = torDirectory;
|
this.torDirectory = torDirectory;
|
||||||
torFile = new File(torDirectory, "tor");
|
|
||||||
geoIpFile = new File(torDirectory, "geoip");
|
geoIpFile = new File(torDirectory, "geoip");
|
||||||
obfs4File = new File(torDirectory, "obfs4proxy");
|
|
||||||
configFile = new File(torDirectory, "torrc");
|
configFile = new File(torDirectory, "torrc");
|
||||||
doneFile = new File(torDirectory, "done");
|
doneFile = new File(torDirectory, "done");
|
||||||
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
||||||
@@ -192,6 +190,14 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
new PoliteExecutor("TorPlugin", ioExecutor, 1);
|
new PoliteExecutor("TorPlugin", ioExecutor, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected File getTorExecutableFile() {
|
||||||
|
return new File(torDirectory, "tor");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected File getObfs4ExecutableFile() {
|
||||||
|
return new File(torDirectory, "obfs4proxy");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportId getId() {
|
public TransportId getId() {
|
||||||
return TorConstants.ID;
|
return TorConstants.ID;
|
||||||
@@ -224,6 +230,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
LOG.warning("Old auth cookie not deleted");
|
LOG.warning("Old auth cookie not deleted");
|
||||||
// Start a new Tor process
|
// Start a new Tor process
|
||||||
LOG.info("Starting Tor");
|
LOG.info("Starting Tor");
|
||||||
|
File torFile = getTorExecutableFile();
|
||||||
String torPath = torFile.getAbsolutePath();
|
String torPath = torFile.getAbsolutePath();
|
||||||
String configPath = configFile.getAbsolutePath();
|
String configPath = configFile.getAbsolutePath();
|
||||||
String pid = String.valueOf(getProcessId());
|
String pid = String.valueOf(getProcessId());
|
||||||
@@ -322,44 +329,43 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void installAssets() throws PluginException {
|
private void installAssets() throws PluginException {
|
||||||
InputStream in = null;
|
|
||||||
OutputStream out = null;
|
|
||||||
try {
|
try {
|
||||||
// The done file may already exist from a previous installation
|
// The done file may already exist from a previous installation
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
doneFile.delete();
|
doneFile.delete();
|
||||||
// Unzip the Tor binary to the filesystem
|
installTorExecutable();
|
||||||
in = getTorInputStream();
|
installObfs4Executable();
|
||||||
out = new FileOutputStream(torFile);
|
extract(getGeoIpInputStream(), geoIpFile);
|
||||||
copyAndClose(in, out);
|
extract(getConfigInputStream(), configFile);
|
||||||
// Make the Tor binary executable
|
|
||||||
if (!torFile.setExecutable(true, true)) throw new IOException();
|
|
||||||
// Unzip the GeoIP database to the filesystem
|
|
||||||
in = getGeoIpInputStream();
|
|
||||||
out = new FileOutputStream(geoIpFile);
|
|
||||||
copyAndClose(in, out);
|
|
||||||
// Unzip the Obfs4 proxy to the filesystem
|
|
||||||
in = getObfs4InputStream();
|
|
||||||
out = new FileOutputStream(obfs4File);
|
|
||||||
copyAndClose(in, out);
|
|
||||||
// Make the Obfs4 proxy executable
|
|
||||||
if (!obfs4File.setExecutable(true, true)) throw new IOException();
|
|
||||||
// Copy the config file to the filesystem
|
|
||||||
in = getConfigInputStream();
|
|
||||||
out = new FileOutputStream(configFile);
|
|
||||||
copyAndClose(in, out);
|
|
||||||
if (!doneFile.createNewFile())
|
if (!doneFile.createNewFile())
|
||||||
LOG.warning("Failed to create done file");
|
LOG.warning("Failed to create done file");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
tryToClose(in, LOG, WARNING);
|
|
||||||
tryToClose(out, LOG, WARNING);
|
|
||||||
throw new PluginException(e);
|
throw new PluginException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getTorInputStream() throws IOException {
|
protected void extract(InputStream in, File dest) throws IOException {
|
||||||
|
OutputStream out = new FileOutputStream(dest);
|
||||||
|
copyAndClose(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void installTorExecutable() throws IOException {
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Installing Tor binary for " + architecture);
|
LOG.info("Installing Tor binary for " + architecture);
|
||||||
|
File torFile = getTorExecutableFile();
|
||||||
|
extract(getTorInputStream(), torFile);
|
||||||
|
if (!torFile.setExecutable(true, true)) throw new IOException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void installObfs4Executable() throws IOException {
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Installing obfs4proxy binary for " + architecture);
|
||||||
|
File obfs4File = getObfs4ExecutableFile();
|
||||||
|
extract(getObfs4InputStream(), obfs4File);
|
||||||
|
if (!obfs4File.setExecutable(true, true)) throw new IOException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream getTorInputStream() throws IOException {
|
||||||
InputStream in = resourceProvider
|
InputStream in = resourceProvider
|
||||||
.getResourceInputStream("tor_" + architecture, ".zip");
|
.getResourceInputStream("tor_" + architecture, ".zip");
|
||||||
ZipInputStream zin = new ZipInputStream(in);
|
ZipInputStream zin = new ZipInputStream(in);
|
||||||
@@ -376,8 +382,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getObfs4InputStream() throws IOException {
|
private InputStream getObfs4InputStream() throws IOException {
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Installing obfs4proxy binary for " + architecture);
|
|
||||||
InputStream in = resourceProvider
|
InputStream in = resourceProvider
|
||||||
.getResourceInputStream("obfs4proxy_" + architecture, ".zip");
|
.getResourceInputStream("obfs4proxy_" + architecture, ".zip");
|
||||||
ZipInputStream zin = new ZipInputStream(in);
|
ZipInputStream zin = new ZipInputStream(in);
|
||||||
@@ -569,6 +573,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
if (enable) {
|
if (enable) {
|
||||||
Collection<String> conf = new ArrayList<>();
|
Collection<String> conf = new ArrayList<>();
|
||||||
conf.add("UseBridges 1");
|
conf.add("UseBridges 1");
|
||||||
|
File obfs4File = getObfs4ExecutableFile();
|
||||||
if (needsMeek) {
|
if (needsMeek) {
|
||||||
conf.add("ClientTransportPlugin meek_lite exec " +
|
conf.add("ClientTransportPlugin meek_lite exec " +
|
||||||
obfs4File.getAbsolutePath());
|
obfs4File.getAbsolutePath());
|
||||||
@@ -858,6 +863,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
if (!state.isTorRunning()) return;
|
if (!state.isTorRunning()) return;
|
||||||
boolean online = status.isConnected();
|
boolean online = status.isConnected();
|
||||||
boolean wifi = status.isWifi();
|
boolean wifi = status.isWifi();
|
||||||
|
boolean ipv6Only = status.isIpv6Only();
|
||||||
String country = locationUtils.getCurrentCountry();
|
String country = locationUtils.getCurrentCountry();
|
||||||
boolean blocked =
|
boolean blocked =
|
||||||
circumventionProvider.isTorProbablyBlocked(country);
|
circumventionProvider.isTorProbablyBlocked(country);
|
||||||
@@ -874,7 +880,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC;
|
boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC;
|
||||||
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Online: " + online + ", wifi: " + wifi);
|
LOG.info("Online: " + online + ", wifi: " + wifi
|
||||||
|
+ ", IPv6 only: " + ipv6Only);
|
||||||
if (country.isEmpty()) LOG.info("Country code unknown");
|
if (country.isEmpty()) LOG.info("Country code unknown");
|
||||||
else LOG.info("Country code: " + country);
|
else LOG.info("Country code: " + country);
|
||||||
LOG.info("Charging: " + charging);
|
LOG.info("Charging: " + charging);
|
||||||
@@ -911,7 +918,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
enableNetwork = true;
|
enableNetwork = true;
|
||||||
if (network == PREF_TOR_NETWORK_WITH_BRIDGES ||
|
if (network == PREF_TOR_NETWORK_WITH_BRIDGES ||
|
||||||
(automatic && bridgesWork)) {
|
(automatic && bridgesWork)) {
|
||||||
if (circumventionProvider.needsMeek(country)) {
|
if (ipv6Only ||
|
||||||
|
circumventionProvider.needsMeek(country)) {
|
||||||
LOG.info("Using meek bridges");
|
LOG.info("Using meek bridges");
|
||||||
enableBridges = true;
|
enableBridges = true;
|
||||||
useMeek = true;
|
useMeek = true;
|
||||||
@@ -937,6 +945,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
if (enableNetwork) {
|
if (enableNetwork) {
|
||||||
enableBridges(enableBridges, useMeek);
|
enableBridges(enableBridges, useMeek);
|
||||||
enableConnectionPadding(enableConnectionPadding);
|
enableConnectionPadding(enableConnectionPadding);
|
||||||
|
useIpv6(ipv6Only);
|
||||||
}
|
}
|
||||||
enableNetwork(enableNetwork);
|
enableNetwork(enableNetwork);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -949,6 +958,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
controlConnection.setConf("ConnectionPadding", enable ? "1" : "0");
|
controlConnection.setConf("ConnectionPadding", enable ? "1" : "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void useIpv6(boolean ipv6Only) throws IOException {
|
||||||
|
controlConnection.setConf("ClientUseIPv4", ipv6Only ? "0" : "1");
|
||||||
|
controlConnection.setConf("ClientUseIPv6", ipv6Only ? "1" : "0");
|
||||||
|
}
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
protected class PluginState {
|
protected class PluginState {
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ import org.briarproject.bramble.api.system.Clock;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
|
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_LOCAL;
|
||||||
|
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_TRANSPORT_ID;
|
||||||
|
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_VERSION;
|
||||||
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
||||||
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
|
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
|
||||||
|
|
||||||
@@ -43,9 +46,9 @@ class TransportPropertyValidator extends BdfMessageValidator {
|
|||||||
clientHelper.parseAndValidateTransportProperties(dictionary);
|
clientHelper.parseAndValidateTransportProperties(dictionary);
|
||||||
// Return the metadata
|
// Return the metadata
|
||||||
BdfDictionary meta = new BdfDictionary();
|
BdfDictionary meta = new BdfDictionary();
|
||||||
meta.put("transportId", transportId);
|
meta.put(MSG_KEY_TRANSPORT_ID, transportId);
|
||||||
meta.put("version", version);
|
meta.put(MSG_KEY_VERSION, version);
|
||||||
meta.put("local", false);
|
meta.put(MSG_KEY_LOCAL, false);
|
||||||
return new BdfMessageContext(meta);
|
return new BdfMessageContext(meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||||
|
|
||||||
@@ -100,11 +101,12 @@ class DevReporterImpl implements DevReporter, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendReports() {
|
public int sendReports() {
|
||||||
File reportDir = devConfig.getReportDir();
|
File reportDir = devConfig.getReportDir();
|
||||||
File[] reports = reportDir.listFiles();
|
File[] reports = reportDir.listFiles();
|
||||||
|
int reportsSent = 0;
|
||||||
if (reports == null || reports.length == 0)
|
if (reports == null || reports.length == 0)
|
||||||
return; // No reports to send
|
return reportsSent; // No reports to send
|
||||||
|
|
||||||
LOG.info("Sending reports to developers");
|
LOG.info("Sending reports to developers");
|
||||||
for (File f : reports) {
|
for (File f : reports) {
|
||||||
@@ -116,13 +118,15 @@ class DevReporterImpl implements DevReporter, EventListener {
|
|||||||
in = new FileInputStream(f);
|
in = new FileInputStream(f);
|
||||||
IoUtils.copyAndClose(in, out);
|
IoUtils.copyAndClose(in, out);
|
||||||
f.delete();
|
f.delete();
|
||||||
|
reportsSent++;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.log(WARNING, "Failed to send reports", e);
|
LOG.log(WARNING, "Failed to send reports", e);
|
||||||
tryToClose(out, LOG, WARNING);
|
tryToClose(out, LOG, WARNING);
|
||||||
tryToClose(in, LOG, WARNING);
|
tryToClose(in, LOG, WARNING);
|
||||||
return;
|
return reportsSent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG.info("Reports sent");
|
if (LOG.isLoggable(INFO)) LOG.info(reportsSent + " report(s) sent");
|
||||||
|
return reportsSent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,15 +24,21 @@ class StreamReaderFactoryImpl implements StreamReaderFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream createStreamReader(InputStream in, StreamContext ctx) {
|
public InputStream createStreamReader(InputStream in, StreamContext ctx) {
|
||||||
return new StreamReaderImpl(
|
return new StreamReaderImpl(streamDecrypterFactory
|
||||||
streamDecrypterFactory.createStreamDecrypter(in, ctx));
|
.createStreamDecrypter(in, ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream createContactExchangeStreamReader(InputStream in,
|
public InputStream createContactExchangeStreamReader(InputStream in,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamReaderImpl(
|
return new StreamReaderImpl(streamDecrypterFactory
|
||||||
streamDecrypterFactory.createContactExchangeStreamDecrypter(in,
|
.createContactExchangeStreamDecrypter(in, headerKey));
|
||||||
headerKey));
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream createLogStreamReader(InputStream in,
|
||||||
|
SecretKey headerKey) {
|
||||||
|
return new StreamReaderImpl(streamDecrypterFactory
|
||||||
|
.createLogStreamDecrypter(in, headerKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,15 +26,21 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
|
|||||||
@Override
|
@Override
|
||||||
public StreamWriter createStreamWriter(OutputStream out,
|
public StreamWriter createStreamWriter(OutputStream out,
|
||||||
StreamContext ctx) {
|
StreamContext ctx) {
|
||||||
return new StreamWriterImpl(
|
return new StreamWriterImpl(streamEncrypterFactory
|
||||||
streamEncrypterFactory.createStreamEncrypter(out, ctx));
|
.createStreamEncrypter(out, ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
public StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamWriterImpl(
|
return new StreamWriterImpl(streamEncrypterFactory
|
||||||
streamEncrypterFactory.createContactExchangeStreamDecrypter(out,
|
.createContactExchangeStreamEncrypter(out, headerKey));
|
||||||
headerKey));
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamWriter createLogStreamWriter(OutputStream out,
|
||||||
|
SecretKey headerKey) {
|
||||||
|
return new StreamWriterImpl(streamEncrypterFactory
|
||||||
|
.createLogStreamEncrypter(out, headerKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,5 @@ interface ClientVersioningConstants {
|
|||||||
// Metadata keys
|
// Metadata keys
|
||||||
String MSG_KEY_UPDATE_VERSION = "version";
|
String MSG_KEY_UPDATE_VERSION = "version";
|
||||||
String MSG_KEY_LOCAL = "local";
|
String MSG_KEY_LOCAL = "local";
|
||||||
String GROUP_KEY_CONTACT_ID = "contactId";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ import static java.util.Collections.emptyList;
|
|||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
||||||
import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID;
|
|
||||||
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
|
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
|
||||||
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
|
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
|
||||||
|
|
||||||
@@ -161,13 +160,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
|||||||
db.addGroup(txn, g);
|
db.addGroup(txn, g);
|
||||||
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
|
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
|
||||||
// Attach the contact ID to the group
|
// Attach the contact ID to the group
|
||||||
BdfDictionary meta = new BdfDictionary();
|
clientHelper.setContactId(txn, g.getId(), c.getId());
|
||||||
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
|
|
||||||
try {
|
|
||||||
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
|
|
||||||
} catch (FormatException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
// Create and store the first local update
|
// Create and store the first local update
|
||||||
List<ClientVersion> versions = new ArrayList<>(clients);
|
List<ClientVersion> versions = new ArrayList<>(clients);
|
||||||
Collections.sort(versions);
|
Collections.sort(versions);
|
||||||
@@ -229,7 +222,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
|||||||
Map<ClientMajorVersion, Visibility> after =
|
Map<ClientMajorVersion, Visibility> after =
|
||||||
getVisibilities(newLocalStates, newRemoteStates);
|
getVisibilities(newLocalStates, newRemoteStates);
|
||||||
// Call hooks for any visibilities that have changed
|
// Call hooks for any visibilities that have changed
|
||||||
ContactId c = getContactId(txn, m.getGroupId());
|
ContactId c = clientHelper.getContactId(txn, m.getGroupId());
|
||||||
if (!before.equals(after)) {
|
if (!before.equals(after)) {
|
||||||
Contact contact = db.getContact(txn, c);
|
Contact contact = db.getContact(txn, c);
|
||||||
callVisibilityHooks(txn, contact, before, after);
|
callVisibilityHooks(txn, contact, before, after);
|
||||||
@@ -521,17 +514,6 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
|||||||
storeUpdate(txn, g, states, 1);
|
storeUpdate(txn, g, states, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContactId getContactId(Transaction txn, GroupId g)
|
|
||||||
throws DbException {
|
|
||||||
try {
|
|
||||||
BdfDictionary meta =
|
|
||||||
clientHelper.getGroupMetadataAsDictionary(txn, g);
|
|
||||||
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
|
|
||||||
} catch (FormatException e) {
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ClientState> updateStatesFromRemoteStates(
|
private List<ClientState> updateStatesFromRemoteStates(
|
||||||
List<ClientState> oldLocalStates, List<ClientState> remoteStates) {
|
List<ClientState> oldLocalStates, List<ClientState> remoteStates) {
|
||||||
Set<ClientMajorVersion> remoteSet = new HashSet<>();
|
Set<ClientMajorVersion> remoteSet = new HashSet<>();
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
|
Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1
|
||||||
|
Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1
|
||||||
|
Bridge obfs4 38.229.33.83:80 0BAC39417268B96B9F514E7F63FA6FBA1A788955 cert=VwEFpk9F/UN9JED7XpG1XOjm/O8ZCXK80oPecgWnNDZDv5pdkhq1OpbAH0wNqOT6H6BmRQ iat-mode=1
|
||||||
|
Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
|
||||||
|
Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
|
||||||
|
Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0
|
||||||
|
Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0
|
||||||
|
Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0
|
||||||
|
Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0
|
||||||
|
Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0
|
||||||
|
Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0
|
||||||
Bridge obfs4 78.46.188.239:37356 5A2D2F4158D0453E00C7C176978D3F41D69C45DB cert=3c0SwxpOisbohNxEc4tb875RVW8eOu1opRTVXJhafaKA/PNNtI7ElQIVOVZg1AdL5bxGCw iat-mode=0
|
Bridge obfs4 78.46.188.239:37356 5A2D2F4158D0453E00C7C176978D3F41D69C45DB cert=3c0SwxpOisbohNxEc4tb875RVW8eOu1opRTVXJhafaKA/PNNtI7ElQIVOVZg1AdL5bxGCw iat-mode=0
|
||||||
Bridge obfs4 52.15.78.72:9443 02069A3C5362476936B62BA6F5ACC41ABD573A9B cert=ijYG/OKc7kqu2YzKNFfeXN7/BG2BOgfEP2KyYEiGDQthnHbsOiTWHeIG0WJVW+BckzDgKw iat-mode=0
|
Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
|
||||||
Bridge obfs4 13.58.29.242:9443 0C58939A77DA6B6B29D4B5236A75865659607AE0 cert=OylWIEHb/ezpq1zWxW0sgKRn+9ARH2eOcQOZ8/Gew+4l+oKOhQ2jUX/Y+FSl61JorXZUWA iat-mode=0
|
|
||||||
Bridge obfs4 45.33.37.112:9443 60A609BB4ABE8D46E634AE81ED29ADAB7776B399 cert=t5v19WmNv5Sc2YPNr8RQids365W7MY8zJwQVkOxBjUMFomMWARDzsbYpcWLLcw0J9Gm+BQ iat-mode=0
|
|
||||||
Bridge meek_lite 0.0.2.0:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
package org.briarproject.bramble;
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
import org.briarproject.bramble.system.DefaultTaskSchedulerModule;
|
import org.briarproject.bramble.system.TimeTravelModule;
|
||||||
|
|
||||||
public interface BrambleCoreIntegrationTestEagerSingletons
|
public interface BrambleCoreIntegrationTestEagerSingletons
|
||||||
extends BrambleCoreEagerSingletons {
|
extends BrambleCoreEagerSingletons {
|
||||||
|
|
||||||
void inject(DefaultTaskSchedulerModule.EagerSingletons init);
|
void inject(TimeTravelModule.EagerSingletons init);
|
||||||
|
|
||||||
class Helper {
|
class Helper {
|
||||||
|
|
||||||
public static void injectEagerSingletons(
|
public static void injectEagerSingletons(
|
||||||
BrambleCoreIntegrationTestEagerSingletons c) {
|
BrambleCoreIntegrationTestEagerSingletons c) {
|
||||||
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(c);
|
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(c);
|
||||||
c.inject(new DefaultTaskSchedulerModule.EagerSingletons());
|
c.inject(new TimeTravelModule.EagerSingletons());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,15 @@ import org.briarproject.bramble.api.contact.PendingContactState;
|
|||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.AuthorInfo;
|
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
import org.jmock.Expectations;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -31,10 +28,6 @@ import static java.util.Collections.singletonList;
|
|||||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
@@ -46,7 +39,6 @@ import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
|||||||
import static org.briarproject.bramble.util.StringUtils.getRandomBase32String;
|
import static org.briarproject.bramble.util.StringUtils.getRandomBase32String;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class ContactManagerImplTest extends BrambleMockTestCase {
|
public class ContactManagerImplTest extends BrambleMockTestCase {
|
||||||
@@ -212,75 +204,6 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
assertTrue(contactManager.contactExists(remote.getId(), local));
|
assertTrue(contactManager.contactExists(remote.getId(), local));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetAuthorInfo() throws Exception {
|
|
||||||
Transaction txn = new Transaction(null, true);
|
|
||||||
|
|
||||||
context.checking(new DbExpectations() {{
|
|
||||||
oneOf(identityManager).getLocalAuthor(txn);
|
|
||||||
will(returnValue(localAuthor));
|
|
||||||
oneOf(db).getContactsByAuthorId(txn, remote.getId());
|
|
||||||
will(returnValue(singletonList(contact)));
|
|
||||||
}});
|
|
||||||
|
|
||||||
AuthorInfo authorInfo =
|
|
||||||
contactManager.getAuthorInfo(txn, remote.getId());
|
|
||||||
assertEquals(UNVERIFIED, authorInfo.getStatus());
|
|
||||||
assertEquals(contact.getAlias(), authorInfo.getAlias());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetAuthorInfoTransaction() throws DbException {
|
|
||||||
Transaction txn = new Transaction(null, true);
|
|
||||||
|
|
||||||
// check unknown author
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(identityManager).getLocalAuthor(txn);
|
|
||||||
will(returnValue(localAuthor));
|
|
||||||
oneOf(db).getContactsByAuthorId(txn, remote.getId());
|
|
||||||
will(returnValue(emptyList()));
|
|
||||||
}});
|
|
||||||
|
|
||||||
AuthorInfo authorInfo =
|
|
||||||
contactManager.getAuthorInfo(txn, remote.getId());
|
|
||||||
assertEquals(UNKNOWN, authorInfo.getStatus());
|
|
||||||
assertNull(authorInfo.getAlias());
|
|
||||||
|
|
||||||
// check unverified contact
|
|
||||||
checkAuthorInfoContext(txn, remote.getId(), singletonList(contact));
|
|
||||||
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
|
|
||||||
assertEquals(UNVERIFIED, authorInfo.getStatus());
|
|
||||||
assertEquals(contact.getAlias(), authorInfo.getAlias());
|
|
||||||
|
|
||||||
// check verified contact
|
|
||||||
Contact verified = getContact(remote, local, true);
|
|
||||||
checkAuthorInfoContext(txn, remote.getId(), singletonList(verified));
|
|
||||||
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
|
|
||||||
assertEquals(VERIFIED, authorInfo.getStatus());
|
|
||||||
assertEquals(verified.getAlias(), authorInfo.getAlias());
|
|
||||||
|
|
||||||
// check ourselves
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(identityManager).getLocalAuthor(txn);
|
|
||||||
will(returnValue(localAuthor));
|
|
||||||
never(db).getContactsByAuthorId(txn, remote.getId());
|
|
||||||
}});
|
|
||||||
|
|
||||||
authorInfo = contactManager.getAuthorInfo(txn, localAuthor.getId());
|
|
||||||
assertEquals(OURSELVES, authorInfo.getStatus());
|
|
||||||
assertNull(authorInfo.getAlias());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkAuthorInfoContext(Transaction txn, AuthorId authorId,
|
|
||||||
Collection<Contact> contacts) throws DbException {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(identityManager).getLocalAuthor(txn);
|
|
||||||
will(returnValue(localAuthor));
|
|
||||||
oneOf(db).getContactsByAuthorId(txn, authorId);
|
|
||||||
will(returnValue(contacts));
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetHandshakeLink() throws Exception {
|
public void testGetHandshakeLink() throws Exception {
|
||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.db;
|
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.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
@@ -69,6 +70,8 @@ import static java.util.Arrays.asList;
|
|||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
|
import static java.util.concurrent.TimeUnit.HOURS;
|
||||||
|
import static org.briarproject.bramble.api.db.DatabaseComponent.TIMER_NOT_STARTED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
||||||
@@ -510,11 +513,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the group is in the DB (which it's not)
|
// Check whether the group is in the DB (which it's not)
|
||||||
exactly(8).of(database).startTransaction();
|
exactly(10).of(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
exactly(8).of(database).containsGroup(txn, groupId);
|
exactly(10).of(database).containsGroup(txn, groupId);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(8).of(database).abortTransaction(txn);
|
exactly(10).of(database).abortTransaction(txn);
|
||||||
// Allow other checks to pass
|
// Allow other checks to pass
|
||||||
allowing(database).containsContact(txn, contactId);
|
allowing(database).containsContact(txn, contactId);
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
@@ -523,7 +526,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
eventExecutor, shutdownManager);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
db.getGroup(transaction, groupId));
|
db.getGroup(transaction, groupId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchGroupException expected) {
|
} catch (NoSuchGroupException expected) {
|
||||||
@@ -531,7 +534,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
db.getGroupMetadata(transaction, groupId));
|
db.getGroupMetadata(transaction, groupId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchGroupException expected) {
|
} catch (NoSuchGroupException expected) {
|
||||||
@@ -539,7 +542,23 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
|
db.getMessageIds(transaction, groupId));
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchGroupException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.transaction(true, transaction ->
|
||||||
|
db.getMessageIds(transaction, groupId, new Metadata()));
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchGroupException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.transaction(true, transaction ->
|
||||||
db.getMessageMetadata(transaction, groupId));
|
db.getMessageMetadata(transaction, groupId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchGroupException expected) {
|
} catch (NoSuchGroupException expected) {
|
||||||
@@ -547,7 +566,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
db.getMessageMetadata(transaction, groupId,
|
db.getMessageMetadata(transaction, groupId,
|
||||||
new Metadata()));
|
new Metadata()));
|
||||||
fail();
|
fail();
|
||||||
@@ -556,7 +575,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
db.getMessageStatus(transaction, contactId, groupId));
|
db.getMessageStatus(transaction, contactId, groupId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchGroupException expected) {
|
} catch (NoSuchGroupException expected) {
|
||||||
@@ -594,11 +613,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the message is in the DB (which it's not)
|
// Check whether the message is in the DB (which it's not)
|
||||||
exactly(12).of(database).startTransaction();
|
exactly(15).of(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
exactly(12).of(database).containsMessage(txn, messageId);
|
exactly(15).of(database).containsMessage(txn, messageId);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(12).of(database).abortTransaction(txn);
|
exactly(15).of(database).abortTransaction(txn);
|
||||||
// Allow other checks to pass
|
// Allow other checks to pass
|
||||||
allowing(database).containsContact(txn, contactId);
|
allowing(database).containsContact(txn, contactId);
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
@@ -623,7 +642,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
db.getMessage(transaction, messageId));
|
db.getMessage(transaction, messageId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchMessageException expected) {
|
} catch (NoSuchMessageException expected) {
|
||||||
@@ -631,7 +650,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
db.getMessageMetadata(transaction, messageId));
|
db.getMessageMetadata(transaction, messageId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchMessageException expected) {
|
} catch (NoSuchMessageException expected) {
|
||||||
@@ -639,7 +658,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
db.getMessageState(transaction, messageId));
|
db.getMessageState(transaction, messageId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchMessageException expected) {
|
} catch (NoSuchMessageException expected) {
|
||||||
@@ -647,7 +666,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
db.getMessageStatus(transaction, contactId, messageId));
|
db.getMessageStatus(transaction, contactId, messageId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchMessageException expected) {
|
} catch (NoSuchMessageException expected) {
|
||||||
@@ -662,6 +681,15 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.transaction(false, transaction ->
|
||||||
|
db.setCleanupTimerDuration(transaction, message.getId(),
|
||||||
|
HOURS.toMillis(1)));
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchMessageException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.setMessagePermanent(transaction, message.getId()));
|
db.setMessagePermanent(transaction, message.getId()));
|
||||||
@@ -687,7 +715,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
db.getMessageDependencies(transaction, messageId));
|
db.getMessageDependencies(transaction, messageId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchMessageException expected) {
|
} catch (NoSuchMessageException expected) {
|
||||||
@@ -695,12 +723,28 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(true, transaction ->
|
||||||
db.getMessageDependents(transaction, messageId));
|
db.getMessageDependents(transaction, messageId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchMessageException expected) {
|
} catch (NoSuchMessageException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.transaction(false, transaction ->
|
||||||
|
db.startCleanupTimer(transaction, messageId));
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchMessageException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.transaction(false, transaction ->
|
||||||
|
db.stopCleanupTimer(transaction, messageId));
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchMessageException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -981,6 +1025,9 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
|
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
|
||||||
|
will(returnValue(true));
|
||||||
|
oneOf(database).startCleanupTimer(txn, messageId);
|
||||||
|
will(returnValue(TIMER_NOT_STARTED)); // No cleanup duration was set
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
|
oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
|
||||||
}});
|
}});
|
||||||
@@ -993,6 +1040,56 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReceiveDuplicateAck() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(database).startTransaction();
|
||||||
|
will(returnValue(txn));
|
||||||
|
oneOf(database).containsContact(txn, contactId);
|
||||||
|
will(returnValue(true));
|
||||||
|
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
||||||
|
will(returnValue(true));
|
||||||
|
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
|
||||||
|
will(returnValue(false)); // Already acked
|
||||||
|
oneOf(database).commitTransaction(txn);
|
||||||
|
}});
|
||||||
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
|
db.transaction(false, transaction -> {
|
||||||
|
Ack a = new Ack(singletonList(messageId));
|
||||||
|
db.receiveAck(transaction, contactId, a);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReceiveAckWithCleanupTimer() throws Exception {
|
||||||
|
long deadline = System.currentTimeMillis();
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(database).startTransaction();
|
||||||
|
will(returnValue(txn));
|
||||||
|
oneOf(database).containsContact(txn, contactId);
|
||||||
|
will(returnValue(true));
|
||||||
|
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
||||||
|
will(returnValue(true));
|
||||||
|
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
|
||||||
|
will(returnValue(true));
|
||||||
|
oneOf(database).startCleanupTimer(txn, messageId);
|
||||||
|
will(returnValue(deadline));
|
||||||
|
oneOf(database).commitTransaction(txn);
|
||||||
|
oneOf(eventBus).broadcast(with(any(
|
||||||
|
CleanupTimerStartedEvent.class)));
|
||||||
|
oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
|
||||||
|
}});
|
||||||
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
|
db.transaction(false, transaction -> {
|
||||||
|
Ack a = new Ack(singletonList(messageId));
|
||||||
|
db.receiveAck(transaction, contactId, a);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReceiveMessage() throws Exception {
|
public void testReceiveMessage() throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
|
|||||||
@@ -41,8 +41,12 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.sql.Driver;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@@ -53,10 +57,11 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.emptyMap;
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
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.db.Metadata.REMOVE;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
@@ -351,7 +356,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
assertTrue(ids.isEmpty());
|
assertTrue(ids.isEmpty());
|
||||||
|
|
||||||
// Sharing the message should make it sendable
|
// Sharing the message should make it sendable
|
||||||
db.setMessageShared(txn, messageId);
|
db.setMessageShared(txn, messageId, true);
|
||||||
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
|
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
|
||||||
assertEquals(singletonList(messageId), ids);
|
assertEquals(singletonList(messageId), ids);
|
||||||
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
|
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
|
||||||
@@ -631,8 +636,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
|
|
||||||
// The group should not be visible to the contact
|
// The group should not be visible to the contact
|
||||||
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
|
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
|
||||||
assertEquals(emptyMap(),
|
assertTrue(db.getGroupVisibility(txn, groupId).isEmpty());
|
||||||
db.getGroupVisibility(txn, groupId));
|
|
||||||
|
|
||||||
// Make the group visible to the contact
|
// Make the group visible to the contact
|
||||||
db.addGroupVisibility(txn, contactId, groupId, false);
|
db.addGroupVisibility(txn, contactId, groupId, false);
|
||||||
@@ -655,8 +659,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
// Make the group invisible again
|
// Make the group invisible again
|
||||||
db.removeGroupVisibility(txn, contactId, groupId);
|
db.removeGroupVisibility(txn, contactId, groupId);
|
||||||
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
|
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
|
||||||
assertEquals(emptyMap(),
|
assertTrue(db.getGroupVisibility(txn, groupId).isEmpty());
|
||||||
db.getGroupVisibility(txn, groupId));
|
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
db.close();
|
db.close();
|
||||||
@@ -2040,7 +2043,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
|
assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
|
||||||
|
|
||||||
// Share the message - now it should be sendable immediately
|
// Share the message - now it should be sendable immediately
|
||||||
db.setMessageShared(txn, messageId);
|
db.setMessageShared(txn, messageId, true);
|
||||||
assertEquals(0, db.getNextSendTime(txn, contactId));
|
assertEquals(0, db.getNextSendTime(txn, contactId));
|
||||||
|
|
||||||
// Mark the message as requested - it should still be sendable
|
// Mark the message as requested - it should still be sendable
|
||||||
@@ -2347,6 +2350,148 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShutdownGracefully() throws Exception {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
db.close();
|
||||||
|
open(true);
|
||||||
|
assertFalse(db.wasDirtyOnInitialisation());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShutdownDirty() throws Exception {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
|
||||||
|
// We want to simulate a dirty shutdown here which would normally be
|
||||||
|
// caused by an empty battery or by force closing the Android app.
|
||||||
|
// As there is no obvious way to simulate this, we're artificially
|
||||||
|
// causing an SqlException during close() here by unloading the JDBC
|
||||||
|
// drivers.
|
||||||
|
List<String> unloadedDrivers = unloadDrivers();
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.close();
|
||||||
|
fail();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// continue
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reloading drivers to continue so that we're able to work with the
|
||||||
|
// database again.
|
||||||
|
reloadDrivers(unloadedDrivers);
|
||||||
|
|
||||||
|
db = open(true);
|
||||||
|
assertTrue(db.wasDirtyOnInitialisation());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShutdownDirtyThenGracefully() throws Exception {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
|
||||||
|
// Simulating a dirty shutdown here, look at #testShutdownDirty for
|
||||||
|
// details.
|
||||||
|
List<String> unloadedDrivers = unloadDrivers();
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.close();
|
||||||
|
fail();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadDrivers(unloadedDrivers);
|
||||||
|
|
||||||
|
db = open(true);
|
||||||
|
assertTrue(db.wasDirtyOnInitialisation());
|
||||||
|
|
||||||
|
db.close();
|
||||||
|
db = open(true);
|
||||||
|
assertFalse(db.wasDirtyOnInitialisation());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanupTimer() throws Exception {
|
||||||
|
long duration = 60_000;
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
AtomicLong time = new AtomicLong(now);
|
||||||
|
Database<Connection> db =
|
||||||
|
open(false, new TestMessageFactory(), new SettableClock(time));
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
|
// No messages should be due or scheduled for deletion
|
||||||
|
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||||
|
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||||
|
|
||||||
|
// Add a group and a message
|
||||||
|
db.addGroup(txn, group);
|
||||||
|
db.addMessage(txn, message, DELIVERED, false, false, null);
|
||||||
|
|
||||||
|
// No messages should be due or scheduled for deletion
|
||||||
|
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||||
|
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||||
|
|
||||||
|
// Set the message's cleanup timer duration
|
||||||
|
db.setCleanupTimerDuration(txn, messageId, duration);
|
||||||
|
|
||||||
|
// No messages should be due or scheduled for deletion
|
||||||
|
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||||
|
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||||
|
|
||||||
|
// Start the message's cleanup timer
|
||||||
|
assertEquals(now + duration, db.startCleanupTimer(txn, messageId));
|
||||||
|
|
||||||
|
// The timer can't be started again
|
||||||
|
assertEquals(TIMER_NOT_STARTED, db.startCleanupTimer(txn, messageId));
|
||||||
|
|
||||||
|
// No messages should be due for deletion, but the message should be
|
||||||
|
// scheduled for deletion
|
||||||
|
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||||
|
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||||
|
|
||||||
|
// Stop the timer
|
||||||
|
db.stopCleanupTimer(txn, messageId);
|
||||||
|
|
||||||
|
// No messages should be due or scheduled for deletion
|
||||||
|
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||||
|
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||||
|
|
||||||
|
// Start the timer again
|
||||||
|
assertEquals(now + duration, db.startCleanupTimer(txn, messageId));
|
||||||
|
|
||||||
|
// No messages should be due for deletion, but the message should be
|
||||||
|
// scheduled for deletion
|
||||||
|
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||||
|
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||||
|
|
||||||
|
// 1 ms before the timer expires, no messages should be due for
|
||||||
|
// deletion but the message should be scheduled for deletion
|
||||||
|
time.set(now + duration - 1);
|
||||||
|
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||||
|
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||||
|
|
||||||
|
// When the timer expires, the message should be due and scheduled for
|
||||||
|
// deletion
|
||||||
|
time.set(now + duration);
|
||||||
|
assertEquals(singletonMap(groupId, singletonList(messageId)),
|
||||||
|
db.getMessagesToDelete(txn));
|
||||||
|
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||||
|
|
||||||
|
// 1 ms after the timer expires, the message should be due and
|
||||||
|
// scheduled for deletion
|
||||||
|
time.set(now + duration + 1);
|
||||||
|
assertEquals(singletonMap(groupId, singletonList(messageId)),
|
||||||
|
db.getMessagesToDelete(txn));
|
||||||
|
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||||
|
|
||||||
|
// Once the message has been deleted, it should no longer be due
|
||||||
|
// or scheduled for deletion
|
||||||
|
db.deleteMessage(txn, messageId);
|
||||||
|
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||||
|
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||||
|
}
|
||||||
|
|
||||||
private Database<Connection> open(boolean resume) throws Exception {
|
private Database<Connection> open(boolean resume) throws Exception {
|
||||||
return open(resume, new TestMessageFactory(), new SystemClock());
|
return open(resume, new TestMessageFactory(), new SystemClock());
|
||||||
}
|
}
|
||||||
@@ -2402,6 +2547,31 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
rootKey, alice);
|
rootKey, alice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> unloadDrivers() {
|
||||||
|
Enumeration<Driver> drivers = DriverManager.getDrivers();
|
||||||
|
List<String> unloaded = new ArrayList<>();
|
||||||
|
while (drivers.hasMoreElements()) {
|
||||||
|
Driver d = drivers.nextElement();
|
||||||
|
try {
|
||||||
|
DriverManager.deregisterDriver(d);
|
||||||
|
unloaded.add(d.getClass().getName());
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unloaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reloadDrivers(List<String> unloadedDrivers)
|
||||||
|
throws ClassNotFoundException, IllegalAccessException,
|
||||||
|
InstantiationException, SQLException {
|
||||||
|
for (String driverName : unloadedDrivers) {
|
||||||
|
DriverManager.registerDriver(
|
||||||
|
(Driver) Class.forName(driverName).newInstance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
deleteTestDirectory(testDir);
|
deleteTestDirectory(testDir);
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||||
|
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link TaskScheduler} for use in tests. The scheduler keeps all scheduled
|
||||||
|
* tasks in a queue until {@link #runTasks()} is called.
|
||||||
|
*/
|
||||||
|
@NotNullByDefault
|
||||||
|
class TestTaskScheduler implements TaskScheduler {
|
||||||
|
|
||||||
|
private final Queue<Task> queue = new PriorityBlockingQueue<>();
|
||||||
|
private final Clock clock;
|
||||||
|
|
||||||
|
TestTaskScheduler(Clock clock) {
|
||||||
|
this.clock = clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cancellable schedule(Runnable task, Executor executor, long delay,
|
||||||
|
TimeUnit unit) {
|
||||||
|
AtomicBoolean cancelled = new AtomicBoolean(false);
|
||||||
|
return schedule(task, executor, delay, unit, cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
|
||||||
|
long delay, long interval, TimeUnit unit) {
|
||||||
|
AtomicBoolean cancelled = new AtomicBoolean(false);
|
||||||
|
return scheduleWithFixedDelay(task, executor, delay, interval, unit,
|
||||||
|
cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Cancellable schedule(Runnable task, Executor executor, long delay,
|
||||||
|
TimeUnit unit, AtomicBoolean cancelled) {
|
||||||
|
long delayMillis = MILLISECONDS.convert(delay, unit);
|
||||||
|
long dueMillis = clock.currentTimeMillis() + delayMillis;
|
||||||
|
Task t = new Task(task, executor, dueMillis, cancelled);
|
||||||
|
queue.add(t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
|
||||||
|
long delay, long interval, TimeUnit unit, AtomicBoolean cancelled) {
|
||||||
|
// All executions of this periodic task share a cancelled flag
|
||||||
|
Runnable wrapped = () -> {
|
||||||
|
task.run();
|
||||||
|
scheduleWithFixedDelay(task, executor, interval, interval, unit,
|
||||||
|
cancelled);
|
||||||
|
};
|
||||||
|
return schedule(wrapped, executor, delay, unit, cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs any scheduled tasks that are due.
|
||||||
|
*/
|
||||||
|
void runTasks() throws InterruptedException {
|
||||||
|
long now = clock.currentTimeMillis();
|
||||||
|
while (true) {
|
||||||
|
Task t = queue.peek();
|
||||||
|
if (t == null || t.dueMillis > now) return;
|
||||||
|
t = queue.poll();
|
||||||
|
// Submit the task to its executor and wait for it to finish
|
||||||
|
if (!t.run().await(1, MINUTES)) fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Task
|
||||||
|
implements Cancellable, Comparable<Task> {
|
||||||
|
|
||||||
|
private final Runnable task;
|
||||||
|
private final Executor executor;
|
||||||
|
private final long dueMillis;
|
||||||
|
private final AtomicBoolean cancelled;
|
||||||
|
|
||||||
|
private Task(Runnable task, Executor executor, long dueMillis,
|
||||||
|
AtomicBoolean cancelled) {
|
||||||
|
this.task = task;
|
||||||
|
this.executor = executor;
|
||||||
|
this.dueMillis = dueMillis;
|
||||||
|
this.cancelled = cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("UseCompareMethod") // Animal Sniffer
|
||||||
|
@Override
|
||||||
|
public int compareTo(Task task) {
|
||||||
|
return Long.valueOf(dueMillis).compareTo(task.dueMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits the task to its executor and returns a latch that will be
|
||||||
|
* released when the task finishes.
|
||||||
|
*/
|
||||||
|
public CountDownLatch run() {
|
||||||
|
if (cancelled.get()) return new CountDownLatch(0);
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
executor.execute(() -> {
|
||||||
|
task.run();
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
return latch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
cancelled.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||||
|
import org.briarproject.bramble.test.SettableClock;
|
||||||
|
import org.briarproject.bramble.test.TimeTravel;
|
||||||
|
|
||||||
|
import java.util.concurrent.RejectedExecutionHandler;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class TimeTravelModule {
|
||||||
|
|
||||||
|
public static class EagerSingletons {
|
||||||
|
@Inject
|
||||||
|
TaskScheduler scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ScheduledExecutorService scheduledExecutorService;
|
||||||
|
private final Clock clock;
|
||||||
|
private final TaskScheduler taskScheduler;
|
||||||
|
private final TimeTravel timeTravel;
|
||||||
|
|
||||||
|
public TimeTravelModule() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeTravelModule(boolean travel) {
|
||||||
|
// Discard tasks that are submitted during shutdown
|
||||||
|
RejectedExecutionHandler policy =
|
||||||
|
new ScheduledThreadPoolExecutor.DiscardPolicy();
|
||||||
|
scheduledExecutorService =
|
||||||
|
new ScheduledThreadPoolExecutor(1, policy);
|
||||||
|
if (travel) {
|
||||||
|
// Use a SettableClock and TestTaskScheduler to allow time travel
|
||||||
|
AtomicLong time = new AtomicLong(System.currentTimeMillis());
|
||||||
|
clock = new SettableClock(time);
|
||||||
|
TestTaskScheduler testTaskScheduler = new TestTaskScheduler(clock);
|
||||||
|
taskScheduler = testTaskScheduler;
|
||||||
|
timeTravel = new TimeTravel() {
|
||||||
|
@Override
|
||||||
|
public void setCurrentTimeMillis(long now)
|
||||||
|
throws InterruptedException {
|
||||||
|
time.set(now);
|
||||||
|
testTaskScheduler.runTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCurrentTimeMillis(long add)
|
||||||
|
throws InterruptedException {
|
||||||
|
time.addAndGet(add);
|
||||||
|
testTaskScheduler.runTasks();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Use the default clock and task scheduler
|
||||||
|
clock = new SystemClock();
|
||||||
|
taskScheduler = new TaskSchedulerImpl(scheduledExecutorService);
|
||||||
|
timeTravel = new TimeTravel() {
|
||||||
|
@Override
|
||||||
|
public void setCurrentTimeMillis(long now) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCurrentTimeMillis(long add) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
Clock provideClock() {
|
||||||
|
return clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
TaskScheduler provideTaskScheduler(LifecycleManager lifecycleManager) {
|
||||||
|
lifecycleManager.registerForShutdown(scheduledExecutorService);
|
||||||
|
return taskScheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
TimeTravel provideTimeTravel() {
|
||||||
|
return timeTravel;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,8 @@ package org.briarproject.bramble.test;
|
|||||||
import org.briarproject.bramble.api.FeatureFlags;
|
import org.briarproject.bramble.api.FeatureFlags;
|
||||||
import org.briarproject.bramble.battery.DefaultBatteryManagerModule;
|
import org.briarproject.bramble.battery.DefaultBatteryManagerModule;
|
||||||
import org.briarproject.bramble.event.DefaultEventExecutorModule;
|
import org.briarproject.bramble.event.DefaultEventExecutorModule;
|
||||||
import org.briarproject.bramble.system.DefaultTaskSchedulerModule;
|
|
||||||
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
|
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
|
||||||
|
import org.briarproject.bramble.system.TimeTravelModule;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
@@ -12,16 +12,37 @@ import dagger.Provides;
|
|||||||
@Module(includes = {
|
@Module(includes = {
|
||||||
DefaultBatteryManagerModule.class,
|
DefaultBatteryManagerModule.class,
|
||||||
DefaultEventExecutorModule.class,
|
DefaultEventExecutorModule.class,
|
||||||
DefaultTaskSchedulerModule.class,
|
|
||||||
DefaultWakefulIoExecutorModule.class,
|
DefaultWakefulIoExecutorModule.class,
|
||||||
TestDatabaseConfigModule.class,
|
TestDatabaseConfigModule.class,
|
||||||
TestPluginConfigModule.class,
|
TestPluginConfigModule.class,
|
||||||
TestSecureRandomModule.class
|
TestSecureRandomModule.class,
|
||||||
|
TimeTravelModule.class
|
||||||
})
|
})
|
||||||
public class BrambleCoreIntegrationTestModule {
|
public class BrambleCoreIntegrationTestModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
FeatureFlags provideFeatureFlags() {
|
FeatureFlags provideFeatureFlags() {
|
||||||
return () -> true;
|
return new FeatureFlags() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldEnableImageAttachments() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldEnableProfilePictures() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldEnableDisappearingMessages() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldEnableConnectViaBluetooth() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import static org.briarproject.bramble.test.TestUtils.getContact;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID;
|
|
||||||
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
|
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
|
||||||
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
|
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -60,8 +59,6 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
|
|||||||
private final ClientId clientId = getClientId();
|
private final ClientId clientId = getClientId();
|
||||||
private final long now = System.currentTimeMillis();
|
private final long now = System.currentTimeMillis();
|
||||||
private final Transaction txn = new Transaction(null, false);
|
private final Transaction txn = new Transaction(null, false);
|
||||||
private final BdfDictionary groupMeta = BdfDictionary.of(
|
|
||||||
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
|
|
||||||
|
|
||||||
private ClientVersioningManagerImpl createInstance() {
|
private ClientVersioningManagerImpl createInstance() {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
@@ -123,8 +120,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(db).addGroup(txn, contactGroup);
|
oneOf(db).addGroup(txn, contactGroup);
|
||||||
oneOf(db).setGroupVisibility(txn, contact.getId(),
|
oneOf(db).setGroupVisibility(txn, contact.getId(),
|
||||||
contactGroup.getId(), SHARED);
|
contactGroup.getId(), SHARED);
|
||||||
oneOf(clientHelper).mergeGroupMetadata(txn, contactGroup.getId(),
|
oneOf(clientHelper).setContactId(txn, contactGroup.getId(),
|
||||||
groupMeta);
|
contact.getId());
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(now));
|
will(returnValue(now));
|
||||||
oneOf(clientHelper).createMessage(contactGroup.getId(), now,
|
oneOf(clientHelper).createMessage(contactGroup.getId(), now,
|
||||||
@@ -460,9 +457,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(db).deleteMessage(txn, oldRemoteUpdateId);
|
oneOf(db).deleteMessage(txn, oldRemoteUpdateId);
|
||||||
oneOf(db).deleteMessageMetadata(txn, oldRemoteUpdateId);
|
oneOf(db).deleteMessageMetadata(txn, oldRemoteUpdateId);
|
||||||
// Get contact ID
|
// Get contact ID
|
||||||
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
|
oneOf(clientHelper).getContactId(txn, contactGroup.getId());
|
||||||
contactGroup.getId());
|
will(returnValue(contact.getId()));
|
||||||
will(returnValue(groupMeta));
|
|
||||||
// No states or visibilities have changed
|
// No states or visibilities have changed
|
||||||
}});
|
}});
|
||||||
|
|
||||||
@@ -492,10 +488,9 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
|
|||||||
// Load the latest local update
|
// Load the latest local update
|
||||||
oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId);
|
oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId);
|
||||||
will(returnValue(oldLocalUpdateBody));
|
will(returnValue(oldLocalUpdateBody));
|
||||||
// Get client ID
|
// Get contact ID
|
||||||
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
|
oneOf(clientHelper).getContactId(txn, contactGroup.getId());
|
||||||
contactGroup.getId());
|
will(returnValue(contact.getId()));
|
||||||
will(returnValue(groupMeta));
|
|
||||||
// No states or visibilities have changed
|
// No states or visibilities have changed
|
||||||
}});
|
}});
|
||||||
|
|
||||||
@@ -546,8 +541,6 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
|
|||||||
BdfDictionary newLocalUpdateMeta = BdfDictionary.of(
|
BdfDictionary newLocalUpdateMeta = BdfDictionary.of(
|
||||||
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
|
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
|
||||||
new BdfEntry(MSG_KEY_LOCAL, true));
|
new BdfEntry(MSG_KEY_LOCAL, true));
|
||||||
BdfDictionary groupMeta = BdfDictionary.of(
|
|
||||||
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(clientHelper).toList(newRemoteUpdate);
|
oneOf(clientHelper).toList(newRemoteUpdate);
|
||||||
@@ -577,9 +570,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate,
|
oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate,
|
||||||
newLocalUpdateMeta, true, false);
|
newLocalUpdateMeta, true, false);
|
||||||
// The client's visibility has changed
|
// The client's visibility has changed
|
||||||
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
|
oneOf(clientHelper).getContactId(txn, contactGroup.getId());
|
||||||
contactGroup.getId());
|
will(returnValue(contact.getId()));
|
||||||
will(returnValue(groupMeta));
|
|
||||||
oneOf(db).getContact(txn, contact.getId());
|
oneOf(db).getContact(txn, contact.getId());
|
||||||
will(returnValue(contact));
|
will(returnValue(contact));
|
||||||
oneOf(hook).onClientVisibilityChanging(txn, contact, visibility);
|
oneOf(hook).onClientVisibilityChanging(txn, contact, visibility);
|
||||||
@@ -619,8 +611,6 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
|
|||||||
BdfDictionary newLocalUpdateMeta = BdfDictionary.of(
|
BdfDictionary newLocalUpdateMeta = BdfDictionary.of(
|
||||||
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
|
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
|
||||||
new BdfEntry(MSG_KEY_LOCAL, true));
|
new BdfEntry(MSG_KEY_LOCAL, true));
|
||||||
BdfDictionary groupMeta = BdfDictionary.of(
|
|
||||||
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(clientHelper).toList(newRemoteUpdate);
|
oneOf(clientHelper).toList(newRemoteUpdate);
|
||||||
@@ -650,9 +640,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate,
|
oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate,
|
||||||
newLocalUpdateMeta, true, false);
|
newLocalUpdateMeta, true, false);
|
||||||
// The client's visibility has changed
|
// The client's visibility has changed
|
||||||
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
|
oneOf(clientHelper).getContactId(txn, contactGroup.getId());
|
||||||
contactGroup.getId());
|
will(returnValue(contact.getId()));
|
||||||
will(returnValue(groupMeta));
|
|
||||||
oneOf(db).getContact(txn, contact.getId());
|
oneOf(db).getContact(txn, contact.getId());
|
||||||
will(returnValue(contact));
|
will(returnValue(contact));
|
||||||
oneOf(hook).onClientVisibilityChanging(txn, contact, INVISIBLE);
|
oneOf(hook).onClientVisibilityChanging(txn, contact, INVISIBLE);
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ dependencies {
|
|||||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||||
implementation 'net.java.dev.jna:jna:4.5.2'
|
implementation 'net.java.dev.jna:jna:4.5.2'
|
||||||
implementation 'net.java.dev.jna:jna-platform:4.5.2'
|
implementation 'net.java.dev.jna:jna-platform:4.5.2'
|
||||||
tor 'org.briarproject:tor:0.3.5.10@zip'
|
tor 'org.briarproject:tor:0.3.5.13-1@zip'
|
||||||
tor 'org.briarproject:obfs4proxy:0.0.7@zip'
|
tor 'org.briarproject:obfs4proxy:0.0.12-dev-40245c4a@zip'
|
||||||
|
|
||||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import org.briarproject.bramble.system.JavaSystemModule;
|
|||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
|
|
||||||
@Module(includes = {
|
@Module(includes = {
|
||||||
|
CircumventionModule.class,
|
||||||
JavaNetworkModule.class,
|
JavaNetworkModule.class,
|
||||||
JavaSystemModule.class,
|
JavaSystemModule.class,
|
||||||
CircumventionModule.class,
|
|
||||||
SocksModule.class
|
SocksModule.class
|
||||||
})
|
})
|
||||||
public class BrambleJavaModule {
|
public class BrambleJavaModule {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import org.briarproject.bramble.api.network.NetworkStatus;
|
|||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
|
||||||
|
import java.net.Inet4Address;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
@@ -14,8 +16,8 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static java.net.NetworkInterface.getNetworkInterfaces;
|
import static java.net.NetworkInterface.getNetworkInterfaces;
|
||||||
import static java.util.Collections.list;
|
import static java.util.Collections.list;
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -23,7 +25,7 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
|||||||
class JavaNetworkManager implements NetworkManager {
|
class JavaNetworkManager implements NetworkManager {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(JavaNetworkManager.class.getName());
|
getLogger(JavaNetworkManager.class.getName());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
JavaNetworkManager() {
|
JavaNetworkManager() {
|
||||||
@@ -31,26 +33,28 @@ class JavaNetworkManager implements NetworkManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetworkStatus getNetworkStatus() {
|
public NetworkStatus getNetworkStatus() {
|
||||||
boolean connected = false;
|
boolean connected = false, hasIpv4 = false, hasIpv6Unicast = false;
|
||||||
try {
|
try {
|
||||||
Enumeration<NetworkInterface> interfaces = getNetworkInterfaces();
|
Enumeration<NetworkInterface> interfaces = getNetworkInterfaces();
|
||||||
if (interfaces != null) {
|
if (interfaces == null) {
|
||||||
|
LOG.info("No network interfaces");
|
||||||
|
} else {
|
||||||
for (NetworkInterface i : list(interfaces)) {
|
for (NetworkInterface i : list(interfaces)) {
|
||||||
if (i.isLoopback()) continue;
|
if (i.isLoopback() || !i.isUp()) continue;
|
||||||
if (i.isUp() && i.getInetAddresses().hasMoreElements()) {
|
for (InetAddress addr : list(i.getInetAddresses())) {
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info("Interface " + i.getDisplayName() +
|
|
||||||
" is up with at least one address.");
|
|
||||||
}
|
|
||||||
connected = true;
|
connected = true;
|
||||||
break;
|
if (addr instanceof Inet4Address) {
|
||||||
|
hasIpv4 = true;
|
||||||
|
} else if (!addr.isMulticastAddress()) {
|
||||||
|
hasIpv6Unicast = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
return new NetworkStatus(connected, false);
|
return new NetworkStatus(connected, false, !hasIpv4 && hasIpv6Unicast);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.OsUtils.isLinux;
|
import static org.briarproject.bramble.util.OsUtils.isLinux;
|
||||||
|
|
||||||
@@ -96,8 +97,15 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
|
|||||||
String architecture = null;
|
String architecture = null;
|
||||||
if (isLinux()) {
|
if (isLinux()) {
|
||||||
String arch = System.getProperty("os.arch");
|
String arch = System.getProperty("os.arch");
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("System's os.arch is " + arch);
|
||||||
|
}
|
||||||
if (arch.equals("amd64")) {
|
if (arch.equals("amd64")) {
|
||||||
architecture = "linux-x86_64";
|
architecture = "linux-x86_64";
|
||||||
|
} else if (arch.equals("aarch64")) {
|
||||||
|
architecture = "linux-aarch64";
|
||||||
|
} else if (arch.equals("arm")) {
|
||||||
|
architecture = "linux-armhf";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (architecture == null) {
|
if (architecture == null) {
|
||||||
@@ -105,6 +113,10 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("The selected architecture for Tor is " + architecture);
|
||||||
|
}
|
||||||
|
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
|
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class BridgeTest extends BrambleTestCase {
|
|||||||
return component.getCircumventionProvider().getBridges(false);
|
return component.getCircumventionProvider().getBridges(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static long TIMEOUT = SECONDS.toMillis(30);
|
private final static long TIMEOUT = SECONDS.toMillis(60);
|
||||||
|
|
||||||
private final static Logger LOG = getLogger(BridgeTest.class.getName());
|
private final static Logger LOG = getLogger(BridgeTest.class.getName());
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ dependencyVerification {
|
|||||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
'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.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||||
'org.briarproject:obfs4proxy:0.0.7:obfs4proxy-0.0.7.zip:5b2f693262ce43a7e130f7cc7d5d1617925330640a2eb6d71085e95df8ee0642',
|
'org.briarproject:obfs4proxy:0.0.12-dev-40245c4a:obfs4proxy-0.0.12-dev-40245c4a.zip:172029e7058b3a83ac93ac4991a44bf76e16ce8d46f558f5836d57da3cb3a766',
|
||||||
'org.briarproject:tor:0.3.5.10:tor-0.3.5.10.zip:7b387d3523ae8af289c23be59dc4c64ec5d3721385d7825a09705095e3318d5c',
|
'org.briarproject:tor:0.3.5.13-1:tor-0.3.5.13-1.zip:ef35c16bf8dc1f4c75ed71d9f55e4514f383d124ec96b859aca647c990927c99',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||||
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||||
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||||
|
|||||||
2
briar-android/.gitignore
vendored
2
briar-android/.gitignore
vendored
@@ -11,3 +11,5 @@ src/main/res/values-iw
|
|||||||
/fastlane/metadata/android/*/images
|
/fastlane/metadata/android/*/images
|
||||||
/fastlane/report.xml
|
/fastlane/report.xml
|
||||||
/fastlane/README.md
|
/fastlane/README.md
|
||||||
|
|
||||||
|
/fastlane/metadata/android/*/changelogs
|
||||||
@@ -9,3 +9,19 @@ source_lang = en
|
|||||||
type = ANDROID
|
type = ANDROID
|
||||||
minimum_perc = 80
|
minimum_perc = 80
|
||||||
|
|
||||||
|
[briar.google-play-short-description]
|
||||||
|
# https://support.google.com/googleplay/android-developer/answer/9844778?hl=en#zippy=%2Cview-list-of-available-languages
|
||||||
|
lang_map = en: en-US, de: de-DE, gl: gl-ES, tr: tr-TR, zh-Hans: zh-CN
|
||||||
|
file_filter = fastlane/metadata/android/<lang>/short_description.txt
|
||||||
|
source_file = fastlane/metadata/android/en-US/short_description.txt
|
||||||
|
source_lang = en
|
||||||
|
type = TXT
|
||||||
|
minimum_perc = 100
|
||||||
|
|
||||||
|
[briar.google-play-full-description]
|
||||||
|
lang_map = en: en-US, de: de-DE, gl: gl-ES, tr: tr-TR, zh-Hans: zh-CN
|
||||||
|
file_filter = fastlane/metadata/android/<lang>/full_description.txt
|
||||||
|
source_file = fastlane/metadata/android/en-US/full_description.txt
|
||||||
|
source_lang = en
|
||||||
|
type = TXT
|
||||||
|
minimum_perc = 100
|
||||||
|
|||||||
1
briar-android/artwork/notification_signout.svg
Normal file
1
briar-android/artwork/notification_signout.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16,17V14H9V10H16V7L21,12L16,17M14,2A2,2 0 0,1 16,4V6H14V4H5V20H14V18H16V20A2,2 0 0,1 14,22H5A2,2 0 0,1 3,20V4A2,2 0 0,1 5,2H14Z" /></svg>
|
||||||
|
After Width: | Height: | Size: 423 B |
@@ -16,15 +16,21 @@ def getStdout = { command, defaultValue ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 30
|
||||||
buildToolsVersion '29.0.2'
|
buildToolsVersion '30.0.2'
|
||||||
|
|
||||||
|
packagingOptions {
|
||||||
|
doNotStrip '**/*.so'
|
||||||
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 28
|
targetSdkVersion 29
|
||||||
versionCode 10209
|
versionCode 10302
|
||||||
versionName "1.2.9"
|
versionName "1.3.2"
|
||||||
applicationId "org.briarproject.briar.android"
|
applicationId "org.briarproject.briar.android"
|
||||||
|
|
||||||
|
vectorDrawables.useSupportLibrary = true
|
||||||
buildConfigField "String", "GitHash",
|
buildConfigField "String", "GitHash",
|
||||||
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
||||||
def now = (long) (System.currentTimeMillis() / 1000)
|
def now = (long) (System.currentTimeMillis() / 1000)
|
||||||
@@ -74,6 +80,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testOptions {
|
testOptions {
|
||||||
|
execution 'ANDROIDX_TEST_ORCHESTRATOR'
|
||||||
unitTests {
|
unitTests {
|
||||||
includeAndroidResources = true
|
includeAndroidResources = true
|
||||||
}
|
}
|
||||||
@@ -93,23 +100,23 @@ dependencies {
|
|||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
implementation project(path: ':bramble-core', configuration: 'default')
|
||||||
implementation project(':bramble-android')
|
implementation project(':bramble-android')
|
||||||
|
|
||||||
implementation 'androidx.preference:preference:1.1.0'
|
implementation 'androidx.fragment:fragment:1.3.0'
|
||||||
implementation 'androidx.exifinterface:exifinterface:1.0.0'
|
implementation 'androidx.preference:preference:1.1.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
|
implementation 'androidx.exifinterface:exifinterface:1.3.1'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
implementation 'com.google.android.material:material:1.1.0-beta01'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||||
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0-rc01'
|
implementation 'com.google.android.material:material:1.2.1'
|
||||||
|
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0-rc03'
|
||||||
|
|
||||||
implementation 'ch.acra:acra:4.11'
|
|
||||||
implementation 'info.guardianproject.panic:panic:1.0'
|
implementation 'info.guardianproject.panic:panic:1.0'
|
||||||
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||||
implementation 'de.hdodenhof:circleimageview:3.0.1'
|
implementation 'de.hdodenhof:circleimageview:3.0.1'
|
||||||
implementation 'com.google.zxing:core:3.3.3' // newer version need minSdk 24
|
implementation 'com.google.zxing:core:3.3.3' // newer version need minSdk 24
|
||||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:3.0.0'
|
implementation 'uk.co.samuelwall:material-tap-target-prompt:3.0.0'
|
||||||
implementation 'com.vanniktech:emoji-google:0.6.0'
|
implementation 'com.vanniktech:emoji-google:0.6.0' // newer versions need minSdk 21
|
||||||
implementation 'com.github.kobakei:MaterialFabSpeedDial:1.2.1'
|
implementation 'com.github.kobakei:MaterialFabSpeedDial:1.2.1'
|
||||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
||||||
def glideVersion = '4.10.0'
|
def glideVersion = '4.11.0'
|
||||||
implementation("com.github.bumptech.glide:glide:$glideVersion") {
|
implementation("com.github.bumptech.glide:glide:$glideVersion") {
|
||||||
exclude group: 'com.android.support'
|
exclude group: 'com.android.support'
|
||||||
exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it
|
exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it
|
||||||
@@ -120,33 +127,35 @@ dependencies {
|
|||||||
|
|
||||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||||
|
|
||||||
def espressoVersion = '3.2.0'
|
def espressoVersion = '3.3.0'
|
||||||
def jmockVersion = '2.8.2'
|
def jmockVersion = '2.8.2'
|
||||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||||
testImplementation 'androidx.test:runner:1.2.0'
|
testImplementation 'androidx.test:runner:1.3.0'
|
||||||
testImplementation 'androidx.test.ext:junit:1.1.1'
|
testImplementation 'androidx.test.ext:junit:1.1.2'
|
||||||
testImplementation 'androidx.fragment:fragment-testing:1.1.0'
|
testImplementation 'androidx.fragment:fragment-testing:1.2.5'
|
||||||
|
testImplementation "androidx.arch.core:core-testing:2.1.0"
|
||||||
testImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
testImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
||||||
testImplementation 'org.robolectric:robolectric:4.3.1'
|
testImplementation 'org.robolectric:robolectric:4.3.1'
|
||||||
testImplementation 'org.mockito:mockito-core:3.1.0'
|
testImplementation 'org.mockito:mockito-core:3.1.0'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.13.1'
|
||||||
testImplementation "org.jmock:jmock:$jmockVersion"
|
testImplementation "org.jmock:jmock:$jmockVersion"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
||||||
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
||||||
|
testAnnotationProcessor "com.google.dagger:dagger-compiler:2.24"
|
||||||
|
|
||||||
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
|
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion"
|
androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion"
|
||||||
|
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||||
|
androidTestUtil 'androidx.test:orchestrator:1.3.0'
|
||||||
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.24"
|
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.24"
|
||||||
androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
|
androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
|
||||||
androidTestImplementation 'junit:junit:4.12'
|
androidTestImplementation 'junit:junit:4.13.1'
|
||||||
androidTestScreenshotImplementation('tools.fastlane:screengrab:1.2.0') {
|
androidTestScreenshotImplementation 'tools.fastlane:screengrab:2.0.0'
|
||||||
// workaround for jetifier issue https://issuetracker.google.com/issues/123060356
|
androidTestScreenshotImplementation 'com.jraska:falcon:2.1.1'
|
||||||
exclude group: 'com.android.support.test.uiautomator'
|
|
||||||
}
|
|
||||||
androidTestScreenshotImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
|
androidTestScreenshotImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
|
json_key_file(ENV["BRIAR_GOOGLE_PLAY_JSON_KEY_FILE"])
|
||||||
package_name("org.briarproject.briar.android")
|
package_name("org.briarproject.briar.android")
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
# Uncomment the line if you want fastlane to automatically update itself
|
# Uncomment the line if you want fastlane to automatically update itself
|
||||||
# update_fastlane
|
# update_fastlane
|
||||||
|
|
||||||
|
opt_out_usage
|
||||||
|
|
||||||
default_platform(:android)
|
default_platform(:android)
|
||||||
|
|
||||||
platform :android do
|
platform :android do
|
||||||
@@ -24,6 +26,21 @@ platform :android do
|
|||||||
system './demo-mode-deactivate.sh'
|
system './demo-mode-deactivate.sh'
|
||||||
system './rename_screenshots.py'
|
system './rename_screenshots.py'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "Updates Google Play metadata (title, descriptions, video, etc.)"
|
||||||
|
lane :metadata do
|
||||||
|
system './update-metadata.sh'
|
||||||
|
upload_to_play_store(
|
||||||
|
skip_upload_apk: true,
|
||||||
|
skip_upload_aab: true,
|
||||||
|
skip_upload_metadata: false,
|
||||||
|
skip_upload_changelogs: true,
|
||||||
|
skip_upload_images: true,
|
||||||
|
skip_upload_screenshots: true,
|
||||||
|
validate_only: false,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
El Briar és una aplicació de missatgeria dissenyada per a activistes, periodistes i qualsevol persona en general que necessiti una manera segura, fàcil i robsuta de comunicar-se. Al contrari que les aplicacions tradicionals, el Briar no depèn d'un servidor central: els missatges se sincronitzen directament entre els dispositius dels usuaris. Si Internet cau, el Briar pot continuar funcionant mitjançant Bluetooth o Wi-Fi, mantenint el flux fins i tot amb aquesta situació caòtica. Amb un funcionament normal d'Internet, el Briar es pot sincronitzar a través de la xarxa Tor, protegint els usuaris i les seves comunicacions d'ulls indiscrets.
|
||||||
|
|
||||||
|
L'aplicació té com a característica principal l'enviament de missatges privats en grups, fòrums o blocs, atès que es basa en la xarxa Tor. Qualsevol interacció al Briar s'emmagatzema només al vostre dispositiu fins que vulgueu compartir-ho amb altres usuaris.
|
||||||
|
|
||||||
|
No hi han anuncis ni seguiment El codi font d'aquesta aplicació està disponible per a tothom que vulgui inspeccionar-ho, com ja l'han auditat empreses professionals. Totes les versions del Briar són reproduïbles, la qual cosa permet verificar que el codi font publicat coincideix exactament amb l'aplicació publicada aquí. El desenvolupament és a càrrec d'un petit equip sense ànim lucratiu.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Missatgeria segura, arreu.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user