mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
73 Commits
release-1.
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f31219d54b | ||
|
|
b0ea32c85f | ||
|
|
651e0b9859 | ||
|
|
f66244b578 | ||
|
|
3a35effae9 | ||
|
|
97f4cd039a | ||
|
|
20a1474457 | ||
|
|
f214208b0a | ||
|
|
d95a5fd58c | ||
|
|
7d4de21be0 | ||
|
|
a65bda04bf | ||
|
|
41ae7b0522 | ||
|
|
c2214f5e61 | ||
|
|
75bd7927ac | ||
|
|
ed2c0336ed | ||
|
|
b23baf70b4 | ||
|
|
17a7144194 | ||
|
|
0f7d27cd95 | ||
|
|
6735e5075b | ||
|
|
135cf086f6 | ||
|
|
72bac59989 | ||
|
|
2c99a75b4e | ||
|
|
5c068ed07b | ||
|
|
fec384c200 | ||
|
|
89a4d1922b | ||
|
|
6ed16802ce | ||
|
|
7f11d7280f | ||
|
|
597e2a233f | ||
|
|
e83d8bb700 | ||
|
|
d1ce0d0628 | ||
|
|
d73ec3cd88 | ||
|
|
71c66c843b | ||
|
|
bd19272099 | ||
|
|
b77b885a94 | ||
|
|
1fc4f657c7 | ||
|
|
df7d48d54d | ||
|
|
1987dcb936 | ||
|
|
f3b69a26f8 | ||
|
|
5e0ca10dae | ||
|
|
685496fb15 | ||
|
|
1521cdd258 | ||
|
|
80561910b1 | ||
|
|
bffb5c94ed | ||
|
|
dcbb3e76d4 | ||
|
|
c19f7c27b1 | ||
|
|
9a5a1489ef | ||
|
|
648793e092 | ||
|
|
e10742a23d | ||
|
|
32ada51831 | ||
|
|
7734a62c3e | ||
|
|
e516c329a1 | ||
|
|
b839041d5a | ||
|
|
65de8707b7 | ||
|
|
dc5bd39ce4 | ||
|
|
3c4513b9c7 | ||
|
|
5320737d49 | ||
|
|
0ad9415850 | ||
|
|
6f1fba44b6 | ||
|
|
46cd7e3719 | ||
|
|
496ffc22ba | ||
|
|
286937e472 | ||
|
|
2908145a06 | ||
|
|
ed53544226 | ||
|
|
6da45a4585 | ||
|
|
0d85a56a29 | ||
|
|
3e5d1a0d20 | ||
|
|
0e9af1d993 | ||
|
|
a3b5ff0bc0 | ||
|
|
9b583ab7ae | ||
|
|
1192f66487 | ||
|
|
a10dc45a6a | ||
|
|
3c6e2e8875 | ||
|
|
cccdacc3e4 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,6 +20,7 @@ local.properties
|
||||
.idea/*
|
||||
!.idea/runConfigurations/
|
||||
!.idea/codeStyleSettings.xml
|
||||
!.idea/codeStyles
|
||||
.gradle
|
||||
build/
|
||||
*.iml
|
||||
|
||||
@@ -8,22 +8,23 @@ cache:
|
||||
before_script:
|
||||
- set -e
|
||||
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||
# Accept the license for the Android build tools
|
||||
- echo y | /opt/android-sdk/tools/bin/sdkmanager "build-tools;26.0.2"
|
||||
# Download OpenJDK 6 so we can compile against its standard library
|
||||
- JDK_FILE=openjdk-6-jre-headless_6b38-1.13.10-1~deb7u1_amd64.deb
|
||||
- if [ ! -d openjdk ]
|
||||
- then
|
||||
- wget -q http://ftp.uk.debian.org/debian/pool/main/o/openjdk-6/$JDK_FILE
|
||||
- dpkg-deb -x $JDK_FILE openjdk
|
||||
- fi
|
||||
- export JAVA_6_HOME=$PWD/openjdk/usr/lib/jvm/java-6-openjdk-amd64
|
||||
|
||||
test:
|
||||
script:
|
||||
- ./gradlew test
|
||||
- ./gradlew --no-daemon animalSnifferMain animalSnifferTest
|
||||
- ./gradlew --no-daemon test
|
||||
|
||||
after_script:
|
||||
# this file changes every time but should not be cached
|
||||
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/
|
||||
|
||||
test_reproducible:
|
||||
image: briar/reproducer:latest
|
||||
script:
|
||||
- cd .. && mv briar /opt/briar-reproducer/
|
||||
- cd /opt/briar-reproducer
|
||||
- ./reproduce.py ${CI_COMMIT_REF_NAME}
|
||||
only:
|
||||
- tags
|
||||
|
||||
|
||||
261
.idea/codeStyles/Project.xml
generated
Normal file
261
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,261 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="RIGHT_MARGIN" value="100" />
|
||||
<AndroidXmlCodeStyleSettings>
|
||||
<option name="USE_CUSTOM_SETTINGS" value="true" />
|
||||
</AndroidXmlCodeStyleSettings>
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||
<value />
|
||||
</option>
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="android" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="com" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="junit" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="net" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="org" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="java" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="javax" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="true" />
|
||||
<emptyLine />
|
||||
</value>
|
||||
</option>
|
||||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||
</JavaCodeStyleSettings>
|
||||
<Objective-C-extensions>
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||
</file>
|
||||
<class>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||
</class>
|
||||
<extensions>
|
||||
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||
</extensions>
|
||||
</Objective-C-extensions>
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="Groovy">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="RIGHT_MARGIN" value="80" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="ALIGN_MULTILINE_RESOURCES" value="false" />
|
||||
<option name="ALIGN_MULTILINE_FOR" value="false" />
|
||||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="1" />
|
||||
<option name="RESOURCE_LIST_WRAP" value="1" />
|
||||
<option name="EXTENDS_LIST_WRAP" value="1" />
|
||||
<option name="THROWS_LIST_WRAP" value="1" />
|
||||
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
|
||||
<option name="THROWS_KEYWORD_WRAP" value="1" />
|
||||
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
|
||||
<option name="BINARY_OPERATION_WRAP" value="1" />
|
||||
<option name="TERNARY_OPERATION_WRAP" value="1" />
|
||||
<option name="FOR_STATEMENT_WRAP" value="1" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
||||
<option name="ASSIGNMENT_WRAP" value="1" />
|
||||
<option name="ASSERT_STATEMENT_WRAP" value="1" />
|
||||
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
|
||||
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
|
||||
<option name="ENUM_CONSTANTS_WRAP" value="1" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_width</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_height</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_.*</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:width</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:height</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
@@ -9,13 +9,13 @@ apply plugin: 'de.undercouch.download'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '26.0.2'
|
||||
buildToolsVersion '27.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 10001
|
||||
versionName "1.0.1"
|
||||
versionCode 10004
|
||||
versionName "1.0.4"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
}
|
||||
|
||||
@@ -36,23 +36,79 @@ dependencies {
|
||||
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.android.tools.analytics-library:protos:26.1.2:protos-26.1.2.jar:52672a0b42b572a06aecc3535d5068eb46c0e15d129b9f1085d3c16a1da5cdbb',
|
||||
'com.android.tools.analytics-library:shared:26.1.2:shared-26.1.2.jar:5c7e0eda18c6f87feeb83628c707e8aaa3298b41fb72e38efe31ad1675f9e8e9',
|
||||
'com.android.tools.analytics-library:tracker:26.1.2:tracker-26.1.2.jar:06f97aa0adf44ffb06f8681c6a79d9be153a08f61d21eddc42b8d3db96df4282',
|
||||
'com.android.tools.build:apksig:3.1.2:apksig-3.1.2.jar:40696a4559124d1d57873d208857eee059d48859239d569c7d18374ac644a8be',
|
||||
'com.android.tools.build:builder-model:3.1.2:builder-model-3.1.2.jar:d49bfa2a135c9562b6ca7aa4342036cfa1582c7074c2d1d93d1dae8b3a134e17',
|
||||
'com.android.tools.build:builder-test-api:3.1.2:builder-test-api-3.1.2.jar:dfe2a50b740d41b11189101062434d4283d18647e89a492ad51710c719363e9f',
|
||||
'com.android.tools.build:builder:3.1.2:builder-3.1.2.jar:b60f825a42e2efe8433619fbc759f3d9effecab718279048d36881188ceb1d14',
|
||||
'com.android.tools.build:gradle-api:3.1.2:gradle-api-3.1.2.jar:e58bcc5b893e4583ab0f5c8ef89c4dbcce202b405a9d7fcc116d21e5357d4893',
|
||||
'com.android.tools.build:manifest-merger:26.1.2:manifest-merger-26.1.2.jar:9c61c27ea5266573107b954acf1216d398f4d7e7ae6fad6409d6b2b767eb091c',
|
||||
'com.android.tools.ddms:ddmlib:26.1.2:ddmlib-26.1.2.jar:18a2a5fbef36882f07d03c2b9e59eba05cf8248177bf5cbff736e4b582804c44',
|
||||
'com.android.tools.external.com-intellij:intellij-core:26.1.2:intellij-core-26.1.2.jar:37c5acf279f1ae3e85b1a5be3c9f15f43bde7b08f978eefefffb9c4035760c52',
|
||||
'com.android.tools.external.com-intellij:kotlin-compiler:26.1.2:kotlin-compiler-26.1.2.jar:152df0bee7580326c77316b669a9d96e3b09efb1d45f545dce4147271b0b8944',
|
||||
'com.android.tools.external.org-jetbrains:uast:26.1.2:uast-26.1.2.jar:02d39582206d3f5fc0a6cb18bfd9e8b9f9c1acb805ec6dac08b4e3a56849d279',
|
||||
'com.android.tools.layoutlib:layoutlib-api:26.1.2:layoutlib-api-26.1.2.jar:20220039fcc7d799f928153beff862e704457c0f55ab44258f3745ebeb662b4f',
|
||||
'com.android.tools.lint:lint-api:26.1.2:lint-api-26.1.2.jar:e1d5b62b870a7c566e9877a6b96b27784a4d713f8caa07fdcb4705d47a40a1d9',
|
||||
'com.android.tools.lint:lint-checks:26.1.2:lint-checks-26.1.2.jar:211e2afd58504372385d71b1e5be982c2b5121ab6fee1c04ddabeb75a8729e07',
|
||||
'com.android.tools.lint:lint-gradle-api:26.1.2:lint-gradle-api-26.1.2.jar:71284f2a8b03c3e55c94511c9eb36f8184fbb85324325fc6b78abf5183f03d90',
|
||||
'com.android.tools.lint:lint-gradle:26.1.2:lint-gradle-26.1.2.jar:855f0c82b7fc690df1b7319c0774f7517f7f8f5dd4eee1f6077dcf50e07c6240',
|
||||
'com.android.tools.lint:lint-kotlin:26.1.2:lint-kotlin-26.1.2.jar:1e591f70bcbbc11569720a9bbcca2bc1f3d4f789f01f40f642848d920643d484',
|
||||
'com.android.tools.lint:lint:26.1.2:lint-26.1.2.jar:93736c62e9f1976998c2b4aa716aea0734cdb162d05502f4af7292654aedb182',
|
||||
'com.android.tools:annotations:26.1.2:annotations-26.1.2.jar:72773dcaf5c4ccca828e3c8467f1b78a8a00b3cc5f8ad1aab88fcf9379928018',
|
||||
'com.android.tools:common:26.1.2:common-26.1.2.jar:ea4320f0c17dcbc4491896bb705c4d25ec08bd62ef02ab0579fe154e75e788e6',
|
||||
'com.android.tools:dvlib:26.1.2:dvlib-26.1.2.jar:1187aa4fb666595c96c4deb6bc0e0f4b7e396bde9f6243330b49a232946130ea',
|
||||
'com.android.tools:repository:26.1.2:repository-26.1.2.jar:8b86e512ad6d32bd76989451eefe2b271f5efce6d4d65ecb173afaf14606e01a',
|
||||
'com.android.tools:sdk-common:26.1.2:sdk-common-26.1.2.jar:23584720a60a21cdcb5b1ec10269e3013789d6805d153cc696c39ec7ce251896',
|
||||
'com.android.tools:sdklib:26.1.2:sdklib-26.1.2.jar:d3870fafc59ab8efa70d3f9649f40ee299c8ec5b58377b06e8853d7272a5bf4e',
|
||||
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
|
||||
'com.google.code.gson:gson:2.7:gson-2.7.jar:2d43eb5ea9e133d2ee2405cc14f5ee08951b8361302fdd93494a3a997b508d32',
|
||||
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
||||
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
||||
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||
'com.google.errorprone:error_prone_annotations:2.0.18:error_prone_annotations-2.0.18.jar:cb4cfad870bf563a07199f3ebea5763f0dec440fcda0b318640b1feaa788656b',
|
||||
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
||||
'com.google.guava:guava:22.0:guava-22.0.jar:1158e94c7de4da480873f0b4ab4a1da14c0d23d4b1902cc94a58a6f0f9ab579e',
|
||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
|
||||
'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.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
|
||||
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
|
||||
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
|
||||
'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4',
|
||||
'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
|
||||
'commons-codec:commons-codec:1.6:commons-codec-1.6.jar:54b34e941b8e1414bd3e40d736efd3481772dc26db3296f6aa45cec9f6203d86',
|
||||
'commons-logging:commons-logging:1.1.1:commons-logging-1.1.1.jar:ce6f913cad1f0db3aad70186d65c5bc7ffcc9a99e3fe8e0b137312819f7c362f',
|
||||
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
|
||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128-runtime.jar:e357a0f1d573c2f702a273992b1b6cb661734f66311854efb3778a888515c5b5',
|
||||
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128.jar:47b4bec6df11a1118da3953da8b9fa1e7079d6fec857faa1a3cf912e53a6fd4e',
|
||||
'org.jacoco:org.jacoco.ant:0.7.4.201502262128:org.jacoco.ant-0.7.4.201502262128.jar:013ce2a68ba57a3c59215ae0dec4df3498c078062a38c3b94c841fc14450f283',
|
||||
'org.jacoco:org.jacoco.core:0.7.4.201502262128:org.jacoco.core-0.7.4.201502262128.jar:ec4c74554312fac5116350164786f91b35c9e082fa4ea598bfa42b5db05d7abb',
|
||||
'org.jacoco:org.jacoco.report:0.7.4.201502262128:org.jacoco.report-0.7.4.201502262128.jar:7a3554c605e088e7e323b1084656243f0444fa353e2f2dee1f1a4204eb64ff09',
|
||||
'org.ow2.asm:asm-debug-all:5.0.1:asm-debug-all-5.0.1.jar:4734de5b515a454b0096db6971fb068e5f70e6f10bbee2b3bd2fdfe5d978ed57',
|
||||
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
|
||||
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
|
||||
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
|
||||
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
|
||||
'org.apache.httpcomponents:httpclient:4.2.6:httpclient-4.2.6.jar:362e9324ee7c697e21279e20077b52737ddef3f1b2c1a7abe5ad34b465145550',
|
||||
'org.apache.httpcomponents:httpcore:4.2.5:httpcore-4.2.5.jar:e5e82da4cc66c8d917bbf743e3c0752efe8522735e7fc9dbddb65bccea81cfe9',
|
||||
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
|
||||
'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.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
|
||||
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
|
||||
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
|
||||
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
|
||||
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0:kotlin-stdlib-jre7-1.2.0.jar:c7a20fb951d437797afe8980aff6c1e5a03f310c661ba58ba1d4fa90cb0f2926',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
|
||||
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
|
||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||
'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
|
||||
'org.ow2.asm:asm-analysis:5.1:asm-analysis-5.1.jar:a34658f5c5de4b573eef21131cc32cc25f7b66407944f312b28ec2e56abb1fa9',
|
||||
'org.ow2.asm:asm-commons:5.1:asm-commons-5.1.jar:97b3786e1f55e74bddf8ad102bf50e33bbcbc1f6b7fd7b36f0bbbb25cd4981be',
|
||||
'org.ow2.asm:asm-tree:5.1:asm-tree-5.1.jar:c0de2bbc4cb8297419659813ecd4ed1d077ed1dd5c1f5544cc5143e493e84c10',
|
||||
'org.ow2.asm:asm-util:5.1:asm-util-5.1.jar:ee032c39ae5e3cd099148fbba9a2124f9ed613e5cb93e03ee0fa8808ce364040',
|
||||
'org.ow2.asm:asm:5.1:asm-5.1.jar:d2da399a9967c69f0a21739256fa79d284222c223082cacadc17372244764b54',
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.Scheduler;
|
||||
@@ -38,15 +37,14 @@ public class AndroidPluginModule {
|
||||
@Scheduler ScheduledExecutorService scheduler,
|
||||
AndroidExecutor androidExecutor, SecureRandom random,
|
||||
SocketFactory torSocketFactory, BackoffFactory backoffFactory,
|
||||
Application app, LocationUtils locationUtils, DevReporter reporter,
|
||||
EventBus eventBus) {
|
||||
Application app, LocationUtils locationUtils, EventBus eventBus) {
|
||||
Context appContext = app.getApplicationContext();
|
||||
DuplexPluginFactory bluetooth =
|
||||
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
|
||||
appContext, random, eventBus, backoffFactory);
|
||||
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler,
|
||||
appContext, locationUtils, reporter, eventBus,
|
||||
torSocketFactory, backoffFactory);
|
||||
appContext, locationUtils, eventBus, torSocketFactory,
|
||||
backoffFactory);
|
||||
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
|
||||
scheduler, backoffFactory, appContext);
|
||||
Collection<DuplexPluginFactory> duplex =
|
||||
|
||||
@@ -32,11 +32,9 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.bramble.util.IoUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
@@ -114,7 +112,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Context appContext;
|
||||
private final LocationUtils locationUtils;
|
||||
private final DevReporter reporter;
|
||||
private final SocketFactory torSocketFactory;
|
||||
private final Backoff backoff;
|
||||
private final DuplexPluginCallback callback;
|
||||
@@ -136,14 +133,13 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
|
||||
TorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
|
||||
Context appContext, LocationUtils locationUtils,
|
||||
DevReporter reporter, SocketFactory torSocketFactory,
|
||||
Backoff backoff, DuplexPluginCallback callback,
|
||||
String architecture, int maxLatency, int maxIdleTime) {
|
||||
SocketFactory torSocketFactory, Backoff backoff,
|
||||
DuplexPluginCallback callback, String architecture,
|
||||
int maxLatency, int maxIdleTime) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.scheduler = scheduler;
|
||||
this.appContext = appContext;
|
||||
this.locationUtils = locationUtils;
|
||||
this.reporter = reporter;
|
||||
this.torSocketFactory = torSocketFactory;
|
||||
this.backoff = backoff;
|
||||
this.callback = callback;
|
||||
@@ -389,14 +385,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendDevReports() {
|
||||
ioExecutor.execute(() -> {
|
||||
// TODO: Trigger this with a TransportEnabledEvent
|
||||
File reportDir = AndroidUtils.getReportDir(appContext);
|
||||
reporter.sendReports(reportDir);
|
||||
});
|
||||
}
|
||||
|
||||
private void bind() {
|
||||
ioExecutor.execute(() -> {
|
||||
// If there's already a port number stored in config, reuse it
|
||||
@@ -624,10 +612,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
connectionStatus.getAndSetCircuitBuilt()) {
|
||||
LOG.info("First circuit built");
|
||||
backoff.reset();
|
||||
if (isRunning()) {
|
||||
sendDevReports();
|
||||
callback.transportEnabled();
|
||||
}
|
||||
if (isRunning()) callback.transportEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,10 +641,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) {
|
||||
connectionStatus.setBootstrapped();
|
||||
backoff.reset();
|
||||
if (isRunning()) {
|
||||
sendDevReports();
|
||||
callback.transportEnabled();
|
||||
}
|
||||
if (isRunning()) callback.transportEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
|
||||
@@ -40,21 +39,18 @@ public class TorPluginFactory implements DuplexPluginFactory {
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Context appContext;
|
||||
private final LocationUtils locationUtils;
|
||||
private final DevReporter reporter;
|
||||
private final EventBus eventBus;
|
||||
private final SocketFactory torSocketFactory;
|
||||
private final BackoffFactory backoffFactory;
|
||||
|
||||
public TorPluginFactory(Executor ioExecutor,
|
||||
ScheduledExecutorService scheduler, Context appContext,
|
||||
LocationUtils locationUtils, DevReporter reporter,
|
||||
EventBus eventBus, SocketFactory torSocketFactory,
|
||||
BackoffFactory backoffFactory) {
|
||||
LocationUtils locationUtils, EventBus eventBus,
|
||||
SocketFactory torSocketFactory, BackoffFactory backoffFactory) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.scheduler = scheduler;
|
||||
this.appContext = appContext;
|
||||
this.locationUtils = locationUtils;
|
||||
this.reporter = reporter;
|
||||
this.eventBus = eventBus;
|
||||
this.torSocketFactory = torSocketFactory;
|
||||
this.backoffFactory = backoffFactory;
|
||||
@@ -94,7 +90,7 @@ public class TorPluginFactory implements DuplexPluginFactory {
|
||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||
TorPlugin plugin = new TorPlugin(ioExecutor, scheduler, appContext,
|
||||
locationUtils, reporter, torSocketFactory, backoff, callback,
|
||||
locationUtils, torSocketFactory, backoff, callback,
|
||||
architecture, MAX_LATENCY, MAX_IDLE_TIME);
|
||||
eventBus.addListener(plugin);
|
||||
return plugin;
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.net.wifi.WifiConfiguration;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.StrictMode;
|
||||
import android.provider.Settings;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
@@ -66,9 +67,12 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
|
||||
|
||||
@Override
|
||||
protected void writeSeed() {
|
||||
// Silence strict mode
|
||||
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
|
||||
super.writeSeed();
|
||||
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
|
||||
applyOpenSslFix();
|
||||
StrictMode.setThreadPolicy(tp);
|
||||
}
|
||||
|
||||
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
package org.briarproject.bramble.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
|
||||
public class AndroidUtils {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(AndroidUtils.class.getName());
|
||||
|
||||
// Fake Bluetooth address returned by BluetoothAdapter on API 23 and later
|
||||
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
|
||||
|
||||
@@ -35,6 +44,7 @@ public class AndroidUtils {
|
||||
public static String getBluetoothAddress(Context ctx,
|
||||
BluetoothAdapter adapter) {
|
||||
// Return the adapter's address if it's valid and not fake
|
||||
@SuppressLint("HardwareIds")
|
||||
String address = adapter.getAddress();
|
||||
if (isValidBluetoothAddress(address)) return address;
|
||||
// Return the address from settings if it's valid and not fake
|
||||
@@ -51,17 +61,77 @@ public class AndroidUtils {
|
||||
&& !address.equals(FAKE_BLUETOOTH_ADDRESS);
|
||||
}
|
||||
|
||||
public static void deleteAppData(Context ctx) {
|
||||
public static void logDataDirContents(Context ctx) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Contents of data directory:");
|
||||
logFileOrDir(new File(ctx.getApplicationInfo().dataDir));
|
||||
}
|
||||
}
|
||||
|
||||
private static void logFileOrDir(File f) {
|
||||
LOG.info(f.getAbsolutePath() + " " + f.length());
|
||||
if (f.isDirectory()) {
|
||||
File[] children = f.listFiles();
|
||||
if (children == null) {
|
||||
LOG.info("Could not list files in " + f.getAbsolutePath());
|
||||
} else {
|
||||
for (File child : children) logFileOrDir(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static File getSharedPrefsFile(Context ctx, String name) {
|
||||
File dataDir = new File(ctx.getApplicationInfo().dataDir);
|
||||
File prefsDir = new File(dataDir, "shared_prefs");
|
||||
return new File(prefsDir, name + ".xml");
|
||||
}
|
||||
|
||||
public static void logFileContents(File f) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Contents of " + f.getAbsolutePath() + ":");
|
||||
try {
|
||||
Scanner s = new Scanner(f);
|
||||
while (s.hasNextLine()) LOG.info(s.nextLine());
|
||||
s.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
LOG.info(f.getAbsolutePath() + " not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
public static void deleteAppData(Context ctx, SharedPreferences... clear) {
|
||||
// Clear and commit shared preferences
|
||||
for (SharedPreferences prefs : clear) {
|
||||
boolean cleared = prefs.edit().clear().commit();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
if (cleared) LOG.info("Cleared shared preferences");
|
||||
else LOG.info("Could not clear shared preferences");
|
||||
}
|
||||
}
|
||||
// Delete files, except lib and shared_prefs directories
|
||||
File dataDir = new File(ctx.getApplicationInfo().dataDir);
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Deleting app data from " + dataDir.getAbsolutePath());
|
||||
File[] children = dataDir.listFiles();
|
||||
if (children != null) {
|
||||
for (File child : children) {
|
||||
if (!child.getName().equals("lib"))
|
||||
String name = child.getName();
|
||||
if (!name.equals("lib") && !name.equals("shared_prefs")) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Deleting " + child.getAbsolutePath());
|
||||
IoUtils.deleteFileOrDir(child);
|
||||
}
|
||||
}
|
||||
} else if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Could not list files in " + dataDir.getAbsolutePath());
|
||||
}
|
||||
// Recreate the cache dir as some OpenGL drivers expect it to exist
|
||||
new File(dataDir, "cache").mkdir();
|
||||
boolean recreated = new File(dataDir, "cache").mkdir();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
if (recreated) LOG.info("Recreated cache dir");
|
||||
else LOG.info("Could not recreate cache dir");
|
||||
}
|
||||
}
|
||||
|
||||
public static File getReportDir(Context ctx) {
|
||||
|
||||
@@ -2,6 +2,7 @@ apply plugin: 'java-library'
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
apply plugin: 'ru.vyarus.animalsniffer'
|
||||
apply plugin: 'witness'
|
||||
|
||||
dependencies {
|
||||
@@ -14,6 +15,8 @@ dependencies {
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||
|
||||
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
@@ -26,6 +29,9 @@ dependencyVerification {
|
||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
||||
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
|
||||
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
|
||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||
@@ -33,6 +39,7 @@ dependencyVerification {
|
||||
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
|
||||
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||
]
|
||||
}
|
||||
@@ -48,8 +55,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
|
||||
artifacts {
|
||||
testOutput jarTest
|
||||
}
|
||||
|
||||
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
|
||||
tasks.withType(JavaCompile) {
|
||||
useJava6StandardLibrary(it)
|
||||
}
|
||||
|
||||
@@ -45,9 +45,9 @@ public interface ContactManager {
|
||||
*
|
||||
* @param alice true if the local party is Alice
|
||||
*/
|
||||
ContactId addContact(Author remote, AuthorId local,
|
||||
SecretKey master, long timestamp, boolean alice, boolean verified,
|
||||
boolean active) throws DbException;
|
||||
ContactId addContact(Author remote, AuthorId local, SecretKey master,
|
||||
long timestamp, boolean alice, boolean verified, boolean active)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the contact with the given ID.
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* A key pair consisting of a {@link PublicKey} and a {@link PrivateKey).
|
||||
* A key pair consisting of a {@link PublicKey} and a {@link PrivateKey}.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.io.IOException;
|
||||
public interface BdfReader {
|
||||
|
||||
int DEFAULT_NESTED_LIMIT = 5;
|
||||
int DEFAULT_MAX_BUFFER_SIZE = 64 * 1024;
|
||||
|
||||
boolean eof() throws IOException;
|
||||
|
||||
@@ -39,13 +40,13 @@ public interface BdfReader {
|
||||
|
||||
boolean hasString() throws IOException;
|
||||
|
||||
String readString(int maxLength) throws IOException;
|
||||
String readString() throws IOException;
|
||||
|
||||
void skipString() throws IOException;
|
||||
|
||||
boolean hasRaw() throws IOException;
|
||||
|
||||
byte[] readRaw(int maxLength) throws IOException;
|
||||
byte[] readRaw() throws IOException;
|
||||
|
||||
void skipRaw() throws IOException;
|
||||
|
||||
|
||||
@@ -9,5 +9,6 @@ public interface BdfReaderFactory {
|
||||
|
||||
BdfReader createReader(InputStream in);
|
||||
|
||||
BdfReader createReader(InputStream in, int nestedLimit);
|
||||
BdfReader createReader(InputStream in, int nestedLimit,
|
||||
int maxBufferSize);
|
||||
}
|
||||
|
||||
@@ -104,18 +104,12 @@ public interface DatabaseComponent {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given transport keys, optionally binding them to the given
|
||||
* contact, and returns a key set ID.
|
||||
* Stores the given transport keys for the given contact and returns a
|
||||
* key set ID.
|
||||
*/
|
||||
KeySetId addTransportKeys(Transaction txn, @Nullable ContactId c,
|
||||
KeySetId addTransportKeys(Transaction txn, ContactId c,
|
||||
TransportKeys k) throws DbException;
|
||||
|
||||
/**
|
||||
* Binds the given keys for the given transport to the given contact.
|
||||
*/
|
||||
void bindTransportKeys(Transaction txn, ContactId c, TransportId t,
|
||||
KeySetId k) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the database contains the given contact for the given
|
||||
* local pseudonym.
|
||||
|
||||
@@ -3,10 +3,14 @@ package org.briarproject.bramble.api.reporting;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface DevConfig {
|
||||
|
||||
PublicKey getDevPublicKey();
|
||||
|
||||
String getDevOnionAddress();
|
||||
|
||||
File getReportDir();
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ public interface DevReporter {
|
||||
|
||||
/**
|
||||
* Sends any reports previously stored on disk.
|
||||
*
|
||||
* @param reportDir the directory where reports are stored.
|
||||
*/
|
||||
void sendReports(File reportDir);
|
||||
void sendReports();
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package org.briarproject.bramble.api.sync;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface SyncSessionFactory {
|
||||
@@ -12,8 +12,8 @@ public interface SyncSessionFactory {
|
||||
SyncSession createIncomingSession(ContactId c, InputStream in);
|
||||
|
||||
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
|
||||
OutputStream out);
|
||||
StreamWriter streamWriter);
|
||||
|
||||
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
|
||||
int maxIdleTime, OutputStream out);
|
||||
int maxIdleTime, StreamWriter streamWriter);
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ package org.briarproject.bramble.api.system;
|
||||
public interface Clock {
|
||||
|
||||
/**
|
||||
* @see {@link System#currentTimeMillis()}
|
||||
* @see System#currentTimeMillis()
|
||||
*/
|
||||
long currentTimeMillis();
|
||||
|
||||
/**
|
||||
* @see {@link Thread#sleep(long)}
|
||||
* @see Thread#sleep(long)
|
||||
*/
|
||||
void sleep(long milliseconds) throws InterruptedException;
|
||||
}
|
||||
|
||||
@@ -19,48 +19,24 @@ public interface KeyManager {
|
||||
/**
|
||||
* Informs the key manager that a new contact has been added. Derives and
|
||||
* stores a set of transport keys for communicating with the contact over
|
||||
* each transport.
|
||||
* each transport and returns the key set IDs.
|
||||
* <p/>
|
||||
* {@link StreamContext StreamContexts} for the contact can be created
|
||||
* after this method has returned.
|
||||
*
|
||||
* @param alice true if the local party is Alice
|
||||
* @param active whether the derived keys can be used for outgoing streams
|
||||
*/
|
||||
void addContact(Transaction txn, ContactId c, SecretKey master,
|
||||
long timestamp, boolean alice) throws DbException;
|
||||
|
||||
/**
|
||||
* Derives and stores a set of unbound transport keys for each transport
|
||||
* and returns the key set IDs.
|
||||
* <p/>
|
||||
* The keys must be bound before they can be used for incoming streams,
|
||||
* and also activated before they can be used for outgoing streams.
|
||||
*
|
||||
* @param alice true if the local party is Alice
|
||||
*/
|
||||
Map<TransportId, KeySetId> addUnboundKeys(Transaction txn, SecretKey master,
|
||||
long timestamp, boolean alice) throws DbException;
|
||||
|
||||
/**
|
||||
* Binds the given transport keys to the given contact.
|
||||
*/
|
||||
void bindKeys(Transaction txn, ContactId c, Map<TransportId, KeySetId> keys)
|
||||
Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
|
||||
SecretKey master, long timestamp, boolean alice, boolean active)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given transport keys as usable for outgoing streams. Keys must
|
||||
* be bound before they are activated.
|
||||
* Marks the given transport keys as usable for outgoing streams.
|
||||
*/
|
||||
void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Removes the given transport keys, which must not have been bound, from
|
||||
* the manager and the database.
|
||||
*/
|
||||
void removeKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if we have keys that can be used for outgoing streams to
|
||||
* the given contact over the given transport.
|
||||
|
||||
@@ -3,23 +3,20 @@ package org.briarproject.bramble.api.transport;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* A set of transport keys for communicating with a contact. If the keys have
|
||||
* not yet been bound to a contact, {@link #getContactId()}} returns null.
|
||||
* A set of transport keys for communicating with a contact.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class KeySet {
|
||||
|
||||
private final KeySetId keySetId;
|
||||
@Nullable
|
||||
private final ContactId contactId;
|
||||
private final TransportKeys transportKeys;
|
||||
|
||||
public KeySet(KeySetId keySetId, @Nullable ContactId contactId,
|
||||
public KeySet(KeySetId keySetId, ContactId contactId,
|
||||
TransportKeys transportKeys) {
|
||||
this.keySetId = keySetId;
|
||||
this.contactId = contactId;
|
||||
@@ -30,7 +27,6 @@ public class KeySet {
|
||||
return keySetId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.briarproject.bramble.api.transport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* An interface for writing data to a transport connection. Data will be
|
||||
* encrypted and authenticated before being written to the connection.
|
||||
*/
|
||||
public interface StreamWriter {
|
||||
|
||||
OutputStream getOutputStream();
|
||||
|
||||
/**
|
||||
* Sends the end of stream marker, informing the recipient that no more
|
||||
* data will be sent. The connection is flushed but not closed.
|
||||
*/
|
||||
void sendEndOfStream() throws IOException;
|
||||
}
|
||||
@@ -12,12 +12,12 @@ public interface StreamWriterFactory {
|
||||
* Creates an {@link OutputStream OutputStream} for writing to a
|
||||
* transport stream
|
||||
*/
|
||||
OutputStream createStreamWriter(OutputStream out, StreamContext ctx);
|
||||
StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
|
||||
|
||||
/**
|
||||
* Creates an {@link OutputStream OutputStream} for writing to a contact
|
||||
* exchange stream.
|
||||
*/
|
||||
OutputStream createContactExchangeStreamWriter(OutputStream out,
|
||||
StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
||||
SecretKey headerKey);
|
||||
}
|
||||
|
||||
@@ -9,25 +9,37 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
|
||||
@NotNullByDefault
|
||||
public class IoUtils {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(IoUtils.class.getName());
|
||||
|
||||
public static void deleteFileOrDir(File f) {
|
||||
if (f.isFile()) {
|
||||
f.delete();
|
||||
delete(f);
|
||||
} else if (f.isDirectory()) {
|
||||
File[] children = f.listFiles();
|
||||
if (children != null)
|
||||
for (File child : children) deleteFileOrDir(child);
|
||||
f.delete();
|
||||
delete(f);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyAndClose(InputStream in, OutputStream out)
|
||||
throws IOException {
|
||||
private static void delete(File f) {
|
||||
boolean deleted = f.delete();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
if (deleted) LOG.info("Deleted " + f.getAbsolutePath());
|
||||
else LOG.info("Could not delete " + f.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyAndClose(InputStream in, OutputStream out) {
|
||||
byte[] buf = new byte[4096];
|
||||
try {
|
||||
while (true) {
|
||||
|
||||
@@ -2,6 +2,7 @@ apply plugin: 'java-library'
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
apply plugin: 'ru.vyarus.animalsniffer'
|
||||
apply plugin: 'net.ltgt.apt'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'witness'
|
||||
@@ -26,12 +27,13 @@ dependencies {
|
||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||
|
||||
testApt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
|
||||
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
||||
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
||||
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||
@@ -45,6 +47,9 @@ dependencyVerification {
|
||||
'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.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
||||
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
|
||||
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
|
||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
|
||||
@@ -53,6 +58,7 @@ dependencyVerification {
|
||||
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
|
||||
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||
'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
|
||||
]
|
||||
@@ -69,8 +75,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
|
||||
artifacts {
|
||||
testOutput jarTest
|
||||
}
|
||||
|
||||
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
|
||||
tasks.withType(JavaCompile) {
|
||||
useJava6StandardLibrary(it)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.briarproject.bramble.identity.IdentityModule;
|
||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||
import org.briarproject.bramble.plugin.PluginModule;
|
||||
import org.briarproject.bramble.properties.PropertiesModule;
|
||||
import org.briarproject.bramble.reporting.ReportingModule;
|
||||
import org.briarproject.bramble.sync.SyncModule;
|
||||
import org.briarproject.bramble.system.SystemModule;
|
||||
import org.briarproject.bramble.transport.TransportModule;
|
||||
@@ -28,6 +29,8 @@ public interface BrambleCoreEagerSingletons {
|
||||
|
||||
void inject(PropertiesModule.EagerSingletons init);
|
||||
|
||||
void inject(ReportingModule.EagerSingletons init);
|
||||
|
||||
void inject(SyncModule.EagerSingletons init);
|
||||
|
||||
void inject(SystemModule.EagerSingletons init);
|
||||
|
||||
@@ -59,6 +59,7 @@ public class BrambleCoreModule {
|
||||
c.inject(new LifecycleModule.EagerSingletons());
|
||||
c.inject(new PluginModule.EagerSingletons());
|
||||
c.inject(new PropertiesModule.EagerSingletons());
|
||||
c.inject(new ReportingModule.EagerSingletons());
|
||||
c.inject(new SyncModule.EagerSingletons());
|
||||
c.inject(new SystemModule.EagerSingletons());
|
||||
c.inject(new TransportModule.EagerSingletons());
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.briarproject.bramble.api.record.RecordWriter;
|
||||
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||
|
||||
import java.io.EOFException;
|
||||
@@ -152,11 +153,11 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
recordReaderFactory.createRecordReader(streamReader);
|
||||
|
||||
// Create the writers
|
||||
OutputStream streamWriter =
|
||||
StreamWriter streamWriter =
|
||||
streamWriterFactory.createContactExchangeStreamWriter(out,
|
||||
alice ? aliceHeaderKey : bobHeaderKey);
|
||||
RecordWriter recordWriter =
|
||||
recordWriterFactory.createRecordWriter(streamWriter);
|
||||
recordWriterFactory.createRecordWriter(streamWriter.getOutputStream());
|
||||
|
||||
// Derive the nonces to be signed
|
||||
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
|
||||
@@ -184,8 +185,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
localSignature, localTimestamp);
|
||||
recordWriter.flush();
|
||||
}
|
||||
// Close the outgoing stream
|
||||
recordWriter.close();
|
||||
// Send EOF on the outgoing stream
|
||||
streamWriter.sendEndOfStream();
|
||||
// Skip any remaining records from the incoming stream
|
||||
try {
|
||||
while (true) recordReader.readRecord();
|
||||
|
||||
@@ -46,7 +46,7 @@ class ContactManagerImpl implements ContactManager {
|
||||
SecretKey master, long timestamp, boolean alice, boolean verified,
|
||||
boolean active) throws DbException {
|
||||
ContactId c = db.addContact(txn, remote, local, verified, active);
|
||||
keyManager.addContact(txn, c, master, timestamp, alice);
|
||||
keyManager.addContact(txn, c, master, timestamp, alice, active);
|
||||
Contact contact = db.getContact(txn, c);
|
||||
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
|
||||
return c;
|
||||
|
||||
@@ -152,59 +152,47 @@ public class MessageEncrypter {
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 1) {
|
||||
printUsage();
|
||||
return;
|
||||
System.exit(1);
|
||||
}
|
||||
SecureRandom random = new SecureRandom();
|
||||
MessageEncrypter encrypter = new MessageEncrypter(random);
|
||||
if (args[0].equals("generate")) {
|
||||
if (args.length != 3) {
|
||||
printUsage();
|
||||
return;
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
generateKeyPair(args[1], args[2]);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(2);
|
||||
}
|
||||
// Generate a key pair
|
||||
KeyPair keyPair = encrypter.generateKeyPair();
|
||||
PrintStream out = new PrintStream(new FileOutputStream(args[1]));
|
||||
out.print(
|
||||
StringUtils.toHexString(keyPair.getPublic().getEncoded()));
|
||||
out.flush();
|
||||
out.close();
|
||||
out = new PrintStream(new FileOutputStream(args[2]));
|
||||
out.print(
|
||||
StringUtils.toHexString(keyPair.getPrivate().getEncoded()));
|
||||
out.flush();
|
||||
out.close();
|
||||
} else if (args[0].equals("encrypt")) {
|
||||
if (args.length != 2) {
|
||||
printUsage();
|
||||
return;
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
encryptMessage(args[1]);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(2);
|
||||
}
|
||||
// Encrypt a decrypted message
|
||||
InputStream in = new FileInputStream(args[1]);
|
||||
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
|
||||
PublicKey publicKey =
|
||||
encrypter.getKeyParser().parsePublicKey(keyBytes);
|
||||
String message = readFully(System.in);
|
||||
byte[] plaintext = message.getBytes(Charset.forName("UTF-8"));
|
||||
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
|
||||
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
|
||||
} else if (args[0].equals("decrypt")) {
|
||||
if (args.length != 2) {
|
||||
printUsage();
|
||||
return;
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
decryptMessage(args[1]);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(2);
|
||||
}
|
||||
// Decrypt an encrypted message
|
||||
InputStream in = new FileInputStream(args[1]);
|
||||
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
|
||||
PrivateKey privateKey =
|
||||
encrypter.getKeyParser().parsePrivateKey(keyBytes);
|
||||
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
|
||||
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
|
||||
System.out.println(new String(plaintext, Charset.forName("UTF-8")));
|
||||
} else {
|
||||
printUsage();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +204,46 @@ public class MessageEncrypter {
|
||||
System.err.println("MessageEncrypter decrypt <private_key_file>");
|
||||
}
|
||||
|
||||
private static void generateKeyPair(String publicKeyFile,
|
||||
String privateKeyFile) throws Exception {
|
||||
SecureRandom random = new SecureRandom();
|
||||
MessageEncrypter encrypter = new MessageEncrypter(random);
|
||||
KeyPair keyPair = encrypter.generateKeyPair();
|
||||
PrintStream out = new PrintStream(new FileOutputStream(publicKeyFile));
|
||||
out.print(StringUtils.toHexString(keyPair.getPublic().getEncoded()));
|
||||
out.flush();
|
||||
out.close();
|
||||
out = new PrintStream(new FileOutputStream(privateKeyFile));
|
||||
out.print(StringUtils.toHexString(keyPair.getPrivate().getEncoded()));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
private static void encryptMessage(String publicKeyFile) throws Exception {
|
||||
SecureRandom random = new SecureRandom();
|
||||
MessageEncrypter encrypter = new MessageEncrypter(random);
|
||||
InputStream in = new FileInputStream(publicKeyFile);
|
||||
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
|
||||
PublicKey publicKey =
|
||||
encrypter.getKeyParser().parsePublicKey(keyBytes);
|
||||
String message = readFully(System.in);
|
||||
byte[] plaintext = message.getBytes(Charset.forName("UTF-8"));
|
||||
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
|
||||
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
|
||||
}
|
||||
|
||||
private static void decryptMessage(String privateKeyFile) throws Exception {
|
||||
SecureRandom random = new SecureRandom();
|
||||
MessageEncrypter encrypter = new MessageEncrypter(random);
|
||||
InputStream in = new FileInputStream(privateKeyFile);
|
||||
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
|
||||
PrivateKey privateKey =
|
||||
encrypter.getKeyParser().parsePrivateKey(keyBytes);
|
||||
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
|
||||
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
|
||||
System.out.println(new String(plaintext, Charset.forName("UTF-8")));
|
||||
}
|
||||
|
||||
private static String readFully(InputStream in) throws IOException {
|
||||
String newline = System.getProperty("line.separator");
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
@@ -10,37 +10,37 @@ import java.security.GeneralSecurityException;
|
||||
interface Signature {
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#initSign(java.security.PrivateKey)}
|
||||
* @see java.security.Signature#initSign(java.security.PrivateKey)
|
||||
*/
|
||||
void initSign(PrivateKey k) throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#initVerify(java.security.PublicKey)}
|
||||
* @see java.security.Signature#initVerify(java.security.PublicKey)
|
||||
*/
|
||||
void initVerify(PublicKey k) throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#update(byte)}
|
||||
* @see java.security.Signature#update(byte)
|
||||
*/
|
||||
void update(byte b) throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#update(byte[])}
|
||||
* @see java.security.Signature#update(byte[])
|
||||
*/
|
||||
void update(byte[] b) throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#update(byte[], int, int)}
|
||||
* @see java.security.Signature#update(byte[], int, int)
|
||||
*/
|
||||
void update(byte[] b, int off, int len) throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#sign()}
|
||||
* @see java.security.Signature#sign()}
|
||||
*/
|
||||
byte[] sign() throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#verify(byte[])}
|
||||
* @see java.security.Signature#verify(byte[])
|
||||
*/
|
||||
boolean verify(byte[] signature) throws GeneralSecurityException;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.io.InputStream;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
|
||||
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_NESTED_LIMIT;
|
||||
|
||||
@Immutable
|
||||
@@ -16,11 +17,13 @@ class BdfReaderFactoryImpl implements BdfReaderFactory {
|
||||
|
||||
@Override
|
||||
public BdfReader createReader(InputStream in) {
|
||||
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT);
|
||||
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||
DEFAULT_MAX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfReader createReader(InputStream in, int nestedLimit) {
|
||||
return new BdfReaderImpl(in, nestedLimit);
|
||||
public BdfReader createReader(InputStream in, int nestedLimit,
|
||||
int maxBufferSize) {
|
||||
return new BdfReaderImpl(in, nestedLimit, maxBufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,15 +37,16 @@ class BdfReaderImpl implements BdfReader {
|
||||
private static final byte[] EMPTY_BUFFER = new byte[0];
|
||||
|
||||
private final InputStream in;
|
||||
private final int nestedLimit;
|
||||
private final int nestedLimit, maxBufferSize;
|
||||
|
||||
private boolean hasLookahead = false, eof = false;
|
||||
private byte next;
|
||||
private byte[] buf = new byte[8];
|
||||
|
||||
BdfReaderImpl(InputStream in, int nestedLimit) {
|
||||
BdfReaderImpl(InputStream in, int nestedLimit, int maxBufferSize) {
|
||||
this.in = in;
|
||||
this.nestedLimit = nestedLimit;
|
||||
this.maxBufferSize = maxBufferSize;
|
||||
}
|
||||
|
||||
private void readLookahead() throws IOException {
|
||||
@@ -91,8 +92,8 @@ class BdfReaderImpl implements BdfReader {
|
||||
if (hasBoolean()) return readBoolean();
|
||||
if (hasLong()) return readLong();
|
||||
if (hasDouble()) return readDouble();
|
||||
if (hasString()) return readString(Integer.MAX_VALUE);
|
||||
if (hasRaw()) return readRaw(Integer.MAX_VALUE);
|
||||
if (hasString()) return readString();
|
||||
if (hasRaw()) return readRaw();
|
||||
if (hasList()) return readList(level);
|
||||
if (hasDictionary()) return readDictionary(level);
|
||||
throw new FormatException();
|
||||
@@ -245,11 +246,11 @@ class BdfReaderImpl implements BdfReader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readString(int maxLength) throws IOException {
|
||||
public String readString() throws IOException {
|
||||
if (!hasString()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
int length = readStringLength();
|
||||
if (length < 0 || length > maxLength) throw new FormatException();
|
||||
if (length < 0 || length > maxBufferSize) throw new FormatException();
|
||||
if (length == 0) return "";
|
||||
readIntoBuffer(length);
|
||||
return new String(buf, 0, length, "UTF-8");
|
||||
@@ -279,11 +280,11 @@ class BdfReaderImpl implements BdfReader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readRaw(int maxLength) throws IOException {
|
||||
public byte[] readRaw() throws IOException {
|
||||
if (!hasRaw()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
int length = readRawLength();
|
||||
if (length < 0 || length > maxLength) throw new FormatException();
|
||||
if (length < 0 || length > maxBufferSize) throw new FormatException();
|
||||
if (length == 0) return EMPTY_BUFFER;
|
||||
byte[] b = new byte[length];
|
||||
readIntoBuffer(b, length);
|
||||
@@ -381,7 +382,7 @@ class BdfReaderImpl implements BdfReader {
|
||||
BdfDictionary dictionary = new BdfDictionary();
|
||||
readDictionaryStart();
|
||||
while (!hasDictionaryEnd())
|
||||
dictionary.put(readString(Integer.MAX_VALUE), readObject(level + 1));
|
||||
dictionary.put(readString(), readObject(level + 1));
|
||||
readDictionaryEnd();
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ class MetadataParserImpl implements MetadataParser {
|
||||
if (reader.hasBoolean()) return reader.readBoolean();
|
||||
if (reader.hasLong()) return reader.readLong();
|
||||
if (reader.hasDouble()) return reader.readDouble();
|
||||
if (reader.hasString()) return reader.readString(Integer.MAX_VALUE);
|
||||
if (reader.hasRaw()) return reader.readRaw(Integer.MAX_VALUE);
|
||||
if (reader.hasString()) return reader.readString();
|
||||
if (reader.hasRaw()) return reader.readRaw();
|
||||
if (reader.hasList()) return reader.readList();
|
||||
if (reader.hasDictionary()) return reader.readDictionary();
|
||||
throw new FormatException();
|
||||
|
||||
@@ -34,8 +34,8 @@ import javax.annotation.Nullable;
|
||||
* A low-level interface to the database (DatabaseComponent provides a
|
||||
* high-level interface). Most operations take a transaction argument, which is
|
||||
* obtained by calling {@link #startTransaction()}. Every transaction must be
|
||||
* terminated by calling either {@link #abortTransaction(T)} or
|
||||
* {@link #commitTransaction(T)}, even if an exception is thrown.
|
||||
* terminated by calling either {@link #abortTransaction(Object) abortTransaction(T)} or
|
||||
* {@link #commitTransaction(Object) commitTransaction(T)}, even if an exception is thrown.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
interface Database<T> {
|
||||
@@ -125,16 +125,10 @@ interface Database<T> {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given transport keys, optionally binding them to the given
|
||||
* contact, and returns a key set ID.
|
||||
* Stores the given transport keys for the given contact and returns a
|
||||
* key set ID.
|
||||
*/
|
||||
KeySetId addTransportKeys(T txn, @Nullable ContactId c, TransportKeys k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Binds the given keys for the given transport to the given contact.
|
||||
*/
|
||||
void bindTransportKeys(T txn, ContactId c, TransportId t, KeySetId k)
|
||||
KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -234,27 +234,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySetId addTransportKeys(Transaction transaction,
|
||||
@Nullable ContactId c, TransportKeys k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (c != null && !db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
if (!db.containsTransport(txn, k.getTransportId()))
|
||||
throw new NoSuchTransportException();
|
||||
return db.addTransportKeys(txn, c, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTransportKeys(Transaction transaction, ContactId c,
|
||||
TransportId t, KeySetId k) throws DbException {
|
||||
public KeySetId addTransportKeys(Transaction transaction, ContactId c,
|
||||
TransportKeys k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
if (!db.containsTransport(txn, t))
|
||||
if (!db.containsTransport(txn, k.getTransportId()))
|
||||
throw new NoSuchTransportException();
|
||||
db.bindTransportKeys(txn, c, t, k);
|
||||
return db.addTransportKeys(txn, c, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -53,6 +53,7 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.sql.Types.INTEGER;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||
@@ -74,7 +75,7 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
||||
abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
// Package access for testing
|
||||
static final int CODE_SCHEMA_VERSION = 38;
|
||||
static final int CODE_SCHEMA_VERSION = 39;
|
||||
|
||||
// Rotation period offsets for incoming transport keys
|
||||
private static final int OFFSET_PREV = -1;
|
||||
@@ -236,7 +237,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " (transportId _STRING NOT NULL,"
|
||||
+ " keySetId _COUNTER,"
|
||||
+ " rotationPeriod BIGINT NOT NULL,"
|
||||
+ " contactId INT," // Null if keys are not bound
|
||||
+ " contactId INT NOT NULL,"
|
||||
+ " tagKey _SECRET NOT NULL,"
|
||||
+ " headerKey _SECRET NOT NULL,"
|
||||
+ " stream BIGINT NOT NULL,"
|
||||
@@ -255,7 +256,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " (transportId _STRING NOT NULL,"
|
||||
+ " keySetId INT NOT NULL,"
|
||||
+ " rotationPeriod BIGINT NOT NULL,"
|
||||
+ " contactId INT," // Null if keys are not bound
|
||||
+ " contactId INT NOT NULL,"
|
||||
+ " tagKey _SECRET NOT NULL,"
|
||||
+ " headerKey _SECRET NOT NULL,"
|
||||
+ " base BIGINT NOT NULL,"
|
||||
@@ -389,7 +390,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
// Package access for testing
|
||||
List<Migration<Connection>> getMigrations() {
|
||||
return Collections.emptyList();
|
||||
return singletonList(new Migration38_39());
|
||||
}
|
||||
|
||||
private void storeSchemaVersion(Connection txn, int version)
|
||||
@@ -883,7 +884,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySetId addTransportKeys(Connection txn, @Nullable ContactId c,
|
||||
public KeySetId addTransportKeys(Connection txn, ContactId c,
|
||||
TransportKeys k) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
@@ -893,8 +894,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " rotationPeriod, tagKey, headerKey, stream, active)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
if (c == null) ps.setNull(1, INTEGER);
|
||||
else ps.setInt(1, c.getInt());
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setString(2, k.getTransportId().getString());
|
||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||
ps.setLong(3, outCurr.getRotationPeriod());
|
||||
@@ -922,8 +922,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, keySetId.getInt());
|
||||
if (c == null) ps.setNull(2, INTEGER);
|
||||
else ps.setInt(2, c.getInt());
|
||||
ps.setInt(2, c.getInt());
|
||||
ps.setString(3, k.getTransportId().getString());
|
||||
// Previous rotation period
|
||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||
@@ -965,33 +964,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTransportKeys(Connection txn, ContactId c, TransportId t,
|
||||
KeySetId k) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE outgoingKeys SET contactId = ?"
|
||||
+ " WHERE keySetId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setInt(2, k.getInt());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0) throw new DbStateException();
|
||||
ps.close();
|
||||
sql = "UPDATE incomingKeys SET contactId = ?"
|
||||
+ " WHERE keySetId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setInt(2, k.getInt());
|
||||
affected = ps.executeUpdate();
|
||||
if (affected < 0) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsContact(Connection txn, AuthorId remote,
|
||||
AuthorId local) throws DbException {
|
||||
@@ -2172,7 +2144,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
|
||||
KeySetId keySetId = new KeySetId(rs.getInt(1));
|
||||
ContactId contactId = new ContactId(rs.getInt(2));
|
||||
if (rs.wasNull()) contactId = null;
|
||||
long rotationPeriod = rs.getLong(3);
|
||||
SecretKey tagKey = new SecretKey(rs.getBytes(4));
|
||||
SecretKey headerKey = new SecretKey(rs.getBytes(5));
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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 javax.annotation.Nullable;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
class Migration38_39 implements Migration<Connection> {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(Migration38_39.class.getName());
|
||||
|
||||
@Override
|
||||
public int getStartVersion() {
|
||||
return 38;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndVersion() {
|
||||
return 39;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(Connection txn) throws DbException {
|
||||
Statement s = null;
|
||||
try {
|
||||
s = txn.createStatement();
|
||||
// Add not null constraints
|
||||
s.execute("ALTER TABLE outgoingKeys"
|
||||
+ " ALTER COLUMN contactId"
|
||||
+ " SET NOT NULL");
|
||||
s.execute("ALTER TABLE incomingKeys"
|
||||
+ " ALTER COLUMN contactId"
|
||||
+ " SET NOT NULL");
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryToClose(@Nullable Statement s) {
|
||||
try {
|
||||
if (s != null) s.close();
|
||||
} catch (SQLException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,12 @@ import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.bramble.api.transport.KeyManager;
|
||||
import org.briarproject.bramble.api.transport.StreamContext;
|
||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -101,7 +101,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
|
||||
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
|
||||
TransportConnectionWriter w) throws IOException {
|
||||
OutputStream streamWriter = streamWriterFactory.createStreamWriter(
|
||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||
w.getOutputStream(), ctx);
|
||||
return syncSessionFactory.createSimplexOutgoingSession(
|
||||
ctx.getContactId(), w.getMaxLatency(), streamWriter);
|
||||
@@ -109,7 +109,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
|
||||
private SyncSession createDuplexOutgoingSession(StreamContext ctx,
|
||||
TransportConnectionWriter w) throws IOException {
|
||||
OutputStream streamWriter = streamWriterFactory.createStreamWriter(
|
||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||
w.getOutputStream(), ctx);
|
||||
return syncSessionFactory.createDuplexOutgoingSession(
|
||||
ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(),
|
||||
@@ -300,8 +300,8 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
}
|
||||
|
||||
private void disposeReader(boolean exception, boolean recognised) {
|
||||
if (exception && outgoingSession != null)
|
||||
outgoingSession.interrupt();
|
||||
// Interrupt the outgoing session so it finishes cleanly
|
||||
if (outgoingSession != null) outgoingSession.interrupt();
|
||||
try {
|
||||
reader.dispose(exception, recognised);
|
||||
} catch (IOException e) {
|
||||
@@ -310,6 +310,8 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
}
|
||||
|
||||
private void disposeWriter(boolean exception) {
|
||||
// Interrupt the incoming session if an exception occurred,
|
||||
// otherwise wait for the end of stream marker
|
||||
if (exception && incomingSession != null)
|
||||
incomingSession.interrupt();
|
||||
try {
|
||||
@@ -407,8 +409,8 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
}
|
||||
|
||||
private void disposeReader(boolean exception, boolean recognised) {
|
||||
if (exception && outgoingSession != null)
|
||||
outgoingSession.interrupt();
|
||||
// Interrupt the outgoing session so it finishes cleanly
|
||||
if (outgoingSession != null) outgoingSession.interrupt();
|
||||
try {
|
||||
reader.dispose(exception, recognised);
|
||||
} catch (IOException e) {
|
||||
@@ -417,6 +419,8 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
}
|
||||
|
||||
private void disposeWriter(boolean exception) {
|
||||
// Interrupt the incoming session if an exception occurred,
|
||||
// otherwise wait for the end of stream marker
|
||||
if (exception && incomingSession != null)
|
||||
incomingSession.interrupt();
|
||||
try {
|
||||
|
||||
@@ -106,7 +106,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
|
||||
if (m == null) return Collections.emptyList();
|
||||
List<ContactId> ids = new ArrayList<>(m.keySet());
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info(ids.size() + " contacts connected");
|
||||
LOG.info(ids.size() + " contacts connected: " + t);
|
||||
return ids;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package org.briarproject.bramble.reporting;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TorConstants;
|
||||
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
|
||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||
import org.briarproject.bramble.util.IoUtils;
|
||||
@@ -18,17 +23,19 @@ import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class DevReporterImpl implements DevReporter {
|
||||
class DevReporterImpl implements DevReporter, EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(DevReporterImpl.class.getName());
|
||||
@@ -36,12 +43,15 @@ class DevReporterImpl implements DevReporter {
|
||||
private static final int SOCKET_TIMEOUT = 30 * 1000; // 30 seconds
|
||||
private static final int LINE_LENGTH = 70;
|
||||
|
||||
private final Executor ioExecutor;
|
||||
private final CryptoComponent crypto;
|
||||
private final DevConfig devConfig;
|
||||
private final SocketFactory torSocketFactory;
|
||||
|
||||
DevReporterImpl(CryptoComponent crypto, DevConfig devConfig,
|
||||
SocketFactory torSocketFactory) {
|
||||
@Inject
|
||||
DevReporterImpl(@IoExecutor Executor ioExecutor, CryptoComponent crypto,
|
||||
DevConfig devConfig, SocketFactory torSocketFactory) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.crypto = crypto;
|
||||
this.devConfig = devConfig;
|
||||
this.torSocketFactory = torSocketFactory;
|
||||
@@ -63,6 +73,7 @@ class DevReporterImpl implements DevReporter {
|
||||
@Override
|
||||
public void encryptReportToFile(File reportDir, String filename,
|
||||
String report) throws FileNotFoundException {
|
||||
LOG.info("Encrypting report to file");
|
||||
byte[] plaintext = StringUtils.toUtf8(report);
|
||||
byte[] ciphertext = crypto.encryptToKey(devConfig.getDevPublicKey(),
|
||||
plaintext);
|
||||
@@ -82,7 +93,17 @@ class DevReporterImpl implements DevReporter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendReports(File reportDir) {
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof TransportEnabledEvent) {
|
||||
TransportEnabledEvent t = (TransportEnabledEvent) e;
|
||||
if (t.getTransportId().equals(TorConstants.ID))
|
||||
ioExecutor.execute(this::sendReports);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendReports() {
|
||||
File reportDir = devConfig.getReportDir();
|
||||
File[] reports = reportDir.listFiles();
|
||||
if (reports == null || reports.length == 0)
|
||||
return; // No reports to send
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.briarproject.bramble.reporting;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -12,9 +12,16 @@ import dagger.Provides;
|
||||
@Module
|
||||
public class ReportingModule {
|
||||
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
DevReporter devReporter;
|
||||
}
|
||||
|
||||
@Provides
|
||||
DevReporter provideDevReporter(CryptoComponent crypto,
|
||||
DevConfig devConfig, SocketFactory torSocketFactory) {
|
||||
return new DevReporterImpl(crypto, devConfig, torSocketFactory);
|
||||
@Singleton
|
||||
DevReporter provideDevReporter(DevReporterImpl devReporter,
|
||||
EventBus eventBus) {
|
||||
eventBus.addListener(devReporter);
|
||||
return devReporter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.briarproject.bramble.api.sync.event.MessageSharedEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
@@ -67,6 +68,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
private final Clock clock;
|
||||
private final ContactId contactId;
|
||||
private final int maxLatency, maxIdleTime;
|
||||
private final StreamWriter streamWriter;
|
||||
private final SyncRecordWriter recordWriter;
|
||||
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
|
||||
|
||||
@@ -81,7 +83,8 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
|
||||
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
|
||||
EventBus eventBus, Clock clock, ContactId contactId, int maxLatency,
|
||||
int maxIdleTime, SyncRecordWriter recordWriter) {
|
||||
int maxIdleTime, StreamWriter streamWriter,
|
||||
SyncRecordWriter recordWriter) {
|
||||
this.db = db;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.eventBus = eventBus;
|
||||
@@ -89,6 +92,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
this.contactId = contactId;
|
||||
this.maxLatency = maxLatency;
|
||||
this.maxIdleTime = maxIdleTime;
|
||||
this.streamWriter = streamWriter;
|
||||
this.recordWriter = recordWriter;
|
||||
writerTasks = new LinkedBlockingQueue<>();
|
||||
}
|
||||
@@ -149,7 +153,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
dataToFlush = true;
|
||||
}
|
||||
}
|
||||
if (dataToFlush) recordWriter.flush();
|
||||
streamWriter.sendEndOfStream();
|
||||
} catch (InterruptedException e) {
|
||||
LOG.info("Interrupted while waiting for a record to write");
|
||||
Thread.currentThread().interrupt();
|
||||
|
||||
@@ -63,7 +63,11 @@ class IncomingSession implements SyncSession, EventListener {
|
||||
eventBus.addListener(this);
|
||||
try {
|
||||
// Read records until interrupted or EOF
|
||||
while (!interrupted && !recordReader.eof()) {
|
||||
while (!interrupted) {
|
||||
if (recordReader.eof()) {
|
||||
LOG.info("End of stream");
|
||||
return;
|
||||
}
|
||||
if (recordReader.hasAck()) {
|
||||
Ack a = recordReader.readAck();
|
||||
dbExecutor.execute(new ReceiveAck(a));
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.Ack;
|
||||
import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
||||
import org.briarproject.bramble.api.sync.SyncSession;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
@@ -51,6 +52,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
|
||||
private final EventBus eventBus;
|
||||
private final ContactId contactId;
|
||||
private final int maxLatency;
|
||||
private final StreamWriter streamWriter;
|
||||
private final SyncRecordWriter recordWriter;
|
||||
private final AtomicInteger outstandingQueries;
|
||||
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
|
||||
@@ -58,13 +60,14 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
|
||||
private volatile boolean interrupted = false;
|
||||
|
||||
SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
|
||||
EventBus eventBus, ContactId contactId,
|
||||
int maxLatency, SyncRecordWriter recordWriter) {
|
||||
EventBus eventBus, ContactId contactId, int maxLatency,
|
||||
StreamWriter streamWriter, SyncRecordWriter recordWriter) {
|
||||
this.db = db;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.eventBus = eventBus;
|
||||
this.contactId = contactId;
|
||||
this.maxLatency = maxLatency;
|
||||
this.streamWriter = streamWriter;
|
||||
this.recordWriter = recordWriter;
|
||||
outstandingQueries = new AtomicInteger(2); // One per type of record
|
||||
writerTasks = new LinkedBlockingQueue<>();
|
||||
@@ -85,7 +88,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
|
||||
if (task == CLOSE) break;
|
||||
task.run();
|
||||
}
|
||||
recordWriter.flush();
|
||||
streamWriter.sendEndOfStream();
|
||||
} catch (InterruptedException e) {
|
||||
LOG.info("Interrupted while waiting for a record to write");
|
||||
Thread.currentThread().interrupt();
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.sync.SyncRecordWriterFactory;
|
||||
import org.briarproject.bramble.api.sync.SyncSession;
|
||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@@ -53,19 +54,21 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
|
||||
|
||||
@Override
|
||||
public SyncSession createSimplexOutgoingSession(ContactId c,
|
||||
int maxLatency, OutputStream out) {
|
||||
int maxLatency, StreamWriter streamWriter) {
|
||||
OutputStream out = streamWriter.getOutputStream();
|
||||
SyncRecordWriter recordWriter =
|
||||
recordWriterFactory.createRecordWriter(out);
|
||||
return new SimplexOutgoingSession(db, dbExecutor, eventBus, c,
|
||||
maxLatency, recordWriter);
|
||||
maxLatency, streamWriter, recordWriter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
|
||||
int maxIdleTime, OutputStream out) {
|
||||
int maxIdleTime, StreamWriter streamWriter) {
|
||||
OutputStream out = streamWriter.getOutputStream();
|
||||
SyncRecordWriter recordWriter =
|
||||
recordWriterFactory.createRecordWriter(out);
|
||||
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c,
|
||||
maxLatency, maxIdleTime, recordWriter);
|
||||
maxLatency, maxIdleTime, streamWriter, recordWriter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,39 +99,18 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContact(Transaction txn, ContactId c, SecretKey master,
|
||||
long timestamp, boolean alice) throws DbException {
|
||||
for (TransportKeyManager m : managers.values())
|
||||
m.addContact(txn, c, master, timestamp, alice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, KeySetId> addUnboundKeys(Transaction txn,
|
||||
SecretKey master, long timestamp, boolean alice)
|
||||
public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
|
||||
SecretKey master, long timestamp, boolean alice, boolean active)
|
||||
throws DbException {
|
||||
Map<TransportId, KeySetId> ids = new HashMap<>();
|
||||
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||
TransportId t = e.getKey();
|
||||
TransportKeyManager m = e.getValue();
|
||||
ids.put(t, m.addUnboundKeys(txn, master, timestamp, alice));
|
||||
ids.put(t, m.addContact(txn, c, master, timestamp, alice, active));
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindKeys(Transaction txn, ContactId c,
|
||||
Map<TransportId, KeySetId> keys) throws DbException {
|
||||
for (Entry<TransportId, KeySetId> e : keys.entrySet()) {
|
||||
TransportId t = e.getKey();
|
||||
TransportKeyManager m = managers.get(t);
|
||||
if (m == null) {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
||||
} else {
|
||||
m.bindKeys(txn, c, e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
||||
throws DbException {
|
||||
@@ -146,24 +125,10 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
||||
throws DbException {
|
||||
for (Entry<TransportId, KeySetId> e : keys.entrySet()) {
|
||||
TransportId t = e.getKey();
|
||||
TransportKeyManager m = managers.get(t);
|
||||
if (m == null) {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
||||
} else {
|
||||
m.removeKeys(txn, e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSendOutgoingStreams(ContactId c, TransportId t) {
|
||||
TransportKeyManager m = managers.get(t);
|
||||
return m == null ? false : m.canSendOutgoingStreams(c);
|
||||
return m != null && m.canSendOutgoingStreams(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,32 +3,28 @@ package org.briarproject.bramble.transport;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class MutableKeySet {
|
||||
class MutableKeySet {
|
||||
|
||||
private final KeySetId keySetId;
|
||||
@Nullable
|
||||
private final ContactId contactId;
|
||||
private final MutableTransportKeys transportKeys;
|
||||
|
||||
public MutableKeySet(KeySetId keySetId, @Nullable ContactId contactId,
|
||||
MutableKeySet(KeySetId keySetId, ContactId contactId,
|
||||
MutableTransportKeys transportKeys) {
|
||||
this.keySetId = keySetId;
|
||||
this.contactId = contactId;
|
||||
this.transportKeys = transportKeys;
|
||||
}
|
||||
|
||||
public KeySetId getKeySetId() {
|
||||
KeySetId getKeySetId() {
|
||||
return keySetId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ContactId getContactId() {
|
||||
ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
public MutableTransportKeys getTransportKeys() {
|
||||
MutableTransportKeys getTransportKeys() {
|
||||
return transportKeys;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.transport.StreamContext;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||
|
||||
import java.io.OutputStream;
|
||||
@@ -23,14 +24,14 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream createStreamWriter(OutputStream out,
|
||||
public StreamWriter createStreamWriter(OutputStream out,
|
||||
StreamContext ctx) {
|
||||
return new StreamWriterImpl(
|
||||
streamEncrypterFactory.createStreamEncrypter(out, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream createContactExchangeStreamWriter(OutputStream out,
|
||||
public StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
||||
SecretKey headerKey) {
|
||||
return new StreamWriterImpl(
|
||||
streamEncrypterFactory.createContactExchangeStreamDecrypter(out,
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -17,7 +18,7 @@ import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYL
|
||||
*/
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class StreamWriterImpl extends OutputStream {
|
||||
class StreamWriterImpl extends OutputStream implements StreamWriter {
|
||||
|
||||
private final StreamEncrypter encrypter;
|
||||
private final byte[] payload;
|
||||
@@ -29,6 +30,17 @@ class StreamWriterImpl extends OutputStream {
|
||||
payload = new byte[MAX_PAYLOAD_LENGTH];
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEndOfStream() throws IOException {
|
||||
writeFrame(true);
|
||||
encrypter.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
writeFrame(true);
|
||||
|
||||
@@ -15,18 +15,11 @@ interface TransportKeyManager {
|
||||
|
||||
void start(Transaction txn) throws DbException;
|
||||
|
||||
void addContact(Transaction txn, ContactId c, SecretKey master,
|
||||
long timestamp, boolean alice) throws DbException;
|
||||
|
||||
KeySetId addUnboundKeys(Transaction txn, SecretKey master, long timestamp,
|
||||
boolean alice) throws DbException;
|
||||
|
||||
void bindKeys(Transaction txn, ContactId c, KeySetId k) throws DbException;
|
||||
KeySetId addContact(Transaction txn, ContactId c, SecretKey master,
|
||||
long timestamp, boolean alice, boolean active) throws DbException;
|
||||
|
||||
void activateKeys(Transaction txn, KeySetId k) throws DbException;
|
||||
|
||||
void removeKeys(Transaction txn, KeySetId k) throws DbException;
|
||||
|
||||
void removeContact(ContactId c);
|
||||
|
||||
boolean canSendOutgoingStreams(ContactId c);
|
||||
|
||||
@@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
@@ -119,16 +118,14 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
|
||||
// Locking: lock
|
||||
private void addKeys(KeySetId keySetId, @Nullable ContactId contactId,
|
||||
private void addKeys(KeySetId keySetId, ContactId contactId,
|
||||
MutableTransportKeys m) {
|
||||
MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
|
||||
keys.put(keySetId, ks);
|
||||
if (contactId != null) {
|
||||
encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
|
||||
encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
|
||||
encodeTags(keySetId, contactId, m.getNextIncomingKeys());
|
||||
considerReplacingOutgoingKeys(ks);
|
||||
}
|
||||
encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
|
||||
encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
|
||||
encodeTags(keySetId, contactId, m.getNextIncomingKeys());
|
||||
considerReplacingOutgoingKeys(ks);
|
||||
}
|
||||
|
||||
// Locking: lock
|
||||
@@ -150,8 +147,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
if (ks.getTransportKeys().getCurrentOutgoingKeys().isActive()) {
|
||||
MutableKeySet old = outContexts.get(ks.getContactId());
|
||||
if (old == null ||
|
||||
old.getKeySetId().getInt() < ks.getKeySetId().getInt())
|
||||
old.getKeySetId().getInt() < ks.getKeySetId().getInt()) {
|
||||
outContexts.put(ks.getContactId(), ks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,20 +175,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContact(Transaction txn, ContactId c, SecretKey master,
|
||||
long timestamp, boolean alice) throws DbException {
|
||||
deriveAndAddKeys(txn, c, master, timestamp, alice, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySetId addUnboundKeys(Transaction txn, SecretKey master,
|
||||
long timestamp, boolean alice) throws DbException {
|
||||
return deriveAndAddKeys(txn, null, master, timestamp, alice, false);
|
||||
}
|
||||
|
||||
private KeySetId deriveAndAddKeys(Transaction txn, @Nullable ContactId c,
|
||||
SecretKey master, long timestamp, boolean alice, boolean active)
|
||||
throws DbException {
|
||||
public KeySetId addContact(Transaction txn, ContactId c, SecretKey master,
|
||||
long timestamp, boolean alice, boolean active) throws DbException {
|
||||
lock.lock();
|
||||
try {
|
||||
// Work out what rotation period the timestamp belongs to
|
||||
@@ -211,31 +197,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindKeys(Transaction txn, ContactId c, KeySetId k)
|
||||
throws DbException {
|
||||
lock.lock();
|
||||
try {
|
||||
MutableKeySet ks = keys.get(k);
|
||||
if (ks == null) throw new IllegalArgumentException();
|
||||
// Check that the keys haven't already been bound
|
||||
if (ks.getContactId() != null) throw new IllegalArgumentException();
|
||||
MutableTransportKeys m = ks.getTransportKeys();
|
||||
addKeys(k, c, m);
|
||||
db.bindTransportKeys(txn, c, m.getTransportId(), k);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateKeys(Transaction txn, KeySetId k) throws DbException {
|
||||
lock.lock();
|
||||
try {
|
||||
MutableKeySet ks = keys.get(k);
|
||||
if (ks == null) throw new IllegalArgumentException();
|
||||
// Check that the keys have been bound
|
||||
if (ks.getContactId() == null) throw new IllegalArgumentException();
|
||||
MutableTransportKeys m = ks.getTransportKeys();
|
||||
m.getCurrentOutgoingKeys().activate();
|
||||
considerReplacingOutgoingKeys(ks);
|
||||
@@ -245,21 +212,6 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeKeys(Transaction txn, KeySetId k) throws DbException {
|
||||
lock.lock();
|
||||
try {
|
||||
MutableKeySet ks = keys.remove(k);
|
||||
if (ks == null) throw new IllegalArgumentException();
|
||||
// Check that the keys haven't been bound
|
||||
if (ks.getContactId() != null) throw new IllegalArgumentException();
|
||||
TransportId t = ks.getTransportKeys().getTransportId();
|
||||
db.removeTransportKeys(txn, t, k);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeContact(ContactId c) {
|
||||
lock.lock();
|
||||
|
||||
@@ -55,8 +55,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(txn));
|
||||
oneOf(db).addContact(txn, remote, local, verified, active);
|
||||
will(returnValue(contactId));
|
||||
oneOf(keyManager)
|
||||
.addContact(txn, contactId, master, timestamp, alice);
|
||||
oneOf(keyManager).addContact(txn, contactId, master, timestamp,
|
||||
alice, active);
|
||||
oneOf(db).getContact(txn, contactId);
|
||||
will(returnValue(contact));
|
||||
oneOf(db).commitTransaction(txn);
|
||||
|
||||
@@ -4,13 +4,16 @@ import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
|
||||
import static org.briarproject.bramble.data.BdfReaderImpl.DEFAULT_NESTED_LIMIT;
|
||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -158,30 +161,32 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadString8() throws Exception {
|
||||
String longest = StringUtils.getRandomString(Byte.MAX_VALUE);
|
||||
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
|
||||
String longest = getRandomString(Byte.MAX_VALUE);
|
||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
||||
// "foo", the empty string, and 127 random letters
|
||||
setContents("41" + "03" + "666F6F" + "41" + "00" +
|
||||
"41" + "7F" + longHex);
|
||||
assertEquals("foo", r.readString(Integer.MAX_VALUE));
|
||||
assertEquals("", r.readString(Integer.MAX_VALUE));
|
||||
assertEquals(longest, r.readString(Integer.MAX_VALUE));
|
||||
assertEquals("foo", r.readString());
|
||||
assertEquals("", r.readString());
|
||||
assertEquals(longest, r.readString());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadString8ChecksMaxLength() throws Exception {
|
||||
// "foo" twice
|
||||
setContents("41" + "03" + "666F6F" + "41" + "03" + "666F6F");
|
||||
assertEquals("foo", r.readString(3));
|
||||
int maxBufferSize = 3;
|
||||
// "foo", "fooo"
|
||||
setContents("41" + "03" + "666F6F"
|
||||
+ "41" + "04" + "666F6F6F", maxBufferSize);
|
||||
assertEquals("foo", r.readString());
|
||||
assertTrue(r.hasString());
|
||||
r.readString(2);
|
||||
r.readString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipString8() throws Exception {
|
||||
String longest = StringUtils.getRandomString(Byte.MAX_VALUE);
|
||||
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
|
||||
String longest = getRandomString(Byte.MAX_VALUE);
|
||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
||||
// "foo", the empty string, and 127 random letters
|
||||
setContents("41" + "03" + "666F6F" + "41" + "00" +
|
||||
"41" + "7F" + longHex);
|
||||
@@ -193,34 +198,37 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadString16() throws Exception {
|
||||
String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
|
||||
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
|
||||
String longest = StringUtils.getRandomString(Short.MAX_VALUE);
|
||||
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
|
||||
String shortest = getRandomString(Byte.MAX_VALUE + 1);
|
||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
||||
String longest = getRandomString(Short.MAX_VALUE);
|
||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
||||
// 128 random letters and 2^15 -1 random letters
|
||||
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
|
||||
assertEquals(shortest, r.readString(Integer.MAX_VALUE));
|
||||
assertEquals(longest, r.readString(Integer.MAX_VALUE));
|
||||
assertEquals(shortest, r.readString());
|
||||
assertEquals(longest, r.readString());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadString16ChecksMaxLength() throws Exception {
|
||||
String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
|
||||
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
|
||||
// 128 random letters, twice
|
||||
setContents("42" + "0080" + shortHex + "42" + "0080" + shortHex);
|
||||
assertEquals(shortest, r.readString(Byte.MAX_VALUE + 1));
|
||||
int maxBufferSize = Byte.MAX_VALUE + 1;
|
||||
String valid = getRandomString(Byte.MAX_VALUE + 1);
|
||||
String validHex = toHexString(valid.getBytes("UTF-8"));
|
||||
String invalidhex = validHex + "20";
|
||||
// 128 random letters, the same plus a space
|
||||
setContents("42" + "0080" + validHex
|
||||
+ "42" + "0081" + invalidhex, maxBufferSize);
|
||||
assertEquals(valid, r.readString());
|
||||
assertTrue(r.hasString());
|
||||
r.readString(Byte.MAX_VALUE);
|
||||
r.readString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipString16() throws Exception {
|
||||
String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
|
||||
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
|
||||
String longest = StringUtils.getRandomString(Short.MAX_VALUE);
|
||||
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
|
||||
String shortest = getRandomString(Byte.MAX_VALUE + 1);
|
||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
||||
String longest = getRandomString(Short.MAX_VALUE);
|
||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
||||
// 128 random letters and 2^15 - 1 random letters
|
||||
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
|
||||
r.skipString();
|
||||
@@ -230,30 +238,32 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadString32() throws Exception {
|
||||
String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
|
||||
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
|
||||
String shortest = getRandomString(Short.MAX_VALUE + 1);
|
||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
||||
// 2^15 random letters
|
||||
setContents("44" + "00008000" + shortHex);
|
||||
assertEquals(shortest, r.readString(Integer.MAX_VALUE));
|
||||
assertEquals(shortest, r.readString());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadString32ChecksMaxLength() throws Exception {
|
||||
String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
|
||||
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
|
||||
// 2^15 random letters, twice
|
||||
setContents("44" + "00008000" + shortHex +
|
||||
"44" + "00008000" + shortHex);
|
||||
assertEquals(shortest, r.readString(Short.MAX_VALUE + 1));
|
||||
int maxBufferSize = Short.MAX_VALUE + 1;
|
||||
String valid = getRandomString(maxBufferSize);
|
||||
String validHex = toHexString(valid.getBytes("UTF-8"));
|
||||
String invalidHex = validHex + "20";
|
||||
// 2^15 random letters, the same plus a space
|
||||
setContents("44" + "00008000" + validHex +
|
||||
"44" + "00008001" + invalidHex, maxBufferSize);
|
||||
assertEquals(valid, r.readString());
|
||||
assertTrue(r.hasString());
|
||||
r.readString(Short.MAX_VALUE);
|
||||
r.readString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipString32() throws Exception {
|
||||
String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
|
||||
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
|
||||
String shortest = getRandomString(Short.MAX_VALUE + 1);
|
||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
||||
// 2^15 random letters, twice
|
||||
setContents("44" + "00008000" + shortHex +
|
||||
"44" + "00008000" + shortHex);
|
||||
@@ -265,41 +275,43 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadUtf8String() throws Exception {
|
||||
String unicode = "\uFDD0\uFDD1\uFDD2\uFDD3";
|
||||
String hex = StringUtils.toHexString(unicode.getBytes("UTF-8"));
|
||||
String hex = toHexString(unicode.getBytes("UTF-8"));
|
||||
// STRING_8 tag, "foo", the empty string, and the test string
|
||||
setContents("41" + "03" + "666F6F" + "41" + "00" + "41" + "0C" + hex);
|
||||
assertEquals("foo", r.readString(Integer.MAX_VALUE));
|
||||
assertEquals("", r.readString(Integer.MAX_VALUE));
|
||||
assertEquals(unicode, r.readString(Integer.MAX_VALUE));
|
||||
assertEquals("foo", r.readString());
|
||||
assertEquals("", r.readString());
|
||||
assertEquals(unicode, r.readString());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadRaw8() throws Exception {
|
||||
byte[] longest = new byte[Byte.MAX_VALUE];
|
||||
String longHex = StringUtils.toHexString(longest);
|
||||
String longHex = toHexString(longest);
|
||||
// {1, 2, 3}, {}, and 127 zero bytes
|
||||
setContents("51" + "03" + "010203" + "51" + "00" +
|
||||
"51" + "7F" + longHex);
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readRaw(Integer.MAX_VALUE));
|
||||
assertArrayEquals(new byte[0], r.readRaw(Integer.MAX_VALUE));
|
||||
assertArrayEquals(longest, r.readRaw(Integer.MAX_VALUE));
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readRaw());
|
||||
assertArrayEquals(new byte[0], r.readRaw());
|
||||
assertArrayEquals(longest, r.readRaw());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadRaw8ChecksMaxLength() throws Exception {
|
||||
// {1, 2, 3} twice
|
||||
setContents("51" + "03" + "010203" + "51" + "03" + "010203");
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readRaw(3));
|
||||
int maxBufferSize = 3;
|
||||
// {1, 2, 3}, {1, 2, 3, 4}
|
||||
setContents("51" + "03" + "010203" + "51" + "04" + "01020304",
|
||||
maxBufferSize);
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readRaw());
|
||||
assertTrue(r.hasRaw());
|
||||
r.readRaw(2);
|
||||
r.readRaw();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipRaw8() throws Exception {
|
||||
byte[] longest = new byte[Byte.MAX_VALUE];
|
||||
String longHex = StringUtils.toHexString(longest);
|
||||
String longHex = toHexString(longest);
|
||||
// {1, 2, 3}, {}, and 127 zero bytes
|
||||
setContents("51" + "03" + "010203" + "51" + "00" +
|
||||
"51" + "7F" + longHex);
|
||||
@@ -312,33 +324,36 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadRaw16() throws Exception {
|
||||
byte[] shortest = new byte[Byte.MAX_VALUE + 1];
|
||||
String shortHex = StringUtils.toHexString(shortest);
|
||||
String shortHex = toHexString(shortest);
|
||||
byte[] longest = new byte[Short.MAX_VALUE];
|
||||
String longHex = StringUtils.toHexString(longest);
|
||||
String longHex = toHexString(longest);
|
||||
// 128 zero bytes and 2^15 - 1 zero bytes
|
||||
setContents("52" + "0080" + shortHex + "52" + "7FFF" + longHex);
|
||||
assertArrayEquals(shortest, r.readRaw(Integer.MAX_VALUE));
|
||||
assertArrayEquals(longest, r.readRaw(Integer.MAX_VALUE));
|
||||
assertArrayEquals(shortest, r.readRaw());
|
||||
assertArrayEquals(longest, r.readRaw());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadRaw16ChecksMaxLength() throws Exception {
|
||||
byte[] shortest = new byte[Byte.MAX_VALUE + 1];
|
||||
String shortHex = StringUtils.toHexString(shortest);
|
||||
// 128 zero bytes, twice
|
||||
setContents("52" + "0080" + shortHex + "52" + "0080" + shortHex);
|
||||
assertArrayEquals(shortest, r.readRaw(Byte.MAX_VALUE + 1));
|
||||
int maxBufferSize = Byte.MAX_VALUE + 1;
|
||||
byte[] valid = new byte[maxBufferSize];
|
||||
String validHex = toHexString(valid);
|
||||
String invalidHex = validHex + "00";
|
||||
// 128 zero bytes, 129 zero bytes
|
||||
setContents("52" + "0080" + validHex
|
||||
+ "52" + "0081" + invalidHex, maxBufferSize);
|
||||
assertArrayEquals(valid, r.readRaw());
|
||||
assertTrue(r.hasRaw());
|
||||
r.readRaw(Byte.MAX_VALUE);
|
||||
r.readRaw();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipRaw16() throws Exception {
|
||||
byte[] shortest = new byte[Byte.MAX_VALUE + 1];
|
||||
String shortHex = StringUtils.toHexString(shortest);
|
||||
String shortHex = toHexString(shortest);
|
||||
byte[] longest = new byte[Short.MAX_VALUE];
|
||||
String longHex = StringUtils.toHexString(longest);
|
||||
String longHex = toHexString(longest);
|
||||
// 128 zero bytes and 2^15 - 1 zero bytes
|
||||
setContents("52" + "0080" + shortHex + "52" + "7FFF" + longHex);
|
||||
r.skipRaw();
|
||||
@@ -349,29 +364,31 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadRaw32() throws Exception {
|
||||
byte[] shortest = new byte[Short.MAX_VALUE + 1];
|
||||
String shortHex = StringUtils.toHexString(shortest);
|
||||
String shortHex = toHexString(shortest);
|
||||
// 2^15 zero bytes
|
||||
setContents("54" + "00008000" + shortHex);
|
||||
assertArrayEquals(shortest, r.readRaw(Integer.MAX_VALUE));
|
||||
assertArrayEquals(shortest, r.readRaw());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadRaw32ChecksMaxLength() throws Exception {
|
||||
byte[] shortest = new byte[Short.MAX_VALUE + 1];
|
||||
String shortHex = StringUtils.toHexString(shortest);
|
||||
// 2^15 zero bytes, twice
|
||||
setContents("54" + "00008000" + shortHex +
|
||||
"54" + "00008000" + shortHex);
|
||||
assertArrayEquals(shortest, r.readRaw(Short.MAX_VALUE + 1));
|
||||
int maxBufferSize = Short.MAX_VALUE + 1;
|
||||
byte[] valid = new byte[maxBufferSize];
|
||||
String validHex = toHexString(valid);
|
||||
String invalidHex = validHex + "00";
|
||||
// 2^15 zero bytes, 2^15 + 1 zero bytes
|
||||
setContents("54" + "00008000" + validHex +
|
||||
"54" + "00008001" + invalidHex, maxBufferSize);
|
||||
assertArrayEquals(valid, r.readRaw());
|
||||
assertTrue(r.hasRaw());
|
||||
r.readRaw(Short.MAX_VALUE);
|
||||
r.readRaw();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipRaw32() throws Exception {
|
||||
byte[] shortest = new byte[Short.MAX_VALUE + 1];
|
||||
String shortHex = StringUtils.toHexString(shortest);
|
||||
String shortHex = toHexString(shortest);
|
||||
// 2^15 zero bytes, twice
|
||||
setContents("54" + "00008000" + shortHex +
|
||||
"54" + "00008000" + shortHex);
|
||||
@@ -393,6 +410,30 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
assertEquals(NULL_VALUE, list.get(2));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadListChecksMaxLengthForString() throws Exception {
|
||||
// A list containing "foo", a list containing "fooo"
|
||||
setContents("60" + "41" + "03" + "666F6F" + "80"
|
||||
+ "60" + "41" + "04" + "666F6F6F" + "80", 3);
|
||||
BdfList list = r.readList();
|
||||
assertEquals(1, list.size());
|
||||
assertEquals("foo", list.get(0));
|
||||
assertTrue(r.hasList());
|
||||
r.readList();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadListChecksMaxLengthForRaw() throws Exception {
|
||||
// A list containing {1, 2, 3}, a list containing {1, 2, 3, 4}
|
||||
setContents("60" + "51" + "03" + "010203" + "80"
|
||||
+ "60" + "51" + "04" + "01020304" + "80", 3);
|
||||
BdfList list = r.readList();
|
||||
assertEquals(1, list.size());
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, (byte[]) list.get(0));
|
||||
assertTrue(r.hasList());
|
||||
r.readList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadListManually() throws Exception {
|
||||
// A list containing 1, "foo", and null
|
||||
@@ -403,7 +444,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals(1, r.readLong());
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals("foo", r.readString(1000));
|
||||
assertEquals("foo", r.readString());
|
||||
assertFalse(r.hasListEnd());
|
||||
assertTrue(r.hasNull());
|
||||
r.readNull();
|
||||
@@ -435,6 +476,47 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
assertEquals(NULL_VALUE, dictionary.get("bar"));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadDictionaryChecksMaxLengthForKey() throws Exception {
|
||||
// A dictionary containing "foo" -> null, a dictionary containing
|
||||
// "fooo" -> null
|
||||
setContents("70" + "41" + "03" + "666F6F" + "00" + "80"
|
||||
+ "70" + "41" + "04" + "666F6F6F" + "00" + "80", 3);
|
||||
BdfDictionary dictionary = r.readDictionary();
|
||||
assertEquals(1, dictionary.size());
|
||||
assertEquals(NULL_VALUE, dictionary.get("foo"));
|
||||
assertTrue(r.hasDictionary());
|
||||
r.readDictionary();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadDictionaryChecksMaxLengthForString() throws Exception {
|
||||
// A dictionary containing "foo" -> "bar", a dictionary containing
|
||||
// "foo" -> "baar"
|
||||
String foo = "41" + "03" + "666F6F";
|
||||
setContents("70" + foo + "41" + "03" + "626172" + "80"
|
||||
+ "70" + foo + "41" + "04" + "62616172" + "80", 3);
|
||||
BdfDictionary dictionary = r.readDictionary();
|
||||
assertEquals(1, dictionary.size());
|
||||
assertEquals("bar", dictionary.get("foo"));
|
||||
assertTrue(r.hasDictionary());
|
||||
r.readDictionary();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadDictionaryChecksMaxLengthForRaw() throws Exception {
|
||||
// A dictionary containing "foo" -> {1, 2, 3}, a dictionary containing
|
||||
// "foo" -> {1, 2, 3, 4}
|
||||
String foo = "41" + "03" + "666F6F";
|
||||
setContents("70" + foo + "51" + "03" + "010203" + "80"
|
||||
+ "70" + foo + "51" + "04" + "01020304" + "80", 3);
|
||||
BdfDictionary dictionary = r.readDictionary();
|
||||
assertEquals(1, dictionary.size());
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, (byte[]) dictionary.get("foo"));
|
||||
assertTrue(r.hasDictionary());
|
||||
r.readDictionary();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDictionaryManually() throws Exception {
|
||||
// A dictionary containing "foo" -> 123 and "bar" -> null
|
||||
@@ -442,11 +524,11 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
"41" + "03" + "626172" + "00" + "80");
|
||||
r.readDictionaryStart();
|
||||
assertFalse(r.hasDictionaryEnd());
|
||||
assertEquals("foo", r.readString(1000));
|
||||
assertEquals("foo", r.readString());
|
||||
assertFalse(r.hasDictionaryEnd());
|
||||
assertEquals(123, r.readLong());
|
||||
assertFalse(r.hasDictionaryEnd());
|
||||
assertEquals("bar", r.readString(1000));
|
||||
assertEquals("bar", r.readString());
|
||||
assertFalse(r.hasDictionaryEnd());
|
||||
assertTrue(r.hasNull());
|
||||
r.readNull();
|
||||
@@ -537,8 +619,11 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
private void setContents(String hex) {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(
|
||||
StringUtils.fromHexString(hex));
|
||||
r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT);
|
||||
setContents(hex, DEFAULT_MAX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
private void setContents(String hex, int maxBufferSize) {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(fromHexString(hex));
|
||||
r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT, maxBufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,11 +289,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
// Check whether the contact is in the DB (which it's not)
|
||||
exactly(17).of(database).startTransaction();
|
||||
exactly(16).of(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
exactly(17).of(database).containsContact(txn, contactId);
|
||||
exactly(16).of(database).containsContact(txn, contactId);
|
||||
will(returnValue(false));
|
||||
exactly(17).of(database).abortTransaction(txn);
|
||||
exactly(16).of(database).abortTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
@@ -308,16 +308,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction(false);
|
||||
try {
|
||||
db.bindTransportKeys(transaction, contactId, transportId, keySetId);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction(false);
|
||||
try {
|
||||
db.generateAck(transaction, contactId, 123);
|
||||
@@ -773,13 +763,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
// endTransaction()
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// Check whether the transport is in the DB (which it's not)
|
||||
exactly(6).of(database).startTransaction();
|
||||
exactly(5).of(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
exactly(6).of(database).containsTransport(txn, transportId);
|
||||
exactly(5).of(database).containsTransport(txn, transportId);
|
||||
will(returnValue(false));
|
||||
exactly(6).of(database).abortTransaction(txn);
|
||||
exactly(5).of(database).abortTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
@@ -794,16 +782,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction(false);
|
||||
try {
|
||||
db.bindTransportKeys(transaction, contactId, transportId, keySetId);
|
||||
fail();
|
||||
} catch (NoSuchTransportException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction(false);
|
||||
try {
|
||||
db.getTransportKeys(transaction, transportId);
|
||||
|
||||
@@ -94,6 +94,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
private final TransportId transportId;
|
||||
private final ContactId contactId;
|
||||
private final KeySetId keySetId, keySetId1;
|
||||
private final Random random = new Random();
|
||||
|
||||
JdbcDatabaseTest() throws Exception {
|
||||
clientId = getClientId();
|
||||
@@ -670,8 +671,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testTransportKeys() throws Exception {
|
||||
long rotationPeriod = 123, rotationPeriod1 = 234;
|
||||
TransportKeys keys = createTransportKeys(rotationPeriod);
|
||||
TransportKeys keys1 = createTransportKeys(rotationPeriod1);
|
||||
boolean active = random.nextBoolean();
|
||||
TransportKeys keys = createTransportKeys(rotationPeriod, active);
|
||||
TransportKeys keys1 = createTransportKeys(rotationPeriod1, active);
|
||||
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
@@ -682,7 +684,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add the contact, the transport and the transport keys
|
||||
db.addLocalAuthor(txn, localAuthor);
|
||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
||||
true, true));
|
||||
true, active));
|
||||
db.addTransport(txn, transportId, 123);
|
||||
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
|
||||
@@ -701,8 +703,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
// Rotate the transport keys
|
||||
TransportKeys rotated = createTransportKeys(rotationPeriod + 1);
|
||||
TransportKeys rotated1 = createTransportKeys(rotationPeriod1 + 1);
|
||||
TransportKeys rotated = createTransportKeys(rotationPeriod + 1, active);
|
||||
TransportKeys rotated1 =
|
||||
createTransportKeys(rotationPeriod1 + 1, active);
|
||||
db.updateTransportKeys(txn, new KeySet(keySetId, contactId, rotated));
|
||||
db.updateTransportKeys(txn, new KeySet(keySetId1, contactId, rotated1));
|
||||
|
||||
@@ -727,95 +730,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnboundTransportKeys() throws Exception {
|
||||
long rotationPeriod = 123, rotationPeriod1 = 234;
|
||||
TransportKeys keys = createTransportKeys(rotationPeriod);
|
||||
TransportKeys keys1 = createTransportKeys(rotationPeriod1);
|
||||
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Initially there should be no transport keys in the database
|
||||
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
|
||||
|
||||
// Add the contact, the transport and the unbound transport keys
|
||||
db.addLocalAuthor(txn, localAuthor);
|
||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
||||
true, true));
|
||||
db.addTransport(txn, transportId, 123);
|
||||
assertEquals(keySetId, db.addTransportKeys(txn, null, keys));
|
||||
assertEquals(keySetId1, db.addTransportKeys(txn, null, keys1));
|
||||
|
||||
// Retrieve the transport keys
|
||||
Collection<KeySet> allKeys = db.getTransportKeys(txn, transportId);
|
||||
assertEquals(2, allKeys.size());
|
||||
for (KeySet ks : allKeys) {
|
||||
assertNull(ks.getContactId());
|
||||
if (ks.getKeySetId().equals(keySetId)) {
|
||||
assertKeysEquals(keys, ks.getTransportKeys());
|
||||
} else {
|
||||
assertEquals(keySetId1, ks.getKeySetId());
|
||||
assertKeysEquals(keys1, ks.getTransportKeys());
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the first set of transport keys
|
||||
db.bindTransportKeys(txn, contactId, transportId, keySetId);
|
||||
|
||||
// Retrieve the keys again - the first set should be bound
|
||||
allKeys = db.getTransportKeys(txn, transportId);
|
||||
assertEquals(2, allKeys.size());
|
||||
for (KeySet ks : allKeys) {
|
||||
if (ks.getKeySetId().equals(keySetId)) {
|
||||
assertEquals(contactId, ks.getContactId());
|
||||
assertKeysEquals(keys, ks.getTransportKeys());
|
||||
} else {
|
||||
assertEquals(keySetId1, ks.getKeySetId());
|
||||
assertNull(ks.getContactId());
|
||||
assertKeysEquals(keys1, ks.getTransportKeys());
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the transport keys
|
||||
TransportKeys rotated = createTransportKeys(rotationPeriod + 1);
|
||||
TransportKeys rotated1 = createTransportKeys(rotationPeriod1 + 1);
|
||||
db.updateTransportKeys(txn, new KeySet(keySetId, contactId, rotated));
|
||||
db.updateTransportKeys(txn, new KeySet(keySetId1, null, rotated1));
|
||||
|
||||
// Retrieve the transport keys again
|
||||
allKeys = db.getTransportKeys(txn, transportId);
|
||||
assertEquals(2, allKeys.size());
|
||||
for (KeySet ks : allKeys) {
|
||||
if (ks.getKeySetId().equals(keySetId)) {
|
||||
assertEquals(contactId, ks.getContactId());
|
||||
assertKeysEquals(rotated, ks.getTransportKeys());
|
||||
} else {
|
||||
assertEquals(keySetId1, ks.getKeySetId());
|
||||
assertNull(ks.getContactId());
|
||||
assertKeysEquals(rotated1, ks.getTransportKeys());
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the unbound transport keys
|
||||
db.removeTransportKeys(txn, transportId, keySetId1);
|
||||
|
||||
// Retrieve the keys again - the second set should be gone
|
||||
allKeys = db.getTransportKeys(txn, transportId);
|
||||
assertEquals(1, allKeys.size());
|
||||
KeySet ks = allKeys.iterator().next();
|
||||
assertEquals(keySetId, ks.getKeySetId());
|
||||
assertEquals(contactId, ks.getContactId());
|
||||
assertKeysEquals(rotated, ks.getTransportKeys());
|
||||
|
||||
// Removing the transport should remove the remaining transport keys
|
||||
db.removeTransport(txn, transportId);
|
||||
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
private void assertKeysEquals(TransportKeys expected,
|
||||
TransportKeys actual) {
|
||||
assertEquals(expected.getTransportId(), actual.getTransportId());
|
||||
@@ -853,7 +767,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testIncrementStreamCounter() throws Exception {
|
||||
long rotationPeriod = 123;
|
||||
TransportKeys keys = createTransportKeys(rotationPeriod);
|
||||
TransportKeys keys = createTransportKeys(rotationPeriod, true);
|
||||
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
|
||||
|
||||
Database<Connection> db = open(false);
|
||||
@@ -893,8 +807,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testSetReorderingWindow() throws Exception {
|
||||
boolean active = random.nextBoolean();
|
||||
long rotationPeriod = 123;
|
||||
TransportKeys keys = createTransportKeys(rotationPeriod);
|
||||
TransportKeys keys = createTransportKeys(rotationPeriod, active);
|
||||
long base = keys.getCurrentIncomingKeys().getWindowBase();
|
||||
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
|
||||
|
||||
@@ -904,12 +819,12 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Add the contact, transport and transport keys
|
||||
db.addLocalAuthor(txn, localAuthor);
|
||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
||||
true, true));
|
||||
true, active));
|
||||
db.addTransport(txn, transportId, 123);
|
||||
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||
|
||||
// Update the reordering window and retrieve the transport keys
|
||||
new Random().nextBytes(bitmap);
|
||||
random.nextBytes(bitmap);
|
||||
db.setReorderingWindow(txn, keySetId, transportId, rotationPeriod,
|
||||
base + 1, bitmap);
|
||||
Collection<KeySet> newKeys = db.getTransportKeys(txn, transportId);
|
||||
@@ -1908,7 +1823,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
return db;
|
||||
}
|
||||
|
||||
private TransportKeys createTransportKeys(long rotationPeriod) {
|
||||
private TransportKeys createTransportKeys(long rotationPeriod,
|
||||
boolean active) {
|
||||
SecretKey inPrevTagKey = getSecretKey();
|
||||
SecretKey inPrevHeaderKey = getSecretKey();
|
||||
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
|
||||
@@ -1924,7 +1840,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
SecretKey outCurrTagKey = getSecretKey();
|
||||
SecretKey outCurrHeaderKey = getSecretKey();
|
||||
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
|
||||
rotationPeriod, 456, true);
|
||||
rotationPeriod, 456, active);
|
||||
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,10 @@ import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.sync.Ack;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -19,33 +18,27 @@ import java.util.Collections;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
|
||||
public class SimplexOutgoingSessionTest extends BrambleTestCase {
|
||||
public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
|
||||
|
||||
private final Mockery context;
|
||||
private final DatabaseComponent db;
|
||||
private final Executor dbExecutor;
|
||||
private final EventBus eventBus;
|
||||
private final ContactId contactId;
|
||||
private final MessageId messageId;
|
||||
private final int maxLatency;
|
||||
private final SyncRecordWriter recordWriter;
|
||||
private static final int MAX_LATENCY = Integer.MAX_VALUE;
|
||||
|
||||
public SimplexOutgoingSessionTest() {
|
||||
context = new Mockery();
|
||||
db = context.mock(DatabaseComponent.class);
|
||||
dbExecutor = new ImmediateExecutor();
|
||||
eventBus = context.mock(EventBus.class);
|
||||
recordWriter = context.mock(SyncRecordWriter.class);
|
||||
contactId = new ContactId(234);
|
||||
messageId = new MessageId(TestUtils.getRandomId());
|
||||
maxLatency = Integer.MAX_VALUE;
|
||||
}
|
||||
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
private final EventBus eventBus = context.mock(EventBus.class);
|
||||
private final StreamWriter streamWriter = context.mock(StreamWriter.class);
|
||||
private final SyncRecordWriter recordWriter =
|
||||
context.mock(SyncRecordWriter.class);
|
||||
|
||||
private final Executor dbExecutor = new ImmediateExecutor();
|
||||
private final ContactId contactId = new ContactId(234);
|
||||
private final MessageId messageId = new MessageId(getRandomId());
|
||||
|
||||
@Test
|
||||
public void testNothingToSend() throws Exception {
|
||||
SimplexOutgoingSession session = new SimplexOutgoingSession(db,
|
||||
dbExecutor, eventBus, contactId, maxLatency, recordWriter);
|
||||
dbExecutor, eventBus, contactId, MAX_LATENCY, streamWriter,
|
||||
recordWriter);
|
||||
Transaction noAckTxn = new Transaction(null, false);
|
||||
Transaction noMsgTxn = new Transaction(null, false);
|
||||
|
||||
@@ -63,19 +56,17 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
|
||||
oneOf(db).startTransaction(false);
|
||||
will(returnValue(noMsgTxn));
|
||||
oneOf(db).generateBatch(with(noMsgTxn), with(contactId),
|
||||
with(any(int.class)), with(maxLatency));
|
||||
with(any(int.class)), with(MAX_LATENCY));
|
||||
will(returnValue(null));
|
||||
oneOf(db).commitTransaction(noMsgTxn);
|
||||
oneOf(db).endTransaction(noMsgTxn);
|
||||
// Flush the output stream
|
||||
oneOf(recordWriter).flush();
|
||||
// Send the end of stream marker
|
||||
oneOf(streamWriter).sendEndOfStream();
|
||||
// Remove listener
|
||||
oneOf(eventBus).removeListener(session);
|
||||
}});
|
||||
|
||||
session.run();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -83,7 +74,8 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
|
||||
Ack ack = new Ack(Collections.singletonList(messageId));
|
||||
byte[] raw = new byte[1234];
|
||||
SimplexOutgoingSession session = new SimplexOutgoingSession(db,
|
||||
dbExecutor, eventBus, contactId, maxLatency, recordWriter);
|
||||
dbExecutor, eventBus, contactId, MAX_LATENCY, streamWriter,
|
||||
recordWriter);
|
||||
Transaction ackTxn = new Transaction(null, false);
|
||||
Transaction noAckTxn = new Transaction(null, false);
|
||||
Transaction msgTxn = new Transaction(null, false);
|
||||
@@ -104,7 +96,7 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
|
||||
oneOf(db).startTransaction(false);
|
||||
will(returnValue(msgTxn));
|
||||
oneOf(db).generateBatch(with(msgTxn), with(contactId),
|
||||
with(any(int.class)), with(maxLatency));
|
||||
with(any(int.class)), with(MAX_LATENCY));
|
||||
will(returnValue(Arrays.asList(raw)));
|
||||
oneOf(db).commitTransaction(msgTxn);
|
||||
oneOf(db).endTransaction(msgTxn);
|
||||
@@ -120,18 +112,16 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
|
||||
oneOf(db).startTransaction(false);
|
||||
will(returnValue(noMsgTxn));
|
||||
oneOf(db).generateBatch(with(noMsgTxn), with(contactId),
|
||||
with(any(int.class)), with(maxLatency));
|
||||
with(any(int.class)), with(MAX_LATENCY));
|
||||
will(returnValue(null));
|
||||
oneOf(db).commitTransaction(noMsgTxn);
|
||||
oneOf(db).endTransaction(noMsgTxn);
|
||||
// Flush the output stream
|
||||
oneOf(recordWriter).flush();
|
||||
// Send the end of stream marker
|
||||
oneOf(streamWriter).sendEndOfStream();
|
||||
// Remove listener
|
||||
oneOf(eventBus).removeListener(session);
|
||||
}});
|
||||
|
||||
session.run();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
||||
import org.briarproject.bramble.api.sync.SyncRecordWriterFactory;
|
||||
import org.briarproject.bramble.api.transport.StreamContext;
|
||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
@@ -27,7 +28,6 @@ import org.junit.Test;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -102,18 +102,18 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
|
||||
headerKey, streamNumber);
|
||||
OutputStream streamWriter = streamWriterFactory.createStreamWriter(out,
|
||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out,
|
||||
ctx);
|
||||
SyncRecordWriter recordWriter = recordWriterFactory.createRecordWriter(
|
||||
streamWriter);
|
||||
streamWriter.getOutputStream());
|
||||
|
||||
recordWriter.writeAck(new Ack(messageIds));
|
||||
recordWriter.writeMessage(message.getRaw());
|
||||
recordWriter.writeMessage(message1.getRaw());
|
||||
recordWriter.writeOffer(new Offer(messageIds));
|
||||
recordWriter.writeRequest(new Request(messageIds));
|
||||
recordWriter.flush();
|
||||
|
||||
streamWriter.sendEndOfStream();
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
@@ -54,6 +55,7 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
||||
new StreamContext(contactId, transportId, getSecretKey(),
|
||||
getSecretKey(), 1);
|
||||
private final byte[] tag = getRandomBytes(TAG_LENGTH);
|
||||
private final Random random = new Random();
|
||||
|
||||
private final KeyManagerImpl keyManager = new KeyManagerImpl(db, executor,
|
||||
pluginConfig, transportKeyManagerFactory);
|
||||
@@ -102,30 +104,18 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
||||
public void testAddContact() throws Exception {
|
||||
SecretKey secretKey = getSecretKey();
|
||||
long timestamp = System.currentTimeMillis();
|
||||
boolean alice = new Random().nextBoolean();
|
||||
boolean alice = random.nextBoolean();
|
||||
boolean active = random.nextBoolean();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportKeyManager).addContact(txn, contactId, secretKey,
|
||||
timestamp, alice);
|
||||
}});
|
||||
|
||||
keyManager.addContact(txn, contactId, secretKey, timestamp, alice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddUnboundKeys() throws Exception {
|
||||
SecretKey secretKey = getSecretKey();
|
||||
long timestamp = System.currentTimeMillis();
|
||||
boolean alice = new Random().nextBoolean();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportKeyManager).addUnboundKeys(txn, secretKey,
|
||||
timestamp, alice);
|
||||
timestamp, alice, active);
|
||||
will(returnValue(keySetId));
|
||||
}});
|
||||
|
||||
assertEquals(singletonMap(transportId, keySetId),
|
||||
keyManager.addUnboundKeys(txn, secretKey, timestamp, alice));
|
||||
Map<TransportId, KeySetId> ids = keyManager.addContact(txn, contactId,
|
||||
secretKey, timestamp, alice, active);
|
||||
assertEquals(singletonMap(transportId, keySetId), ids);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -61,7 +61,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
private final ContactId contactId1 = new ContactId(234);
|
||||
private final KeySetId keySetId = new KeySetId(345);
|
||||
private final KeySetId keySetId1 = new KeySetId(456);
|
||||
private final KeySetId keySetId2 = new KeySetId(567);
|
||||
private final SecretKey tagKey = TestUtils.getSecretKey();
|
||||
private final SecretKey headerKey = TestUtils.getSecretKey();
|
||||
private final SecretKey masterKey = TestUtils.getSecretKey();
|
||||
@@ -71,14 +70,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
public void testKeysAreRotatedAtStartup() throws Exception {
|
||||
TransportKeys shouldRotate = createTransportKeys(900, 0, true);
|
||||
TransportKeys shouldNotRotate = createTransportKeys(1000, 0, true);
|
||||
TransportKeys shouldRotate1 = createTransportKeys(999, 0, false);
|
||||
Collection<KeySet> loaded = asList(
|
||||
new KeySet(keySetId, contactId, shouldRotate),
|
||||
new KeySet(keySetId1, contactId1, shouldNotRotate),
|
||||
new KeySet(keySetId2, null, shouldRotate1)
|
||||
new KeySet(keySetId1, contactId1, shouldNotRotate)
|
||||
);
|
||||
TransportKeys rotated = createTransportKeys(1000, 0, true);
|
||||
TransportKeys rotated1 = createTransportKeys(1000, 0, false);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
@@ -93,8 +89,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(rotated));
|
||||
oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
|
||||
will(returnValue(shouldNotRotate));
|
||||
oneOf(transportCrypto).rotateTransportKeys(shouldRotate1, 1000);
|
||||
will(returnValue(rotated1));
|
||||
// Encode the tags (3 sets per contact)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(6).of(transportCrypto).encodeTag(
|
||||
@@ -103,10 +97,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Save the keys that were rotated
|
||||
oneOf(db).updateTransportKeys(txn, asList(
|
||||
new KeySet(keySetId, contactId, rotated),
|
||||
new KeySet(keySetId2, null, rotated1))
|
||||
);
|
||||
oneOf(db).updateTransportKeys(txn,
|
||||
singletonList(new KeySet(keySetId, contactId, rotated)));
|
||||
// Schedule key rotation at the start of the next rotation period
|
||||
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
||||
with(rotationPeriodLength - 1), with(MILLISECONDS));
|
||||
@@ -153,43 +145,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
maxLatency);
|
||||
// The timestamp is 1 ms before the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000 - 1;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
||||
masterKey, timestamp, alice, true));
|
||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeysAreRotatedWhenAddingUnboundKeys() throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
TransportKeys transportKeys = createTransportKeys(999, 0, false);
|
||||
TransportKeys rotated = createTransportKeys(1000, 0, false);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
999, alice, false);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (1 ms after start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000 + 1));
|
||||
// Rotate the transport keys
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(rotated));
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, null, rotated);
|
||||
will(returnValue(keySetId));
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
// The timestamp is 1 ms before the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000 - 1;
|
||||
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
|
||||
masterKey, timestamp, alice));
|
||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
||||
throws Exception {
|
||||
@@ -211,15 +171,15 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
MAX_32_BIT_UNSIGNED + 1, true);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
expectAddContactNoRotation(alice, transportKeys, txn);
|
||||
expectAddContactNoRotation(alice, true, transportKeys, txn);
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
||||
masterKey, timestamp, alice, true));
|
||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
}
|
||||
@@ -232,7 +192,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
MAX_32_BIT_UNSIGNED, true);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
expectAddContactNoRotation(alice, transportKeys, txn);
|
||||
expectAddContactNoRotation(alice, true, transportKeys, txn);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// Increment the stream counter
|
||||
@@ -244,8 +204,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
||||
masterKey, timestamp, alice, true));
|
||||
// The first request should return a stream context
|
||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
StreamContext ctx = transportKeyManager.getStreamContext(txn,
|
||||
@@ -265,19 +225,21 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
public void testIncomingStreamContextIsNullIfTagIsNotFound()
|
||||
throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
TransportKeys transportKeys = createTransportKeys(1000, 0, true);
|
||||
boolean active = random.nextBoolean();
|
||||
TransportKeys transportKeys = createTransportKeys(1000, 0, active);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
expectAddContactNoRotation(alice, transportKeys, txn);
|
||||
expectAddContactNoRotation(alice, active, transportKeys, txn);
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
||||
masterKey, timestamp, alice, active));
|
||||
assertEquals(active,
|
||||
transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
// The tag should not be recognised
|
||||
assertNull(transportKeyManager.getStreamContext(txn,
|
||||
new byte[TAG_LENGTH]));
|
||||
@@ -327,8 +289,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
||||
masterKey, timestamp, alice, true));
|
||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
// Use the first tag (previous rotation period, stream number 0)
|
||||
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
|
||||
@@ -415,23 +377,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindingAndActivatingKeys() throws Exception {
|
||||
public void testActivatingKeys() throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
TransportKeys transportKeys = createTransportKeys(1000, 0, false);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
expectAddUnboundKeysNoRotation(alice, transportKeys, txn);
|
||||
expectAddContactNoRotation(alice, false, transportKeys, txn);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// When the keys are bound, encode the tags (3 sets)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(3).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Save the key binding
|
||||
oneOf(db).bindTransportKeys(txn, contactId, transportId, keySetId);
|
||||
// Activate the keys
|
||||
oneOf(db).setTransportKeysActive(txn, transportId, keySetId);
|
||||
// Increment the stream counter
|
||||
@@ -443,12 +396,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
|
||||
masterKey, timestamp, alice));
|
||||
// The keys are unbound so no stream context should be returned
|
||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
transportKeyManager.bindKeys(txn, contactId, keySetId);
|
||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
||||
masterKey, timestamp, alice, false));
|
||||
// The keys are inactive so no stream context should be returned
|
||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
@@ -474,18 +423,26 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
// Keep a copy of the tags
|
||||
List<byte[]> tags = new ArrayList<>();
|
||||
|
||||
expectAddUnboundKeysNoRotation(alice, transportKeys, txn);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// When the keys are bound, encode the tags (3 sets)
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice, false);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000));
|
||||
// Encode the tags (3 sets)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(3).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction(tags));
|
||||
}
|
||||
// Save the key binding
|
||||
oneOf(db).bindTransportKeys(txn, contactId, transportId, keySetId);
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||
will(returnValue(keySetId));
|
||||
// Encode a new tag after sliding the window
|
||||
oneOf(transportCrypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION),
|
||||
@@ -505,9 +462,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
|
||||
masterKey, timestamp, alice));
|
||||
transportKeyManager.bindKeys(txn, contactId, keySetId);
|
||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
||||
masterKey, timestamp, alice, false));
|
||||
// The keys are inactive so no stream context should be returned
|
||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
@@ -532,36 +488,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
assertEquals(0L, ctx.getStreamNumber());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovingUnboundKeys() throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
TransportKeys transportKeys = createTransportKeys(1000, 0, false);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
expectAddUnboundKeysNoRotation(alice, transportKeys, txn);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// Remove the unbound keys
|
||||
oneOf(db).removeTransportKeys(txn, transportId, keySetId);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
|
||||
masterKey, timestamp, alice));
|
||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
transportKeyManager.removeKeys(txn, keySetId);
|
||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||
}
|
||||
|
||||
private void expectAddContactNoRotation(boolean alice,
|
||||
private void expectAddContactNoRotation(boolean alice, boolean active,
|
||||
TransportKeys transportKeys, Transaction txn) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice, true);
|
||||
1000, alice, active);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
@@ -582,24 +513,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectAddUnboundKeysNoRotation(boolean alice,
|
||||
TransportKeys transportKeys, Transaction txn) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice, false);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000));
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Save the unbound keys
|
||||
oneOf(db).addTransportKeys(txn, null, transportKeys);
|
||||
will(returnValue(keySetId));
|
||||
}});
|
||||
}
|
||||
|
||||
private TransportKeys createTransportKeys(long rotationPeriod,
|
||||
long streamCounter, boolean active) {
|
||||
IncomingKeys inPrev = new IncomingKeys(tagKey, headerKey,
|
||||
|
||||
@@ -27,22 +27,17 @@ dependencies {
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
||||
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
||||
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||
'net.java.dev.jna:jna-platform:4.4.0:jna-platform-4.4.0.jar:e9dda9e884fc107eb6367710540789a12dfa8ad28be9326b22ca6e352e325499',
|
||||
'net.java.dev.jna:jna:4.4.0:jna-4.4.0.jar:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
|
||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'witness'
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy.preferProjectModules()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':briar-core', configuration: 'default')
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
implementation project(path: ':bramble-android', configuration: 'default')
|
||||
implementation project(':bramble-android')
|
||||
|
||||
def supportVersion = '27.0.1'
|
||||
def supportVersion = '27.1.1'
|
||||
implementation "com.android.support:support-v4:$supportVersion"
|
||||
implementation("com.android.support:appcompat-v7:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
@@ -24,7 +20,7 @@ dependencies {
|
||||
}
|
||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||
implementation "com.android.support:support-annotations:$supportVersion"
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.0-beta3'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
|
||||
|
||||
implementation('ch.acra:acra:4.8.5') {
|
||||
exclude module: 'support-v4'
|
||||
@@ -32,11 +28,11 @@ dependencies {
|
||||
}
|
||||
implementation 'info.guardianproject.panic:panic:0.5'
|
||||
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||
implementation 'de.hdodenhof:circleimageview:2.1.0'
|
||||
implementation 'de.hdodenhof:circleimageview:2.2.0'
|
||||
implementation 'com.google.zxing:core:3.3.0'
|
||||
implementation 'com.jpardogo.materialtabstrip:library:1.1.0'
|
||||
implementation 'com.github.bumptech.glide:glide:3.8.0'
|
||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.1.0'
|
||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0'
|
||||
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
|
||||
@@ -57,64 +53,107 @@ dependencies {
|
||||
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'android.arch.core:common:1.0.0:common-1.0.0.jar:5192934cd73df32e2c15722ed7fc488dde90baaec9ae030010dd1a80fb4e74e1',
|
||||
'android.arch.lifecycle:common:1.0.0:common-1.0.0.jar:86bf301a20ad0cd0a391e22a52e6fbf90575c096ff83233fa9fd0d52b3219121',
|
||||
'android.arch.lifecycle:runtime:1.0.0:runtime-1.0.0.aar:e4e34e5d02bd102e8d39ddbc29f9ead8a15a61e367993d02238196ac48509ad8',
|
||||
'android.arch.core:common:1.1.0:common-1.1.0.jar:d34824b794bc92ff8f647a9bb13a7c73de920de5b47075b5d2c4f0770e9b8bfd',
|
||||
'android.arch.core:runtime:1.1.0:runtime-1.1.0.aar:83400f7575bcfb8a2eeec64e05590f037bfaed1e56aa3a4214d20e55878445e3',
|
||||
'android.arch.lifecycle:common:1.1.0:common-1.1.0.jar:614e31cfd33255dc4d5f5d8e62cfa6be2fbbc2a35643a79dc3ed008004c30807',
|
||||
'android.arch.lifecycle:livedata-core:1.1.0:livedata-core-1.1.0.aar:14e57ff8ffb65a80c7e72d91f2076acccdaf2970f234c6261e03a6127eb5206b',
|
||||
'android.arch.lifecycle:runtime:1.1.0:runtime-1.1.0.aar:094fd793924dd6a5136753e599ac8174a8147f4a401386b694ba7d818c223e2e',
|
||||
'android.arch.lifecycle:viewmodel:1.1.0:viewmodel-1.1.0.aar:6407c93a5ea9850661dca42a0068d6f3deccefd7228ee69bae1c35d70cbc2557',
|
||||
'backport-util-concurrent:backport-util-concurrent:3.1:backport-util-concurrent-3.1.jar:f5759b7fcdfc83a525a036deedcbd32e5b536b625ebc282426f16ca137eb5902',
|
||||
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
||||
'ch.acra:acra:4.8.5:acra-4.8.5.aar:afd5b28934d5166b55f261c85685ad59e8a4ebe9ca1960906afaa8c76d8dc9eb',
|
||||
'classworlds:classworlds:1.1-alpha-2:classworlds-1.1-alpha-2.jar:2bf4e59f3acd106fea6145a9a88fe8956509f8b9c0fdd11eb96fee757269e3f3',
|
||||
'com.almworks.sqlite4java:sqlite4java:0.282:sqlite4java-0.282.jar:9e1d8dd83ca6003f841e3af878ce2dc7c22497493a7bb6d1b62ec1b0d0a83c05',
|
||||
'com.android.support.constraint:constraint-layout-solver:1.1.0-beta3:constraint-layout-solver-1.1.0-beta3.jar:c9084108415046c423983bdff8cf04c8e9a5bed41b8d5329f3764c08312ee3dd',
|
||||
'com.android.support.constraint:constraint-layout:1.1.0-beta3:constraint-layout-1.1.0-beta3.aar:1754a6bd135feae485aa2ebf9e170f0f3d3282b392f8aa3067d0ed668839db79',
|
||||
'com.android.support:animated-vector-drawable:27.0.1:animated-vector-drawable-27.0.1.aar:365050110411c86c7eec86101b49ab53557ffe6667f60b19055f1d35c38a577b',
|
||||
'com.android.support:appcompat-v7:27.0.1:appcompat-v7-27.0.1.aar:1402c29a49db30346c21a7d40634461765b3ab826f5dd95bc4dcc76787b21851',
|
||||
'com.android.support:cardview-v7:27.0.1:cardview-v7-27.0.1.aar:43fccd44086c51eaa9d78be2fcf0dfea1556c8876a6fd325ea8d24e860054202',
|
||||
'com.android.support:design:27.0.1:design-27.0.1.aar:0e7c694bd5385e82d193e33b7289f769ce6ff05e277443daf5bffc3d852ef448',
|
||||
'com.android.support:preference-v14:27.0.1:preference-v14-27.0.1.aar:beed3ee97ae0d2541bd0adac6c84bb7d7b7580399f24e8efb9bc624161b164df',
|
||||
'com.android.support:preference-v7:27.0.1:preference-v7-27.0.1.aar:d87ba6ae7c834fd68ef09cbc0f064975627ba14189dbef639c34080691366da9',
|
||||
'com.android.support:recyclerview-v7:27.0.1:recyclerview-v7-27.0.1.aar:99ae2863287df800f239617d09cafc58b95f7a5365db32e3761394fa01ab12c9',
|
||||
'com.android.support:support-annotations:27.0.1:support-annotations-27.0.1.jar:240912b779fe7413bdd97c94ac2a92d1fb29bd53c4d50c6982e5942b01278ac0',
|
||||
'com.android.support:support-compat:27.0.1:support-compat-27.0.1.aar:70dfd92561421e5ae113dfc4045fc94c90caca9912839b3d08c793aa391aedcc',
|
||||
'com.android.support:support-core-ui:27.0.1:support-core-ui-27.0.1.aar:91614f829f8a6f1fc2d28efc58ea2735212225ab02cdf768b3dd9af72371d5f0',
|
||||
'com.android.support:support-core-utils:27.0.1:support-core-utils-27.0.1.aar:b33c5a5d67b8bfa6c8829875661779279157bf23e6042179820eaf1745034a72',
|
||||
'com.android.support:support-fragment:27.0.1:support-fragment-27.0.1.aar:05a859783ae4a285c6d5c9865e1242b86f5bfb69a30455ca6c8495f24c09d9ed',
|
||||
'com.android.support:support-media-compat:27.0.1:support-media-compat-27.0.1.aar:bc1c0252c1d071a1676fd280a792cf0717833fb3afd0ef73070ddc51ef921ad7',
|
||||
'com.android.support:support-v4:27.0.1:support-v4-27.0.1.aar:afd802f1926475f40c5b6b42855c9a626a854298908dafdb455deb6dab2640f2',
|
||||
'com.android.support:support-vector-drawable:27.0.1:support-vector-drawable-27.0.1.aar:67154ea191f735496502214bc6da6195738829a8e9d59e8042a1e030a2c7f19c',
|
||||
'com.android.support:transition:27.0.1:transition-27.0.1.aar:8a46d152391bd1598d1778ae889b3aeaff77a17d0caaa29446df6e26a28c4e4b',
|
||||
'com.android.support.constraint:constraint-layout-solver:1.1.0:constraint-layout-solver-1.1.0.jar:fcb4c7d705754ca3d69b1b2c3caf445a425599fda8caabbcf855d98ea0663e4e',
|
||||
'com.android.support.constraint:constraint-layout:1.1.0:constraint-layout-1.1.0.aar:d490188709b7bb2f11609beadd7e5eb7538892f308828ec3ff261a74e6ecf47e',
|
||||
'com.android.support:animated-vector-drawable:27.1.1:animated-vector-drawable-27.1.1.aar:59670473f6e98fda792f7bef25dd7292b0a3106031c7a5e30eb020bf26f077bd',
|
||||
'com.android.support:appcompat-v7:27.1.1:appcompat-v7-27.1.1.aar:0c7808fbbc5838d831e32e3c0a6f84e1f2c981deb8f11e010650f2b57923a335',
|
||||
'com.android.support:cardview-v7:27.1.1:cardview-v7-27.1.1.aar:8ed955dd037d82a7b4bbcaedb4f896523c3e4c1bf3ca698ce807c350767a2886',
|
||||
'com.android.support:design:27.1.1:design-27.1.1.aar:7225973f7ee03765008a9c2f17a40b154c6885169fef022276e811c926a2202c',
|
||||
'com.android.support:preference-v14:27.1.1:preference-v14-27.1.1.aar:dc058932e6fd93bf57d23fcc2f22351c21e5125e513721e4b0b03374341ad400',
|
||||
'com.android.support:preference-v7:27.1.1:preference-v7-27.1.1.aar:a1798a826b4097d00e49280f412b21af08f9bf1179c2e3838dc339d9f843416d',
|
||||
'com.android.support:recyclerview-v7:27.1.1:recyclerview-v7-27.1.1.aar:d735e4727878e99ef3980c10d15dc3468462fd509d4fb60cb8bd20b0f735085c',
|
||||
'com.android.support:support-annotations:27.1.1:support-annotations-27.1.1.jar:3365960206c3d2b09e845f555e7f88f8effc8d2f00b369e66c4be384029299cf',
|
||||
'com.android.support:support-compat:27.1.1:support-compat-27.1.1.aar:880ce01ff5be42b233ff8ec0c61cefb7dc3dc9500fea9e24423214813ac27ea2',
|
||||
'com.android.support:support-core-ui:27.1.1:support-core-ui-27.1.1.aar:a3ae20e6d5dffba69ac97b99846d2738003af8563843d5f3c9dc4c35b4804241',
|
||||
'com.android.support:support-core-utils:27.1.1:support-core-utils-27.1.1.aar:61036832c54e8701aae954fc3bf96d1d80bf8d9dd531bff77d72def456ba087a',
|
||||
'com.android.support:support-fragment:27.1.1:support-fragment-27.1.1.aar:ec72d6ac36a1a0e6523bbddba33d73ffad070b9b3dd246cc44d8727a41ddb5e6',
|
||||
'com.android.support:support-media-compat:27.1.1:support-media-compat-27.1.1.aar:55e9837dda88b74a8c812c63a78c63fd83c6c039a8c22d318492663a493585eb',
|
||||
'com.android.support:support-v4:27.1.1:support-v4-27.1.1.aar:4f41dfc3e89f2738e45c86264a85c0934d055ee8ebe2020e23c97f303b80a48b',
|
||||
'com.android.support:support-vector-drawable:27.1.1:support-vector-drawable-27.1.1.aar:1c0f421114cf4627cf208776d6eb4f76340c78b7e96fe6e12b3e6eb950caf1b9',
|
||||
'com.android.support:transition:27.1.1:transition-27.1.1.aar:c0765b2f3c78696567ec5b3f519d22da1e3df11ac994625adf4bb4dc571caacc',
|
||||
'com.android.tools.analytics-library:protos:26.1.2:protos-26.1.2.jar:52672a0b42b572a06aecc3535d5068eb46c0e15d129b9f1085d3c16a1da5cdbb',
|
||||
'com.android.tools.analytics-library:shared:26.1.2:shared-26.1.2.jar:5c7e0eda18c6f87feeb83628c707e8aaa3298b41fb72e38efe31ad1675f9e8e9',
|
||||
'com.android.tools.analytics-library:tracker:26.1.2:tracker-26.1.2.jar:06f97aa0adf44ffb06f8681c6a79d9be153a08f61d21eddc42b8d3db96df4282',
|
||||
'com.android.tools.build:apksig:3.1.2:apksig-3.1.2.jar:40696a4559124d1d57873d208857eee059d48859239d569c7d18374ac644a8be',
|
||||
'com.android.tools.build:builder-model:3.1.2:builder-model-3.1.2.jar:d49bfa2a135c9562b6ca7aa4342036cfa1582c7074c2d1d93d1dae8b3a134e17',
|
||||
'com.android.tools.build:builder-test-api:3.1.2:builder-test-api-3.1.2.jar:dfe2a50b740d41b11189101062434d4283d18647e89a492ad51710c719363e9f',
|
||||
'com.android.tools.build:builder:3.1.2:builder-3.1.2.jar:b60f825a42e2efe8433619fbc759f3d9effecab718279048d36881188ceb1d14',
|
||||
'com.android.tools.build:gradle-api:3.1.2:gradle-api-3.1.2.jar:e58bcc5b893e4583ab0f5c8ef89c4dbcce202b405a9d7fcc116d21e5357d4893',
|
||||
'com.android.tools.build:manifest-merger:26.1.2:manifest-merger-26.1.2.jar:9c61c27ea5266573107b954acf1216d398f4d7e7ae6fad6409d6b2b767eb091c',
|
||||
'com.android.tools.ddms:ddmlib:26.1.2:ddmlib-26.1.2.jar:18a2a5fbef36882f07d03c2b9e59eba05cf8248177bf5cbff736e4b582804c44',
|
||||
'com.android.tools.external.com-intellij:intellij-core:26.1.2:intellij-core-26.1.2.jar:37c5acf279f1ae3e85b1a5be3c9f15f43bde7b08f978eefefffb9c4035760c52',
|
||||
'com.android.tools.external.com-intellij:kotlin-compiler:26.1.2:kotlin-compiler-26.1.2.jar:152df0bee7580326c77316b669a9d96e3b09efb1d45f545dce4147271b0b8944',
|
||||
'com.android.tools.external.org-jetbrains:uast:26.1.2:uast-26.1.2.jar:02d39582206d3f5fc0a6cb18bfd9e8b9f9c1acb805ec6dac08b4e3a56849d279',
|
||||
'com.android.tools.layoutlib:layoutlib-api:26.1.2:layoutlib-api-26.1.2.jar:20220039fcc7d799f928153beff862e704457c0f55ab44258f3745ebeb662b4f',
|
||||
'com.android.tools.lint:lint-api:26.1.2:lint-api-26.1.2.jar:e1d5b62b870a7c566e9877a6b96b27784a4d713f8caa07fdcb4705d47a40a1d9',
|
||||
'com.android.tools.lint:lint-checks:26.1.2:lint-checks-26.1.2.jar:211e2afd58504372385d71b1e5be982c2b5121ab6fee1c04ddabeb75a8729e07',
|
||||
'com.android.tools.lint:lint-gradle-api:26.1.2:lint-gradle-api-26.1.2.jar:71284f2a8b03c3e55c94511c9eb36f8184fbb85324325fc6b78abf5183f03d90',
|
||||
'com.android.tools.lint:lint-gradle:26.1.2:lint-gradle-26.1.2.jar:855f0c82b7fc690df1b7319c0774f7517f7f8f5dd4eee1f6077dcf50e07c6240',
|
||||
'com.android.tools.lint:lint-kotlin:26.1.2:lint-kotlin-26.1.2.jar:1e591f70bcbbc11569720a9bbcca2bc1f3d4f789f01f40f642848d920643d484',
|
||||
'com.android.tools.lint:lint:26.1.2:lint-26.1.2.jar:93736c62e9f1976998c2b4aa716aea0734cdb162d05502f4af7292654aedb182',
|
||||
'com.android.tools:annotations:26.1.2:annotations-26.1.2.jar:72773dcaf5c4ccca828e3c8467f1b78a8a00b3cc5f8ad1aab88fcf9379928018',
|
||||
'com.android.tools:common:26.1.2:common-26.1.2.jar:ea4320f0c17dcbc4491896bb705c4d25ec08bd62ef02ab0579fe154e75e788e6',
|
||||
'com.android.tools:dvlib:26.1.2:dvlib-26.1.2.jar:1187aa4fb666595c96c4deb6bc0e0f4b7e396bde9f6243330b49a232946130ea',
|
||||
'com.android.tools:repository:26.1.2:repository-26.1.2.jar:8b86e512ad6d32bd76989451eefe2b271f5efce6d4d65ecb173afaf14606e01a',
|
||||
'com.android.tools:sdk-common:26.1.2:sdk-common-26.1.2.jar:23584720a60a21cdcb5b1ec10269e3013789d6805d153cc696c39ec7ce251896',
|
||||
'com.android.tools:sdklib:26.1.2:sdklib-26.1.2.jar:d3870fafc59ab8efa70d3f9649f40ee299c8ec5b58377b06e8853d7272a5bf4e',
|
||||
'com.github.bumptech.glide:glide:3.8.0:glide-3.8.0.jar:750d9e7b940dc0ee48f8680623b55d46e14e8727acc922d7b156e57e7c549655',
|
||||
'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.1:accessibility-test-framework-2.1.jar:7b0aa6ed7553597ce0610684a9f7eca8021eee218f2e2f427c04a7fbf5f920bd',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
|
||||
'com.google.code.gson:gson:2.7:gson-2.7.jar:2d43eb5ea9e133d2ee2405cc14f5ee08951b8361302fdd93494a3a997b508d32',
|
||||
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
||||
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
||||
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||
'com.google.errorprone:error_prone_annotations:2.0.18:error_prone_annotations-2.0.18.jar:cb4cfad870bf563a07199f3ebea5763f0dec440fcda0b318640b1feaa788656b',
|
||||
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||
'com.google.guava:guava:20.0:guava-20.0.jar:36a666e3b71ae7f0f0dca23654b67e086e6c93d192f60ba5dfd5519db6c288c8',
|
||||
'com.google.guava:guava:22.0:guava-22.0.jar:1158e94c7de4da480873f0b4ab4a1da14c0d23d4b1902cc94a58a6f0f9ab579e',
|
||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
|
||||
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
|
||||
'com.google.protobuf:protobuf-java:2.6.1:protobuf-java-2.6.1.jar:55aa554843983f431df5616112cf688d38aa17c132357afd1c109435bfdac4e6',
|
||||
'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
|
||||
'com.google.zxing:core:3.3.0:core-3.3.0.jar:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
|
||||
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
|
||||
'com.ibm.icu:icu4j:53.1:icu4j-53.1.jar:e37a4467bac5cdeb02c5c4b8e5063d2f4e67b69e3c7df6d6b610f13185572bab',
|
||||
'com.jpardogo.materialtabstrip:library:1.1.0:library-1.1.0.aar:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
|
||||
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
||||
'com.rometools:rome-utils:1.7.3:rome-utils-1.7.3.jar:f774a80e7e1bc9db5426b62333ab362537f901636cd0812dc84cb5ee2668acf9',
|
||||
'com.rometools:rome:1.7.3:rome-1.7.3.jar:077367bf8fa01b211c9da712c2aa862724195773c5693fd4d97d61d6457e96c6',
|
||||
'com.squareup.okhttp3:okhttp:3.8.0:okhttp-3.8.0.jar:19e1db51787716ff0046fa19e408fb34ed32a6274baa0c07475bf724b4eb6800',
|
||||
'com.squareup.okio:okio:1.13.0:okio-1.13.0.jar:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850',
|
||||
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
|
||||
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
|
||||
'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4',
|
||||
'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
|
||||
'com.thoughtworks.xstream:xstream:1.4.8:xstream-1.4.8.jar:a219a1abf948400b669d08be73a6f9209fb720d237e5ff74c223bffe5f9df93d',
|
||||
'de.hdodenhof:circleimageview:2.1.0:circleimageview-2.1.0.aar:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503',
|
||||
'commons-codec:commons-codec:1.6:commons-codec-1.6.jar:54b34e941b8e1414bd3e40d736efd3481772dc26db3296f6aa45cec9f6203d86',
|
||||
'commons-logging:commons-logging:1.1.1:commons-logging-1.1.1.jar:ce6f913cad1f0db3aad70186d65c5bc7ffcc9a99e3fe8e0b137312819f7c362f',
|
||||
'de.hdodenhof:circleimageview:2.2.0:circleimageview-2.2.0.aar:bc34761dcd5036229ac1ffed6b5b1bef722ad5b097a2c8bba1c5ed2cd4b5c82b',
|
||||
'info.guardianproject.panic:panic:0.5:panic-0.5.jar:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
|
||||
'info.guardianproject.trustedintents:trustedintents:0.2:trustedintents-0.2.jar:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
|
||||
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
|
||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
|
||||
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||
'nekohtml:nekohtml:1.9.6.2:nekohtml-1.9.6.2.jar:fdff6cfa9ed9cc911c842a5d2395f209ec621ef1239d46810e9e495809d3ae09',
|
||||
'nekohtml:xercesMinimal:1.9.6.2:xercesMinimal-1.9.6.2.jar:95b8b357d19f63797dd7d67622fd3f18374d64acbc6584faba1c7759a31e8438',
|
||||
'net.bytebuddy:byte-buddy-agent:1.6.14:byte-buddy-agent-1.6.14.jar:c141a2d6809c3eeff4a43d25992826abccebdd4b793af3e7a5f346e88ae73a33',
|
||||
'net.bytebuddy:byte-buddy:1.6.14:byte-buddy-1.6.14.jar:917758b3c651e278a15a029ba1d42dbf802d8b0e1fe2aa4b81c5750c64f461c1',
|
||||
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
|
||||
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
|
||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
|
||||
'org.apache.httpcomponents:httpclient:4.2.6:httpclient-4.2.6.jar:362e9324ee7c697e21279e20077b52737ddef3f1b2c1a7abe5ad34b465145550',
|
||||
'org.apache.httpcomponents:httpcore:4.2.5:httpcore-4.2.5.jar:e5e82da4cc66c8d917bbf743e3c0752efe8522735e7fc9dbddb65bccea81cfe9',
|
||||
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
|
||||
'org.apache.maven.wagon:wagon-file:1.0-beta-6:wagon-file-1.0-beta-6.jar:7298feeb36ff14dd933c38e62585fb9973fea32fb3c4bc5379428cb1aac5dd3c',
|
||||
'org.apache.maven.wagon:wagon-http-lightweight:1.0-beta-6:wagon-http-lightweight-1.0-beta-6.jar:be214032de23c6b520b79c1ccdb160948e0c67ed7c11984b7ec4ca5537867b4e',
|
||||
'org.apache.maven.wagon:wagon-http-shared:1.0-beta-6:wagon-http-shared-1.0-beta-6.jar:f095c882716d49269a806685dcb256fa6a36389b2713ac56bb758bf8693565a2',
|
||||
@@ -130,30 +169,40 @@ dependencyVerification {
|
||||
'org.apache.maven:maven-repository-metadata:2.2.1:maven-repository-metadata-2.2.1.jar:5fe283f47b0e7f7d95a4252af3fa7a0db4d8f080cd9df308608c0472b8f168a1',
|
||||
'org.apache.maven:maven-settings:2.2.1:maven-settings-2.2.1.jar:9a9f556713a404e770c9dbdaed7eb086078014c989291960c76fdde6db4192f7',
|
||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||
'org.bouncycastle:bcprov-jdk15on:1.52:bcprov-jdk15on-1.52.jar:0dc4d181e4d347893c2ddbd2e6cd5d7287fc651c03648fa64b2341c7366b1773',
|
||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||
'org.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
|
||||
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
|
||||
'org.codehaus.plexus:plexus-container-default:1.0-alpha-9-stable-1:plexus-container-default-1.0-alpha-9-stable-1.jar:7c758612888782ccfe376823aee7cdcc7e0cdafb097f7ef50295a0b0c3a16edf',
|
||||
'org.codehaus.plexus:plexus-interpolation:1.11:plexus-interpolation-1.11.jar:fd9507feb858fa620d1b4aa4b7039fdea1a77e09d3fd28cfbddfff468d9d8c28',
|
||||
'org.codehaus.plexus:plexus-utils:1.5.15:plexus-utils-1.5.15.jar:2ca121831e597b4d8f2cb22d17c5c041fc23a7777ceb6bfbdd4dfb34bbe7d997',
|
||||
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
|
||||
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
|
||||
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
|
||||
'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.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128-runtime.jar:e357a0f1d573c2f702a273992b1b6cb661734f66311854efb3778a888515c5b5',
|
||||
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128.jar:47b4bec6df11a1118da3953da8b9fa1e7079d6fec857faa1a3cf912e53a6fd4e',
|
||||
'org.jacoco:org.jacoco.ant:0.7.4.201502262128:org.jacoco.ant-0.7.4.201502262128.jar:013ce2a68ba57a3c59215ae0dec4df3498c078062a38c3b94c841fc14450f283',
|
||||
'org.jacoco:org.jacoco.core:0.7.4.201502262128:org.jacoco.core-0.7.4.201502262128.jar:ec4c74554312fac5116350164786f91b35c9e082fa4ea598bfa42b5db05d7abb',
|
||||
'org.jacoco:org.jacoco.report:0.7.4.201502262128:org.jacoco.report-0.7.4.201502262128.jar:7a3554c605e088e7e323b1084656243f0444fa353e2f2dee1f1a4204eb64ff09',
|
||||
'org.jdom:jdom2:2.0.6:jdom2-2.0.6.jar:1345f11ba606d15603d6740551a8c21947c0215640770ec67271fe78bea97cf5',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0:kotlin-stdlib-jre7-1.2.0.jar:c7a20fb951d437797afe8980aff6c1e5a03f310c661ba58ba1d4fa90cb0f2926',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
|
||||
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
|
||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
|
||||
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||
'org.jsoup:jsoup:1.10.3:jsoup-1.10.3.jar:a0784e793d7f518eb1defb47b428da011cd483c5da32d49c569bf491e4f1579a',
|
||||
'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
|
||||
'org.mockito:mockito-core:2.8.9:mockito-core-2.8.9.jar:a2bb9b8b40d81bb02ccb84259524c0f4911f73c6577bfc7ddd940b8fc729b6e6',
|
||||
'org.objenesis:objenesis:2.5:objenesis-2.5.jar:293328e1b0d31ed30bb89fca542b6c52fac00989bb0e62eb9d98d630c4dd6b7c',
|
||||
'org.ow2.asm:asm-analysis:5.1:asm-analysis-5.1.jar:a34658f5c5de4b573eef21131cc32cc25f7b66407944f312b28ec2e56abb1fa9',
|
||||
'org.ow2.asm:asm-commons:5.0.1:asm-commons-5.0.1.jar:fb1cb7fa27d892712ced8fbf8d027eb5052ecd3999dba1ba47824357accb40e7',
|
||||
'org.ow2.asm:asm-debug-all:5.0.1:asm-debug-all-5.0.1.jar:4734de5b515a454b0096db6971fb068e5f70e6f10bbee2b3bd2fdfe5d978ed57',
|
||||
'org.ow2.asm:asm-commons:5.1:asm-commons-5.1.jar:97b3786e1f55e74bddf8ad102bf50e33bbcbc1f6b7fd7b36f0bbbb25cd4981be',
|
||||
'org.ow2.asm:asm-tree:5.0.1:asm-tree-5.0.1.jar:ff2aceed10da9930a44f6c8f81c6372d5e55eb59c4e0ea9d37f77dfd765fa9fa',
|
||||
'org.ow2.asm:asm-tree:5.1:asm-tree-5.1.jar:c0de2bbc4cb8297419659813ecd4ed1d077ed1dd5c1f5544cc5143e493e84c10',
|
||||
'org.ow2.asm:asm-util:5.1:asm-util-5.1.jar:ee032c39ae5e3cd099148fbba9a2124f9ed613e5cb93e03ee0fa8808ce364040',
|
||||
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||
'org.ow2.asm:asm:5.1:asm-5.1.jar:d2da399a9967c69f0a21739256fa79d284222c223082cacadc17372244764b54',
|
||||
'org.robolectric:annotations:3.5.1:annotations-3.5.1.jar:14db0f7d2299c5400ff7764bb37b4fa80306582d8965fdf6999091723e2384ce',
|
||||
'org.robolectric:junit:3.5.1:junit-3.5.1.jar:b2e81b7d5a22755f2ea76aa9bbbd4359d61c4cb9577193ccfbb8f97378ed293b',
|
||||
'org.robolectric:resources:3.5.1:resources-3.5.1.jar:22a5564590c8bfd8df7efb2b0c7d9942b46a0beb59ba38899d59c1270f293b1c',
|
||||
@@ -163,39 +212,41 @@ dependencyVerification {
|
||||
'org.robolectric:shadows-framework:3.5.1:shadows-framework-3.5.1.jar:597b54cc1a494799d783921c6ac04352f33e94fca8e00f299d4ca192db79e3fc',
|
||||
'org.robolectric:shadows-support-v4:3.0:shadows-support-v4-3.0.jar:66bcc3257b037d72998e860d67b1bc58215b7eeac8ad860fcc3e613332d88619',
|
||||
'org.robolectric:utils:3.5.1:utils-3.5.1.jar:d7d77326867e6d903156ebb18c244819b26aebe3aa82a1c57081081a0b6c4f63',
|
||||
'org.slf4j:slf4j-api:1.7.16:slf4j-api-1.7.16.jar:e56288031f5e60652c06e7bb6e9fa410a61231ab54890f7b708fc6adc4107c5b',
|
||||
'uk.co.samuelwall:material-tap-target-prompt:2.1.0:material-tap-target-prompt-2.1.0.aar:6df17e7c371f9264bfe440506f91205dd47566869c1528891943a04a26c96a94',
|
||||
'uk.co.samuelwall:material-tap-target-prompt:2.8.0:material-tap-target-prompt-2.8.0.aar:ac70770c05bbc4675a1d5712c0e53d46ee4fa961b74947589fce50d8003065ec',
|
||||
'xmlpull:xmlpull:1.1.3.1:xmlpull-1.1.3.1.jar:34e08ee62116071cbb69c0ed70d15a7a5b208d62798c59f2120bb8929324cb63',
|
||||
'xpp3:xpp3_min:1.1.4c:xpp3_min-1.1.4c.jar:bfc90e9e32d0eab1f397fb974b5f150a815188382ac41f372a7149d5bc178008',
|
||||
]
|
||||
}
|
||||
|
||||
def getGitHash = { ->
|
||||
def getStdout = { command, defaultValue ->
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
try {
|
||||
exec {
|
||||
commandLine 'git', 'rev-parse', '--short=7', 'HEAD'
|
||||
commandLine = command
|
||||
standardOutput = stdout
|
||||
}
|
||||
return stdout.toString().trim()
|
||||
} catch (Exception ignored) {
|
||||
return "No commit hash"
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '26.0.2'
|
||||
buildToolsVersion '27.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 10001
|
||||
versionName "1.0.1"
|
||||
versionCode 10004
|
||||
versionName "1.0.4"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
resValue "string", "app_package", "org.briarproject.briar.android"
|
||||
resValue "string", "app_name", "Briar"
|
||||
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
|
||||
buildConfigField "String", "GitHash",
|
||||
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
||||
buildConfigField "Long", "BuildTimestamp",
|
||||
"${getStdout(['git', 'log', '-n', '1', '--date=unix', '--format=%cd'], 0)}000L"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_LOGS"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
|
||||
@NotNullByDefault
|
||||
class AndroidDatabaseConfig implements DatabaseConfig {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(AndroidDatabaseConfig.class.getName());
|
||||
|
||||
private final File dir;
|
||||
|
||||
@Nullable
|
||||
private volatile SecretKey key = null;
|
||||
@Nullable
|
||||
private volatile String nickname = null;
|
||||
|
||||
AndroidDatabaseConfig(File dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean databaseExists() {
|
||||
// FIXME should not run on UiThread #620
|
||||
if (!dir.isDirectory()) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info(dir.getAbsolutePath() + " is not a directory");
|
||||
return false;
|
||||
}
|
||||
File[] files = dir.listFiles();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
if (files == null) {
|
||||
LOG.info("Could not list files in " + dir.getAbsolutePath());
|
||||
} else {
|
||||
LOG.info("Files in " + dir.getAbsolutePath() + ":");
|
||||
for (File f : files) LOG.info(f.getName());
|
||||
}
|
||||
LOG.info("Database exists: " + (files != null && files.length > 0));
|
||||
}
|
||||
return files != null && files.length > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDatabaseDirectory() {
|
||||
File dir = this.dir;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Database directory: " + dir.getAbsolutePath());
|
||||
return dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncryptionKey(SecretKey key) {
|
||||
LOG.info("Setting database key");
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalAuthorName(String nickname) {
|
||||
LOG.info("Setting local author name");
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getLocalAuthorName() {
|
||||
String nickname = this.nickname;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Local author name has been set: " + (nickname != null));
|
||||
return nickname;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public SecretKey getEncryptionKey() {
|
||||
SecretKey key = this.key;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Database key has been set: " + (key != null));
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxSize() {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@ package org.briarproject.briar.android;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.StrictMode;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
||||
import org.briarproject.bramble.api.ui.UiCallback;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.android.DozeWatchdog;
|
||||
@@ -23,7 +24,6 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||
import java.io.File;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -84,60 +84,20 @@ public class AppModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
DatabaseConfig provideDatabaseConfig(Application app) {
|
||||
//FIXME: StrictMode
|
||||
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskReads();
|
||||
StrictMode.allowThreadDiskWrites();
|
||||
File dir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
|
||||
StrictMode.setThreadPolicy(tp);
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
DatabaseConfig databaseConfig = new DatabaseConfig() {
|
||||
|
||||
private volatile SecretKey key;
|
||||
private volatile String nickname;
|
||||
|
||||
@Override
|
||||
public boolean databaseExists() {
|
||||
// FIXME should not run on UiThread #620
|
||||
if (!dir.isDirectory()) return false;
|
||||
File[] files = dir.listFiles();
|
||||
return files != null && files.length > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDatabaseDirectory() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncryptionKey(SecretKey key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalAuthorName(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getLocalAuthorName() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public SecretKey getEncryptionKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxSize() {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
};
|
||||
DatabaseConfig databaseConfig = new AndroidDatabaseConfig(dir);
|
||||
return databaseConfig;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
DevConfig provideDevConfig(CryptoComponent crypto) {
|
||||
DevConfig provideDevConfig(Application app, CryptoComponent crypto) {
|
||||
@NotNullByDefault
|
||||
DevConfig devConfig = new DevConfig() {
|
||||
|
||||
@@ -155,6 +115,11 @@ public class AppModule {
|
||||
public String getDevOnionAddress() {
|
||||
return DEV_ONION_ADDRESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getReportDir() {
|
||||
return AndroidUtils.getReportDir(app.getApplicationContext());
|
||||
}
|
||||
};
|
||||
return devConfig;
|
||||
}
|
||||
@@ -204,4 +169,5 @@ public class AppModule {
|
||||
lifecycleManager.registerService(dozeWatchdog);
|
||||
return dozeWatchdog;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
/**
|
||||
* This exists so that the Application object will not necessarily be cast
|
||||
* directly to the Briar application object.
|
||||
*/
|
||||
public interface BriarApplication {
|
||||
|
||||
// This build expires on 31 December 2018
|
||||
long EXPIRY_DATE = 1546214400 * 1000L;
|
||||
Collection<LogRecord> getRecentLogRecords();
|
||||
|
||||
AndroidComponent getApplicationComponent();
|
||||
|
||||
}
|
||||
|
||||
@@ -12,12 +12,17 @@ import org.acra.annotation.ReportsCrashes;
|
||||
import org.briarproject.bramble.BrambleCoreModule;
|
||||
import org.briarproject.briar.BriarCoreModule;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||
import org.briarproject.briar.android.reporting.BriarReportPrimer;
|
||||
import org.briarproject.briar.android.reporting.BriarReportSenderFactory;
|
||||
import org.briarproject.briar.android.reporting.DevReportActivity;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static org.acra.ReportField.ANDROID_VERSION;
|
||||
import static org.acra.ReportField.APP_VERSION_CODE;
|
||||
import static org.acra.ReportField.APP_VERSION_NAME;
|
||||
@@ -28,7 +33,6 @@ import static org.acra.ReportField.CUSTOM_DATA;
|
||||
import static org.acra.ReportField.DEVICE_FEATURES;
|
||||
import static org.acra.ReportField.DISPLAY;
|
||||
import static org.acra.ReportField.INITIAL_CONFIGURATION;
|
||||
import static org.acra.ReportField.LOGCAT;
|
||||
import static org.acra.ReportField.PACKAGE_NAME;
|
||||
import static org.acra.ReportField.PHONE_MODEL;
|
||||
import static org.acra.ReportField.PRODUCT;
|
||||
@@ -36,7 +40,7 @@ import static org.acra.ReportField.REPORT_ID;
|
||||
import static org.acra.ReportField.STACK_TRACE;
|
||||
import static org.acra.ReportField.USER_APP_START_DATE;
|
||||
import static org.acra.ReportField.USER_CRASH_DATE;
|
||||
import static org.briarproject.briar.android.TestingConstants.DEFAULT_LOG_LEVEL;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_BETA_BUILD;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
|
||||
@ReportsCrashes(
|
||||
@@ -56,8 +60,7 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
STACK_TRACE,
|
||||
INITIAL_CONFIGURATION, CRASH_CONFIGURATION,
|
||||
DISPLAY, DEVICE_FEATURES,
|
||||
USER_APP_START_DATE, USER_CRASH_DATE,
|
||||
LOGCAT
|
||||
USER_APP_START_DATE, USER_CRASH_DATE
|
||||
}
|
||||
)
|
||||
public class BriarApplicationImpl extends Application
|
||||
@@ -66,6 +69,8 @@ public class BriarApplicationImpl extends Application
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BriarApplicationImpl.class.getName());
|
||||
|
||||
private final CachingLogHandler logHandler = new CachingLogHandler();
|
||||
|
||||
private AndroidComponent applicationComponent;
|
||||
|
||||
@Override
|
||||
@@ -79,7 +84,17 @@ public class BriarApplicationImpl extends Application
|
||||
super.onCreate();
|
||||
|
||||
if (IS_DEBUG_BUILD) enableStrictMode();
|
||||
Logger.getLogger("").setLevel(DEFAULT_LOG_LEVEL);
|
||||
|
||||
Logger rootLogger = Logger.getLogger("");
|
||||
if (!IS_DEBUG_BUILD && !IS_BETA_BUILD) {
|
||||
// Remove default log handlers so system log is not used
|
||||
for (Handler handler : rootLogger.getHandlers()) {
|
||||
rootLogger.removeHandler(handler);
|
||||
}
|
||||
}
|
||||
rootLogger.addHandler(logHandler);
|
||||
rootLogger.setLevel(INFO);
|
||||
|
||||
LOG.info("Created");
|
||||
|
||||
applicationComponent = DaggerAndroidComponent.builder()
|
||||
@@ -104,6 +119,11 @@ public class BriarApplicationImpl extends Application
|
||||
StrictMode.setVmPolicy(vmPolicy.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LogRecord> getRecentLogRecords() {
|
||||
return logHandler.getRecentLogRecords();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidComponent getApplicationComponent() {
|
||||
return applicationComponent;
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.logout.HideUiActivity;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -44,6 +43,7 @@ import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
|
||||
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
|
||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||
@@ -221,18 +221,52 @@ public class BriarService extends Service {
|
||||
public void onLowMemory() {
|
||||
super.onLowMemory();
|
||||
LOG.warning("Memory is low");
|
||||
// Clear the UI - this is done in onTrimMemory() if SDK_INT >= 16
|
||||
if (SDK_INT < 16) hideUi();
|
||||
}
|
||||
|
||||
private void shutdownFromBackground() {
|
||||
// Stop the service
|
||||
stopSelf();
|
||||
// Hide the UI
|
||||
@Override
|
||||
public void onTrimMemory(int level) {
|
||||
super.onTrimMemory(level);
|
||||
if (level == TRIM_MEMORY_UI_HIDDEN) {
|
||||
LOG.info("Trim memory: UI hidden");
|
||||
} else if (level == TRIM_MEMORY_BACKGROUND) {
|
||||
LOG.info("Trim memory: added to LRU list");
|
||||
} else if (level == TRIM_MEMORY_MODERATE) {
|
||||
LOG.info("Trim memory: near middle of LRU list");
|
||||
} else if (level == TRIM_MEMORY_COMPLETE) {
|
||||
LOG.info("Trim memory: near end of LRU list");
|
||||
} else if (SDK_INT >= 16) {
|
||||
if (level == TRIM_MEMORY_RUNNING_MODERATE) {
|
||||
LOG.info("Trim memory: running moderately low");
|
||||
} else if (level == TRIM_MEMORY_RUNNING_LOW) {
|
||||
LOG.info("Trim memory: running low");
|
||||
} else if (level == TRIM_MEMORY_RUNNING_CRITICAL) {
|
||||
LOG.info("Trim memory: running critically low");
|
||||
// Clear the UI to save some memory
|
||||
hideUi();
|
||||
} else if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Trim memory: unknown level " + level);
|
||||
}
|
||||
} else if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Trim memory: unknown level " + level);
|
||||
}
|
||||
}
|
||||
|
||||
private void hideUi() {
|
||||
Intent i = new Intent(this, HideUiActivity.class);
|
||||
i.addFlags(FLAG_ACTIVITY_NEW_TASK
|
||||
| FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
|
||||
| FLAG_ACTIVITY_NO_ANIMATION
|
||||
| FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
private void shutdownFromBackground() {
|
||||
// Stop the service
|
||||
stopSelf();
|
||||
// Hide the UI
|
||||
hideUi();
|
||||
// Wait for shutdown to complete, then exit
|
||||
new Thread(() -> {
|
||||
try {
|
||||
@@ -245,25 +279,6 @@ public class BriarService extends Service {
|
||||
}).start();
|
||||
}
|
||||
|
||||
// TODO: Remove if low memory shutdowns are not appropriate
|
||||
private void showLowMemoryShutdownNotification() {
|
||||
androidExecutor.runOnUiThread(() -> {
|
||||
NotificationCompat.Builder b = new NotificationCompat.Builder(
|
||||
BriarService.this, FAILURE_CHANNEL_ID);
|
||||
b.setSmallIcon(android.R.drawable.stat_notify_error);
|
||||
b.setContentTitle(getText(
|
||||
R.string.low_memory_shutdown_notification_title));
|
||||
b.setContentText(getText(
|
||||
R.string.low_memory_shutdown_notification_text));
|
||||
Intent i = new Intent(this, SplashScreenActivity.class);
|
||||
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
||||
b.setAutoCancel(true);
|
||||
Object o = getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.notify(FAILURE_NOTIFICATION_ID, b.build());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for all services to start before returning.
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,6 @@ import android.content.pm.Signature;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
@@ -196,7 +195,7 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor, Service {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startService() throws ServiceException {
|
||||
public void startService() {
|
||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||
androidExecutor.runOnUiThread(() -> {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
@@ -212,7 +211,7 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor, Service {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopService() throws ServiceException {
|
||||
public void stopService() {
|
||||
androidExecutor.runOnUiThread(() -> {
|
||||
if (receiver != null) app.unregisterReceiver(receiver);
|
||||
});
|
||||
|
||||
@@ -2,11 +2,6 @@ package org.briarproject.briar.android;
|
||||
|
||||
import org.briarproject.briar.BuildConfig;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.OFF;
|
||||
|
||||
public interface TestingConstants {
|
||||
|
||||
/**
|
||||
@@ -20,12 +15,6 @@ public interface TestingConstants {
|
||||
*/
|
||||
boolean IS_BETA_BUILD = false;
|
||||
|
||||
/**
|
||||
* Default log level. Disable logging for final release builds.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
Level DEFAULT_LOG_LEVEL = IS_DEBUG_BUILD || IS_BETA_BUILD ? INFO : OFF;
|
||||
|
||||
/**
|
||||
* Whether to prevent screenshots from being taken. Setting this to true
|
||||
* prevents Recent Apps from storing screenshots of private information.
|
||||
@@ -33,4 +22,12 @@ public interface TestingConstants {
|
||||
* intentionally.
|
||||
*/
|
||||
boolean PREVENT_SCREENSHOTS = !IS_DEBUG_BUILD;
|
||||
|
||||
/**
|
||||
* Debug and beta builds expire after 90 days. Final release builds expire
|
||||
* after 292 million years.
|
||||
*/
|
||||
long EXPIRY_DATE = IS_DEBUG_BUILD || IS_BETA_BUILD ?
|
||||
BuildConfig.BuildTimestamp + 90 * 24 * 60 * 60 * 1000L :
|
||||
Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
package org.briarproject.briar.android.controller;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
|
||||
@NotNullByDefault
|
||||
public class ConfigControllerImpl implements ConfigController {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ConfigControllerImpl.class.getName());
|
||||
|
||||
private static final String PREF_DB_KEY = "key";
|
||||
|
||||
private final SharedPreferences briarPrefs;
|
||||
@@ -23,39 +32,46 @@ public class ConfigControllerImpl implements ConfigController {
|
||||
DatabaseConfig databaseConfig) {
|
||||
this.briarPrefs = briarPrefs;
|
||||
this.databaseConfig = databaseConfig;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getEncryptedDatabaseKey() {
|
||||
return briarPrefs.getString(PREF_DB_KEY, null);
|
||||
String key = briarPrefs.getString(PREF_DB_KEY, null);
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Got database key from preferences: " + (key != null));
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint("ApplySharedPref")
|
||||
public void storeEncryptedDatabaseKey(String hex) {
|
||||
SharedPreferences.Editor editor = briarPrefs.edit();
|
||||
editor.putString(PREF_DB_KEY, hex);
|
||||
editor.apply();
|
||||
LOG.info("Storing database key in preferences");
|
||||
briarPrefs.edit().putString(PREF_DB_KEY, hex).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAccount(Context ctx) {
|
||||
SharedPreferences.Editor editor = briarPrefs.edit();
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
AndroidUtils.deleteAppData(ctx);
|
||||
LOG.info("Deleting account");
|
||||
SharedPreferences defaultPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
AndroidUtils.deleteAppData(ctx, briarPrefs, defaultPrefs);
|
||||
AndroidUtils.logDataDirContents(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accountExists() {
|
||||
String hex = getEncryptedDatabaseKey();
|
||||
return hex != null && databaseConfig.databaseExists();
|
||||
boolean exists = hex != null && databaseConfig.databaseExists();
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Account exists: " + exists);
|
||||
return exists;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accountSignedIn() {
|
||||
return databaseConfig.getEncryptionKey() != null;
|
||||
boolean signedIn = databaseConfig.getEncryptionKey() != null;
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Signed in: " + signedIn);
|
||||
return signedIn;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.Locale.US;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
public class BriefLogFormatter extends Formatter {
|
||||
|
||||
private final Object lock = new Object();
|
||||
private final DateFormat dateFormat; // Locking: lock
|
||||
private final Date date; // Locking: lock
|
||||
|
||||
public BriefLogFormatter() {
|
||||
synchronized (lock) {
|
||||
dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS ", US);
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
date = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
String dateString;
|
||||
synchronized (lock) {
|
||||
date.setTime(record.getMillis());
|
||||
dateString = dateFormat.format(date);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(dateString);
|
||||
sb.append(record.getLevel().getName().charAt(0)).append('/');
|
||||
String tag = record.getLoggerName();
|
||||
tag = tag.substring(tag.lastIndexOf('.') + 1);
|
||||
sb.append(tag).append(": ");
|
||||
sb.append(record.getMessage());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
public class CachingLogHandler extends Handler {
|
||||
|
||||
private static final int MAX_RECENT_RECORDS = 100;
|
||||
|
||||
private final Object lock = new Object();
|
||||
// Locking: lock
|
||||
private final Queue<LogRecord> recent = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
synchronized (lock) {
|
||||
recent.add(record);
|
||||
if (recent.size() > MAX_RECENT_RECORDS) recent.poll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (lock) {
|
||||
recent.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<LogRecord> getRecentLogRecords() {
|
||||
synchronized (lock) {
|
||||
return new ArrayList<>(recent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,15 +8,21 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_NEXT;
|
||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_NONE;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.briar.android.util.UiUtils.setError;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class AuthorNameFragment extends SetupFragment {
|
||||
|
||||
private final static String TAG = AuthorNameFragment.class.getName();
|
||||
@@ -30,8 +36,9 @@ public class AuthorNameFragment extends SetupFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
getActivity().setTitle(getString(R.string.setup_title));
|
||||
View v = inflater.inflate(R.layout.fragment_setup_author_name,
|
||||
container, false);
|
||||
@@ -75,6 +82,7 @@ public class AuthorNameFragment extends SetupFragment {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
setupController.setAuthorName(authorNameInput.getText().toString());
|
||||
setupController.showPasswordFragment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.login.PowerView.OnCheckedChangedListener;
|
||||
@@ -21,7 +22,8 @@ import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
|
||||
|
||||
@NotNullByDefault
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class DozeFragment extends SetupFragment
|
||||
implements OnCheckedChangedListener {
|
||||
|
||||
|
||||
@@ -11,15 +11,21 @@ import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class PasswordFragment extends SetupFragment {
|
||||
|
||||
private final static String TAG = PasswordFragment.class.getName();
|
||||
@@ -37,8 +43,9 @@ public class PasswordFragment extends SetupFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
getActivity().setTitle(getString(R.string.setup_password_intro));
|
||||
View v = inflater.inflate(R.layout.fragment_setup_password, container,
|
||||
false);
|
||||
@@ -105,16 +112,17 @@ public class PasswordFragment extends SetupFragment {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (!setupController.needToShowDozeFragment()) {
|
||||
nextButton.setVisibility(INVISIBLE);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
}
|
||||
String password = passwordEntry.getText().toString();
|
||||
IBinder token = passwordEntry.getWindowToken();
|
||||
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
|
||||
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
|
||||
setupController.setPassword(password);
|
||||
setupController.showDozeOrCreateAccount();
|
||||
setupController.setPassword(passwordEntry.getText().toString());
|
||||
if (setupController.needToShowDozeFragment()) {
|
||||
setupController.showDozeFragment();
|
||||
} else {
|
||||
nextButton.setVisibility(INVISIBLE);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
setupController.createAccount();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -89,9 +89,9 @@ abstract class PowerView extends ConstraintLayout {
|
||||
public void setChecked(boolean checked) {
|
||||
this.checked = checked;
|
||||
if (checked) {
|
||||
checkImage.setImageResource(R.drawable.ic_check_white);
|
||||
checkImage.setVisibility(VISIBLE);
|
||||
} else {
|
||||
checkImage.setImageResource(R.drawable.contact_disconnected);
|
||||
checkImage.setVisibility(INVISIBLE);
|
||||
}
|
||||
if (onCheckedChangedListener != null) {
|
||||
onCheckedChangedListener.onCheckedChanged();
|
||||
|
||||
@@ -4,30 +4,46 @@ import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BaseActivity;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class SetupActivity extends BaseActivity
|
||||
implements BaseFragmentListener {
|
||||
|
||||
private static final String STATE_KEY_AUTHOR_NAME = "authorName";
|
||||
private static final String STATE_KEY_PASSWORD = "password";
|
||||
|
||||
@Inject
|
||||
SetupController setupController;
|
||||
|
||||
@Nullable
|
||||
private String authorName, password;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
super.onCreate(state);
|
||||
// fade-in after splash screen instead of default animation
|
||||
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
|
||||
setContentView(R.layout.activity_fragment_container);
|
||||
|
||||
if (state == null) {
|
||||
if (setupController.accountExists())
|
||||
throw new AssertionError();
|
||||
showInitialFragment(AuthorNameFragment.newInstance());
|
||||
} else {
|
||||
authorName = state.getString(STATE_KEY_AUTHOR_NAME);
|
||||
password = state.getString(STATE_KEY_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,16 +53,46 @@ public class SetupActivity extends BaseActivity
|
||||
setupController.setSetupActivity(this);
|
||||
}
|
||||
|
||||
public void showPasswordFragment() {
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle state) {
|
||||
super.onSaveInstanceState(state);
|
||||
if (authorName != null)
|
||||
state.putString(STATE_KEY_AUTHOR_NAME, authorName);
|
||||
if (password != null)
|
||||
state.putString(STATE_KEY_PASSWORD, password);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getAuthorName() {
|
||||
return authorName;
|
||||
}
|
||||
|
||||
void setAuthorName(String authorName) {
|
||||
this.authorName = authorName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
void showPasswordFragment() {
|
||||
if (authorName == null) throw new IllegalStateException();
|
||||
showNextFragment(PasswordFragment.newInstance());
|
||||
}
|
||||
|
||||
@TargetApi(23)
|
||||
public void showDozeFragment() {
|
||||
void showDozeFragment() {
|
||||
if (authorName == null) throw new IllegalStateException();
|
||||
if (password == null) throw new IllegalStateException();
|
||||
showNextFragment(DozeFragment.newInstance());
|
||||
}
|
||||
|
||||
public void showApp() {
|
||||
void showApp() {
|
||||
Intent i = new Intent(this, OpenDatabaseActivity.class);
|
||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(i);
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package org.briarproject.briar.android.login;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface SetupController {
|
||||
public interface SetupController extends PasswordController {
|
||||
|
||||
void setSetupActivity(SetupActivity setupActivity);
|
||||
|
||||
@@ -14,17 +13,20 @@ public interface SetupController {
|
||||
|
||||
void setPassword(String password);
|
||||
|
||||
float estimatePasswordStrength(String password);
|
||||
/**
|
||||
* This should be called after the author name has been set.
|
||||
*/
|
||||
void showPasswordFragment();
|
||||
|
||||
/**
|
||||
* This should be called after the author name and the password have been
|
||||
* set. It decides whether to ask for doze exception or create the account
|
||||
* right away.
|
||||
* set.
|
||||
*/
|
||||
void showDozeOrCreateAccount();
|
||||
void showDozeFragment();
|
||||
|
||||
/**
|
||||
* This should be called after the author name and the password have been
|
||||
* set.
|
||||
*/
|
||||
void createAccount();
|
||||
|
||||
void createAccount(ResultHandler<Void> resultHandler);
|
||||
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -20,10 +22,11 @@ import javax.inject.Inject;
|
||||
public class SetupControllerImpl extends PasswordControllerImpl
|
||||
implements SetupController {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(SetupControllerImpl.class.getName());
|
||||
|
||||
@Nullable
|
||||
private String authorName, password;
|
||||
@Nullable
|
||||
private SetupActivity setupActivity;
|
||||
private volatile SetupActivity setupActivity;
|
||||
|
||||
@Inject
|
||||
SetupControllerImpl(SharedPreferences briarPrefs,
|
||||
@@ -41,6 +44,7 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
||||
|
||||
@Override
|
||||
public boolean needToShowDozeFragment() {
|
||||
SetupActivity setupActivity = this.setupActivity;
|
||||
if (setupActivity == null) throw new IllegalStateException();
|
||||
return DozeView.needsToBeShown(setupActivity) ||
|
||||
HuaweiView.needsToBeShown(setupActivity);
|
||||
@@ -48,28 +52,35 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
||||
|
||||
@Override
|
||||
public void setAuthorName(String authorName) {
|
||||
this.authorName = authorName;
|
||||
SetupActivity setupActivity = this.setupActivity;
|
||||
if (setupActivity == null) throw new IllegalStateException();
|
||||
setupActivity.setAuthorName(authorName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPassword(String password) {
|
||||
SetupActivity setupActivity = this.setupActivity;
|
||||
if (setupActivity == null) throw new IllegalStateException();
|
||||
setupActivity.setPassword(password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showPasswordFragment() {
|
||||
SetupActivity setupActivity = this.setupActivity;
|
||||
if (setupActivity == null) throw new IllegalStateException();
|
||||
setupActivity.showPasswordFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showDozeOrCreateAccount() {
|
||||
public void showDozeFragment() {
|
||||
SetupActivity setupActivity = this.setupActivity;
|
||||
if (setupActivity == null) throw new IllegalStateException();
|
||||
if (needToShowDozeFragment()) {
|
||||
setupActivity.showDozeFragment();
|
||||
} else {
|
||||
createAccount();
|
||||
}
|
||||
setupActivity.showDozeFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAccount() {
|
||||
SetupActivity setupActivity = this.setupActivity;
|
||||
UiResultHandler<Void> resultHandler =
|
||||
new UiResultHandler<Void>(setupActivity) {
|
||||
@Override
|
||||
@@ -82,16 +93,24 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
||||
createAccount(resultHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAccount(ResultHandler<Void> resultHandler) {
|
||||
if (authorName == null || password == null)
|
||||
throw new IllegalStateException();
|
||||
// Package access for testing
|
||||
void createAccount(ResultHandler<Void> resultHandler) {
|
||||
SetupActivity setupActivity = this.setupActivity;
|
||||
if (setupActivity == null) throw new IllegalStateException();
|
||||
String authorName = setupActivity.getAuthorName();
|
||||
if (authorName == null) throw new IllegalStateException();
|
||||
String password = setupActivity.getPassword();
|
||||
if (password == null) throw new IllegalStateException();
|
||||
cryptoExecutor.execute(() -> {
|
||||
LOG.info("Creating account");
|
||||
AndroidUtils.logDataDirContents(setupActivity);
|
||||
databaseConfig.setLocalAuthorName(authorName);
|
||||
SecretKey key = crypto.generateSecretKey();
|
||||
databaseConfig.setEncryptionKey(key);
|
||||
String hex = encryptDatabaseKey(key, password);
|
||||
storeEncryptedDatabaseKey(hex);
|
||||
LOG.info("Created account");
|
||||
AndroidUtils.logDataDirContents(setupActivity);
|
||||
resultHandler.onResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
|
||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_BETA_BUILD;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
import static org.briarproject.briar.android.controller.BriarControllerImpl.DOZE_ASK_AGAIN;
|
||||
|
||||
@@ -18,6 +18,8 @@ import org.acra.builder.ReportBuilder;
|
||||
import org.acra.builder.ReportPrimer;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.BuildConfig;
|
||||
import org.briarproject.briar.android.BriarApplication;
|
||||
import org.briarproject.briar.android.logging.BriefLogFormatter;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -28,6 +30,8 @@ import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
|
||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
||||
@@ -69,6 +73,16 @@ public class BriarReportPrimer implements ReportPrimer {
|
||||
public Map<String, String> call() {
|
||||
Map<String, String> customData = new LinkedHashMap<>();
|
||||
|
||||
// Log
|
||||
BriarApplication app =
|
||||
(BriarApplication) ctx.getApplicationContext();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Formatter formatter = new BriefLogFormatter();
|
||||
for (LogRecord record : app.getRecentLogRecords()) {
|
||||
sb.append(formatter.format(record)).append('\n');
|
||||
}
|
||||
customData.put("Log", sb.toString());
|
||||
|
||||
// System memory
|
||||
Object o = ctx.getSystemService(ACTIVITY_SERVICE);
|
||||
ActivityManager am = (ActivityManager) o;
|
||||
@@ -223,9 +237,10 @@ public class BriarReportPrimer implements ReportPrimer {
|
||||
customData.put("Bluetooth LE status", btLeStatus);
|
||||
}
|
||||
|
||||
if (bt != null)
|
||||
if (bt != null) {
|
||||
customData.put("Bluetooth address",
|
||||
scrubMacAddress(bt.getAddress()));
|
||||
}
|
||||
String btSettingsAddr;
|
||||
try {
|
||||
btSettingsAddr = Settings.Secure.getString(
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.support.annotation.NonNull;
|
||||
import org.acra.collector.CrashReportData;
|
||||
import org.acra.sender.ReportSender;
|
||||
import org.acra.sender.ReportSenderException;
|
||||
import org.acra.util.JSONReportBuilder;
|
||||
import org.acra.util.JSONReportBuilder.JSONReportException;
|
||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.briar.android.AndroidComponent;
|
||||
@@ -37,7 +37,7 @@ public class BriarReportSender implements ReportSender {
|
||||
String crashReport;
|
||||
try {
|
||||
crashReport = errorContent.toJSON().toString();
|
||||
} catch (JSONReportBuilder.JSONReportException e) {
|
||||
} catch (JSONReportException e) {
|
||||
throw new ReportSenderException("Couldn't create JSON", e);
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.support.v7.preference.CheckBoxPreference;
|
||||
import android.support.v7.preference.ListPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.support.v7.preference.PreferenceGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.acra.ACRA;
|
||||
@@ -149,9 +150,19 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference testData = findPreference("pref_key_test_data");
|
||||
if (!IS_DEBUG_BUILD) {
|
||||
testData.setVisible(false);
|
||||
if (IS_DEBUG_BUILD) {
|
||||
findPreference("pref_key_explode").setOnPreferenceClickListener(
|
||||
preference -> {
|
||||
throw new RuntimeException("Boom!");
|
||||
}
|
||||
);
|
||||
} else {
|
||||
findPreference("pref_key_explode").setVisible(false);
|
||||
findPreference("pref_key_test_data").setVisible(false);
|
||||
PreferenceGroup testing =
|
||||
findPreference("pref_key_explode").getParent();
|
||||
if (testing == null) throw new AssertionError();
|
||||
testing.setVisible(false);
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.support.v7.preference.PreferenceManager;
|
||||
import android.transition.Fade;
|
||||
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BaseActivity;
|
||||
@@ -19,7 +20,7 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
|
||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
||||
|
||||
public class SplashScreenActivity extends BaseActivity {
|
||||
|
||||
@@ -44,9 +45,11 @@ public class SplashScreenActivity extends BaseActivity {
|
||||
setContentView(R.layout.splash);
|
||||
|
||||
if (configController.accountSignedIn()) {
|
||||
LOG.info("Already signed in, not showing splash screen");
|
||||
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
||||
finish();
|
||||
} else {
|
||||
LOG.info("Showing splash screen");
|
||||
new Handler().postDelayed(() -> {
|
||||
startNextActivity();
|
||||
supportFinishAfterTransition();
|
||||
@@ -64,9 +67,12 @@ public class SplashScreenActivity extends BaseActivity {
|
||||
LOG.info("Expired");
|
||||
startActivity(new Intent(this, ExpiredActivity.class));
|
||||
} else {
|
||||
AndroidUtils.logDataDirContents(this);
|
||||
if (configController.accountExists()) {
|
||||
LOG.info("Account exists");
|
||||
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
||||
} else {
|
||||
LOG.info("Account does not exist");
|
||||
configController.deleteAccount(this);
|
||||
startActivity(new Intent(this, SetupActivity.class));
|
||||
}
|
||||
@@ -74,8 +80,10 @@ public class SplashScreenActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
private void setPreferencesDefaults() {
|
||||
androidExecutor.runOnBackgroundThread(() ->
|
||||
PreferenceManager.setDefaultValues(SplashScreenActivity.this,
|
||||
R.xml.panic_preferences, false));
|
||||
androidExecutor.runOnBackgroundThread(() -> {
|
||||
PreferenceManager.setDefaultValues(SplashScreenActivity.this,
|
||||
R.xml.panic_preferences, false);
|
||||
LOG.info("Finished setting panic preference defaults");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,20 @@ public class TestDataActivity extends BriarActivity {
|
||||
@Inject
|
||||
TestDataCreator testDataCreator;
|
||||
|
||||
private TextView[] textViews = new TextView[5];
|
||||
private SeekBar[] seekBars = new SeekBar[5];
|
||||
private TextView contactsTextView;
|
||||
private SeekBar contactsSeekBar;
|
||||
|
||||
private TextView messagesTextView;
|
||||
private SeekBar messagesSeekBar;
|
||||
|
||||
private TextView blogPostsTextView;
|
||||
private SeekBar blogPostsSeekBar;
|
||||
|
||||
private TextView forumsTextView;
|
||||
private SeekBar forumsSeekBar;
|
||||
|
||||
private TextView forumPostsTextView;
|
||||
private SeekBar forumPostsSeekBar;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
@@ -37,45 +49,111 @@ public class TestDataActivity extends BriarActivity {
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_test_data);
|
||||
textViews[0] = findViewById(R.id.textViewContactsSb);
|
||||
textViews[1] = findViewById(R.id.textViewMessagesSb);
|
||||
textViews[2] = findViewById(R.id.TextViewBlogPostsSb);
|
||||
textViews[3] = findViewById(R.id.TextViewForumsSb);
|
||||
textViews[4] = findViewById(R.id.TextViewForumMessagesSb);
|
||||
seekBars[0] = findViewById(R.id.seekBarContacts);
|
||||
seekBars[1] = findViewById(R.id.seekBarMessages);
|
||||
seekBars[2] = findViewById(R.id.seekBarBlogPosts);
|
||||
seekBars[3] = findViewById(R.id.seekBarForums);
|
||||
seekBars[4] = findViewById(R.id.seekBarForumMessages);
|
||||
contactsTextView = findViewById(R.id.textViewContactsSb);
|
||||
messagesTextView = findViewById(R.id.textViewMessagesSb);
|
||||
blogPostsTextView = findViewById(R.id.TextViewBlogPostsSb);
|
||||
forumsTextView = findViewById(R.id.TextViewForumsSb);
|
||||
forumPostsTextView = findViewById(R.id.TextViewForumMessagesSb);
|
||||
contactsSeekBar = findViewById(R.id.seekBarContacts);
|
||||
messagesSeekBar = findViewById(R.id.seekBarMessages);
|
||||
blogPostsSeekBar = findViewById(R.id.seekBarBlogPosts);
|
||||
forumsSeekBar = findViewById(R.id.seekBarForums);
|
||||
forumPostsSeekBar = findViewById(R.id.seekBarForumMessages);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
final TextView textView = textViews[i];
|
||||
seekBars[i].setOnSeekBarChangeListener(
|
||||
new OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar,
|
||||
int progress, boolean fromUser) {
|
||||
textView.setText(String.valueOf(progress));
|
||||
}
|
||||
contactsSeekBar
|
||||
.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar,
|
||||
int progress, boolean fromUser) {
|
||||
contactsTextView.setText(String.valueOf(progress + 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
|
||||
messagesSeekBar
|
||||
.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar,
|
||||
int progress, boolean fromUser) {
|
||||
messagesTextView.setText(String.valueOf(progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
|
||||
blogPostsSeekBar
|
||||
.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar,
|
||||
int progress, boolean fromUser) {
|
||||
blogPostsTextView.setText(String.valueOf(progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
|
||||
forumsSeekBar
|
||||
.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar,
|
||||
int progress, boolean fromUser) {
|
||||
forumsTextView.setText(String.valueOf(progress));
|
||||
forumPostsSeekBar.setEnabled(progress > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
|
||||
forumPostsSeekBar
|
||||
.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar,
|
||||
int progress, boolean fromUser) {
|
||||
forumPostsTextView.setText(String.valueOf(progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.buttonCreateTestData).setOnClickListener(
|
||||
v -> createTestData());
|
||||
}
|
||||
|
||||
private void createTestData() {
|
||||
testDataCreator.createTestData(seekBars[0].getProgress(),
|
||||
seekBars[1].getProgress(), seekBars[2].getProgress(),
|
||||
seekBars[3].getProgress(), seekBars[4].getProgress());
|
||||
testDataCreator.createTestData(contactsSeekBar.getProgress() + 1,
|
||||
messagesSeekBar.getProgress(), blogPostsSeekBar.getProgress(),
|
||||
forumsSeekBar.getProgress(), forumPostsSeekBar.getProgress());
|
||||
Intent intent = new Intent(this, NavDrawerActivity.class);
|
||||
intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
|
||||
@@ -46,7 +46,7 @@ import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
|
||||
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
|
||||
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
|
||||
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
|
||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
android:id="@+id/seekBarContacts"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:max="50"
|
||||
android:max="49"
|
||||
android:progress="20"
|
||||
app:layout_constraintEnd_toStartOf="@+id/textViewContactsSb"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/divider">
|
||||
</View>
|
||||
@@ -20,10 +20,12 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/checkImage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/contact_disconnected"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_margin="8dp"
|
||||
android:src="@drawable/ic_check_white"
|
||||
android:tint="?colorControlNormal"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/button"
|
||||
@@ -34,9 +36,7 @@
|
||||
style="@style/BriarButton.Default"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/helpButton"
|
||||
app:layout_constraintStart_toEndOf="@+id/checkImage"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView"
|
||||
@@ -44,11 +44,13 @@
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/helpButton"
|
||||
style="@style/BriarButton.Default"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/BriarButtonFlat.Positive"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_margin="8dp"
|
||||
android:contentDescription="@string/help"
|
||||
android:src="@drawable/ic_help_outline_white"
|
||||
android:tint="@color/briar_button_positive"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/button"/>
|
||||
|
||||
11
briar-android/src/main/res/layout/preferences_category.xml
Normal file
11
briar-android/src/main/res/layout/preferences_category.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/briar_blue_light"/>
|
||||
147
briar-android/src/main/res/values-ast/strings.xml
Normal file
147
briar-android/src/main/res/values-ast/strings.xml
Normal file
@@ -0,0 +1,147 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<!--Setup-->
|
||||
<string name="setup_title">Afáyate\'n Briar</string>
|
||||
<string name="setup_name_explanation">El to alcuñu apaecerá cabo cualquier conteníu que publiques.Nun puede cambiase después de crear la cuenta.</string>
|
||||
<string name="setup_next">Siguiente</string>
|
||||
<string name="setup_password_intro">Escueye una contraseña</string>
|
||||
<string name="setup_doze_title">Conexones de fondu</string>
|
||||
<string name="setup_doze_button">Permitir conexones</string>
|
||||
<string name="choose_nickname">Escueyi un alcuñu</string>
|
||||
<string name="choose_password">Escueyi una contraseña</string>
|
||||
<string name="confirm_password">Confirma la contraseña</string>
|
||||
<string name="name_too_long">El nome ye enforma llargu</string>
|
||||
<string name="password_too_weak">La contraseña ye enforma feble</string>
|
||||
<string name="passwords_do_not_match">Les contraseñes nun casen</string>
|
||||
<string name="create_account_button">Crear cuenta</string>
|
||||
<string name="more_info">Más información</string>
|
||||
<string name="don_t_ask_again">Nun volver a entrugar</string>
|
||||
<string name="setup_huawei_button">Protexer Briar</string>
|
||||
<string name="warning_dozed">%s nun pudo executase\'n segundu planu</string>
|
||||
<!--Login-->
|
||||
<string name="enter_password">Escribi la contraseña</string>
|
||||
<string name="try_again">Contraseña enquivocada, tenta otra vuelta</string>
|
||||
<string name="sign_in_button">Aniciar sesión</string>
|
||||
<string name="forgotten_password">Escaecí la contraseña</string>
|
||||
<string name="dialog_title_lost_password">Contraseña perdida</string>
|
||||
<string name="dialog_message_lost_password">La cuenta de Briar guárdase cifrada nel preséu, non na nube, de manera que nun podemos reaniciala.. ¿Quies desaniciar la cuenta y principiar de cero?\n\nAtención: Les tos identidaes, contautos y mensaxes perderánse de mou permanente.</string>
|
||||
<string name="startup_failed_notification_title">Briar nun pudo arrancar</string>
|
||||
<string name="startup_failed_activity_title">Fallu nel arranque de Briar</string>
|
||||
<string name="expiry_date_reached">Esti software caducó.\n¡Gracies por probalu!</string>
|
||||
<string name="download_briar">Pa siguir usando Briar, descarga la versión 1.0.</string>
|
||||
<string name="create_new_account">Tendrás de crear una cuenta nueva, pero puedes usar el mesmu alcuñu.</string>
|
||||
<string name="download_briar_button">Descargar Briar 1.0</string>
|
||||
<!--Navigation Drawer-->
|
||||
<string name="nav_drawer_open_description">Abrir el caxón de navegación</string>
|
||||
<string name="nav_drawer_close_description">Zarrar el caxón de navegación</string>
|
||||
<string name="contact_list_button">Contactos</string>
|
||||
<string name="groups_button">Grupos privaos</string>
|
||||
<string name="forums_button">Foros</string>
|
||||
<string name="blogs_button">Blogs</string>
|
||||
<string name="settings_button">Preferencies</string>
|
||||
<string name="sign_out_button">Colar</string>
|
||||
<!--Transports-->
|
||||
<string name="transport_tor">Internet</string>
|
||||
<string name="transport_bt">Bluetooth</string>
|
||||
<string name="transport_lan">Wi-Fi</string>
|
||||
<!--Notifications-->
|
||||
<string name="ongoing_notification_title">Sesión aniciada</string>
|
||||
<string name="ongoing_notification_text">Toca p\'abrir Briar.</string>
|
||||
<plurals name="private_message_notification_text">
|
||||
<item quantity="one">Mensaxes priváu nuevu.</item>
|
||||
<item quantity="other">%d mensaxes privaos nuevos.</item>
|
||||
</plurals>
|
||||
<plurals name="group_message_notification_text">
|
||||
<item quantity="one">Mensaxe de grupu nuevu.</item>
|
||||
<item quantity="other">%d mensaxes de grupu nuevos.</item>
|
||||
</plurals>
|
||||
<plurals name="forum_post_notification_text">
|
||||
<item quantity="one">Mensaxe de foru nuevu.</item>
|
||||
<item quantity="other">%d mensaxes de foru nuevos.</item>
|
||||
</plurals>
|
||||
<plurals name="blog_post_notification_text">
|
||||
<item quantity="one">Una publicación del blog nueva.</item>
|
||||
<item quantity="other">%d publicaciones del blog nueves.</item>
|
||||
</plurals>
|
||||
<!--Misc-->
|
||||
<string name="now">agora</string>
|
||||
<string name="show">Amosar</string>
|
||||
<string name="hide">Anubrir</string>
|
||||
<string name="ok">Aceutar</string>
|
||||
<string name="cancel">Encaboxar</string>
|
||||
<string name="got_it">Entendílo</string>
|
||||
<string name="delete">Desaniciar</string>
|
||||
<string name="accept">Aceutar</string>
|
||||
<string name="decline">Refugar</string>
|
||||
<string name="options">Opciones</string>
|
||||
<string name="online">En llinia</string>
|
||||
<string name="offline">Sin conexón</string>
|
||||
<string name="send">Unviar</string>
|
||||
<string name="allow">Permitir</string>
|
||||
<string name="open">Abrir</string>
|
||||
<string name="no_data">Sin datos</string>
|
||||
<string name="ellipsis">…</string>
|
||||
<string name="fix">Iguar</string>
|
||||
<string name="help">Ayuda</string>
|
||||
<!--Contacts and Private Conversations-->
|
||||
<!--Adding Contacts-->
|
||||
<!--Introductions-->
|
||||
<!--Private Groups-->
|
||||
<plurals name="messages">
|
||||
<item quantity="one">%d mensaxe</item>
|
||||
<item quantity="other">%d mensaxes</item>
|
||||
</plurals>
|
||||
<string name="groups_remove">Desaniciar</string>
|
||||
<string name="groups_create_group_title">Crear Grupu priváu</string>
|
||||
<string name="groups_create_group_button">Crear Grupu</string>
|
||||
<string name="groups_create_group_invitation_button">Unviar Invitación</string>
|
||||
<string name="groups_member_list">Llista de miembros</string>
|
||||
<string name="groups_invite_members">Invitar a miembros</string>
|
||||
<string name="groups_member_created_you">Creasti\'\'l grupu</string>
|
||||
<string name="groups_member_created">%s creó\'\'l grupu</string>
|
||||
<string name="groups_dissolve_button">Disolver</string>
|
||||
<string name="groups_dissolved_dialog_title">Disolvióse\'\'l grupu</string>
|
||||
<!--Private Group Invitations-->
|
||||
<string name="groups_invitations_invitation_sent">Invitasti a %1$s a xunise al grupu \"%2$s\".</string>
|
||||
<string name="groups_invitations_invitation_received">%1$s invitóte a xunite al grupu \"%2$s\".</string>
|
||||
<!--Private Groups Revealing Contacts-->
|
||||
<!--Forums-->
|
||||
<string name="create_forum_title">Crear foru</string>
|
||||
<string name="choose_forum_hint">Escueyi un nome pal foru</string>
|
||||
<string name="create_forum_button">Crear foru</string>
|
||||
<string name="forum_created_toast">Creóse\'l foru</string>
|
||||
<string name="no_forum_posts">Nun hai mensaxes qu\'amosar</string>
|
||||
<!--Forum Sharing-->
|
||||
<string name="nobody">Naide</string>
|
||||
<!--Blogs-->
|
||||
<string name="blogs_other_blog_empty_state">Nun hai mensaxes qu\'amosar</string>
|
||||
<string name="read_more">lleer más</string>
|
||||
<string name="blogs_publish_blog_post">Publicar</string>
|
||||
<string name="blogs_remove_blog_ok">Desaniciar</string>
|
||||
<!--Blog Sharing-->
|
||||
<!--RSS Feeds-->
|
||||
<string name="blogs_rss_feeds_import">Importar canal RSS</string>
|
||||
<string name="blogs_rss_feeds_import_button">Importar</string>
|
||||
<string name="blogs_rss_feeds_manage_author">Autor:</string>
|
||||
<string name="blogs_rss_remove_feed_ok">Desaniciar</string>
|
||||
<!--Settings Network-->
|
||||
<string name="network_settings_title">Redes</string>
|
||||
<string name="tor_network_setting">Coneutar vía Tor</string>
|
||||
<string name="tor_network_setting_never">Nunca</string>
|
||||
<string name="tor_network_setting_wifi">Sólo al usar Wi-Fi</string>
|
||||
<!--Settings Security and Panic-->
|
||||
<string name="security_settings_title">Seguridá</string>
|
||||
<string name="panic_setting">Configuración del Botón d\'espantu</string>
|
||||
<string name="panic_setting_title">Botón d\'espantu</string>
|
||||
<string name="lock_setting_title">Colar</string>
|
||||
<string name="uninstall_setting_title">Desinstalar Briar</string>
|
||||
<!--Settings Notifications-->
|
||||
<string name="notification_settings_title">Avisos</string>
|
||||
<string name="notify_private_messages_setting_title">Mensaxes privaos</string>
|
||||
<!--Settings Feedback-->
|
||||
<!--Link Warning-->
|
||||
<!--Crash Reporter-->
|
||||
<!--Sign Out-->
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<!--Permission Requests-->
|
||||
</resources>
|
||||
@@ -326,5 +326,4 @@
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<string name="screen_filter_title">Открит е овърлей на екрана</string>
|
||||
<!--Permission Requests-->
|
||||
<!--Low Memory Notification-->
|
||||
</resources>
|
||||
|
||||
@@ -39,18 +39,30 @@
|
||||
<string name="ongoing_notification_text">Touchit da zigeriñ Briar.</string>
|
||||
<plurals name="private_message_notification_text">
|
||||
<item quantity="one">Kemennadenn prevez nevez</item>
|
||||
<item quantity="two">%d a gemennadennoù prevez nevez. </item>
|
||||
<item quantity="few">%d a gemennadennoù prevez nevez. </item>
|
||||
<item quantity="many">%d a gemennadennoù prevez nevez. </item>
|
||||
<item quantity="other">%d a gemennadennoù prevez nevez. </item>
|
||||
</plurals>
|
||||
<plurals name="group_message_notification_text">
|
||||
<item quantity="one">Kemennadenn strollad nevez.</item>
|
||||
<item quantity="two">%d a gemennadennoù strollad nevez.</item>
|
||||
<item quantity="few">%d a gemennadennoù strollad nevez.</item>
|
||||
<item quantity="many">%d a gemennadennoù strollad nevez.</item>
|
||||
<item quantity="other">%d a gemennadennoù strollad nevez.</item>
|
||||
</plurals>
|
||||
<plurals name="forum_post_notification_text">
|
||||
<item quantity="one">Postadenn forom nevez.</item>
|
||||
<item quantity="two">%da bostadennoù forom nevez.</item>
|
||||
<item quantity="few">%da bostadennoù forom nevez.</item>
|
||||
<item quantity="many">%da bostadennoù forom nevez.</item>
|
||||
<item quantity="other">%da bostadennoù forom nevez.</item>
|
||||
</plurals>
|
||||
<plurals name="blog_post_notification_text">
|
||||
<item quantity="one">Postadenn blog nevez.</item>
|
||||
<item quantity="two">%d a bostadennoù blog nevez.</item>
|
||||
<item quantity="few">%d a bostadennoù blog nevez.</item>
|
||||
<item quantity="many">%d a bostadennoù blog nevez.</item>
|
||||
<item quantity="other">%d a bostadennoù blog nevez.</item>
|
||||
</plurals>
|
||||
<!--Misc-->
|
||||
@@ -99,12 +111,18 @@
|
||||
<string name="introduction_response_error">Fazi en ur respont d\'an digoradur</string>
|
||||
<plurals name="introduction_notification_text">
|
||||
<item quantity="one">Darempred nevez ouzhpennet.</item>
|
||||
<item quantity="two">%d a zarempredoù nevez ouzhpennet.</item>
|
||||
<item quantity="few">%d a zarempredoù nevez ouzhpennet.</item>
|
||||
<item quantity="many">%d a zarempredoù nevez ouzhpennet.</item>
|
||||
<item quantity="other">%d a zarempredoù nevez ouzhpennet.</item>
|
||||
</plurals>
|
||||
<!--Private Groups-->
|
||||
<string name="groups_created_by">Graet gant %s</string>
|
||||
<plurals name="messages">
|
||||
<item quantity="one">%d kemennadenn</item>
|
||||
<item quantity="two">%d a gemennadennoù</item>
|
||||
<item quantity="few">%d a gemennadennoù</item>
|
||||
<item quantity="many">%d a gemennadennoù</item>
|
||||
<item quantity="other">%d a gemennadennoù</item>
|
||||
</plurals>
|
||||
<string name="groups_group_is_empty">Leun eo ar strollad-mañ</string>
|
||||
@@ -149,5 +167,4 @@
|
||||
<!--Sign Out-->
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<!--Permission Requests-->
|
||||
<!--Low Memory Notification-->
|
||||
</resources>
|
||||
|
||||
@@ -393,7 +393,4 @@
|
||||
<string name="permission_camera_denied_toast">No s\'ha concedit el permís de la càmera</string>
|
||||
<string name="qr_code">Codi QR</string>
|
||||
<string name="show_qr_code_fullscreen">Mostra el codi QR a pantalla completa</string>
|
||||
<!--Low Memory Notification-->
|
||||
<string name="low_memory_shutdown_notification_title">Has sortit de Briar</string>
|
||||
<string name="low_memory_shutdown_notification_text">Has sortit de Briar a causa de falta de memòria.</string>
|
||||
</resources>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<plurals name="expiry_warning">
|
||||
<item quantity="one">Toto je testovací verze Briar. Váš účet a jeho platnost vyprší po %d dnech a není možné ho obnovit.</item>
|
||||
<item quantity="few">Toto je testovací verze Briar. Váš účet a jeho platnost vyprší po %d dnech a není možné ho obnovit.</item>
|
||||
<item quantity="many">Toto je testovací verze Briar. Váš účet a jeho platnost vyprší po %d dnech a není možné ho obnovit.</item>
|
||||
<item quantity="other">Toto je testovací verze Briar. Váš účet a jeho platnost vyprší po %d dnech a není možné ho obnovit.</item>
|
||||
</plurals>
|
||||
<string name="expiry_update">Datum expirace pro testování bylo prodlouženo. Váš účet nyní bude expirovat po %d dnech.</string>
|
||||
@@ -59,21 +60,25 @@
|
||||
<plurals name="private_message_notification_text">
|
||||
<item quantity="one">Nová soukromá zpráva</item>
|
||||
<item quantity="few">%d nových soukromých zpráv.</item>
|
||||
<item quantity="many">%d nových soukromých zpráv.</item>
|
||||
<item quantity="other">%d nových soukromých zpráv.</item>
|
||||
</plurals>
|
||||
<plurals name="group_message_notification_text">
|
||||
<item quantity="one">Nová zpráva ve skupině.</item>
|
||||
<item quantity="few">%d nových zpráv ve skupině. </item>
|
||||
<item quantity="many">%d nových zpráv ve skupině.</item>
|
||||
<item quantity="other">%d nových zpráv ve skupině.</item>
|
||||
</plurals>
|
||||
<plurals name="forum_post_notification_text">
|
||||
<item quantity="one">Nový příspěvek ve fóru.</item>
|
||||
<item quantity="few">%dnových příspěvků ve fóru. </item>
|
||||
<item quantity="many">%d nových příspěvků ve fóru.</item>
|
||||
<item quantity="other">%d nových příspěvků ve fóru.</item>
|
||||
</plurals>
|
||||
<plurals name="blog_post_notification_text">
|
||||
<item quantity="one">Nový příspěvek v blogu.</item>
|
||||
<item quantity="few">%dnových příspěvků v blogu. </item>
|
||||
<item quantity="many">%d nových příspěvků v blogu.</item>
|
||||
<item quantity="other">%d nových příspěvků v blogu.</item>
|
||||
</plurals>
|
||||
<!--Misc-->
|
||||
@@ -144,6 +149,7 @@
|
||||
<plurals name="introduction_notification_text">
|
||||
<item quantity="one">Nový kontakt byl přidán.</item>
|
||||
<item quantity="few">Bylo přidáno %d nových kontaktů.</item>
|
||||
<item quantity="many">%d nových kontaktů bylo přidáno.</item>
|
||||
<item quantity="other">%d nových kontaktů bylo přidáno.</item>
|
||||
</plurals>
|
||||
<!--Private Groups-->
|
||||
@@ -151,6 +157,7 @@
|
||||
<plurals name="messages">
|
||||
<item quantity="one">%d zpráva</item>
|
||||
<item quantity="few">%d zpráv</item>
|
||||
<item quantity="many">%d zpráv</item>
|
||||
<item quantity="other">%d zpráv</item>
|
||||
</plurals>
|
||||
<string name="groups_group_is_empty">Tato skupina je prázdná</string>
|
||||
@@ -186,6 +193,7 @@
|
||||
<plurals name="groups_invitations_open">
|
||||
<item quantity="one">%d otevřená skupinová pozvánka</item>
|
||||
<item quantity="few">%d otevřených skupinových pozvánek</item>
|
||||
<item quantity="many">%d otevřených skupinových pozvánek</item>
|
||||
<item quantity="other">%d otevřených skupinových pozvánek</item>
|
||||
</plurals>
|
||||
<string name="groups_invitations_response_accepted_sent">Přijali jste pozvání do skupiny od %s.</string>
|
||||
@@ -209,6 +217,7 @@
|
||||
<plurals name="posts">
|
||||
<item quantity="one">%d příspěvek</item>
|
||||
<item quantity="few">%d příspěvků</item>
|
||||
<item quantity="many">%d příspěvků</item>
|
||||
<item quantity="other">%d příspěvků</item>
|
||||
</plurals>
|
||||
<string name="forum_new_entry_posted">Záznam byl na fóru zveřejněn</string>
|
||||
@@ -240,6 +249,7 @@
|
||||
<plurals name="forums_shared">
|
||||
<item quantity="one">%d fórum sdíleno kontakty</item>
|
||||
<item quantity="few">%d fór sdíleno kontakty </item>
|
||||
<item quantity="many">%d fór sdíleno kontakty</item>
|
||||
<item quantity="other">%d fór sdíleno kontakty</item>
|
||||
</plurals>
|
||||
<string name="nobody">Nikdo</string>
|
||||
@@ -362,5 +372,4 @@
|
||||
<string name="permission_camera_request_body">Pro scan QR kódu, Briar vyžaduje přístup k fotoaparátu.</string>
|
||||
<string name="permission_camera_denied_body">Odmítli jste udělit oprávnění přístupu k fotoaparátu, avšak pro přidání kontaktů je nutné použití fotoaparátu.\n\nZvažte prosím, opětovné udělení přístupu.</string>
|
||||
<string name="permission_camera_denied_toast">Oprávnění pro přístup k fotoaparátu nebylo uděleno</string>
|
||||
<!--Low Memory Notification-->
|
||||
</resources>
|
||||
|
||||
@@ -31,7 +31,11 @@
|
||||
<string name="dialog_title_lost_password">Passwort vergessen</string>
|
||||
<string name="dialog_message_lost_password">Dein Briar-Konto ist auf deinem Gerät verschlüsselt und nicht in der Cloud gespeichert, deshalb kannst du dein Passwort nicht zurücksetzen. Willst du dein Konto löschen und neu beginnen?\n\nAchtung: Deine bestehenden Identitäten, Kontakte und Nachrichten gehen dann für immer verloren.</string>
|
||||
<string name="startup_failed_notification_title">Briar konnte nicht gestartet werden</string>
|
||||
<string name="startup_failed_notification_text">Für weitere Informationen, hier klicken.</string>
|
||||
<string name="startup_failed_activity_title">Fehler beim Starten von Briar</string>
|
||||
<string name="startup_failed_db_error">Aus irgendeinem Grund ist deine Briar-Datenbank irreparabel beschädigt. Dein Konto, deine Daten und alle deinen Kontakte sind verloren. Leider musst du Briar neu installieren oder ein neues Konto einrichten, indem du ,Ich habe mein Passwort vergessen\' auswählst, wenn du zur Eingabe deines Passworts aufgefordert wirst. </string>
|
||||
<string name="startup_failed_data_too_old_error">Dein Konto wurde mit einer alten Version dieser App erstellt und kann mit dieser Version nicht geöffnet werden. Installiere entweder die alte Version oder richte ein neues Konto ein, indem du \"Ich habe mein Passwort vergessen\" auswählst, wenn du zur Eingabe deines Passworts aufgefordert wirst. </string>
|
||||
<string name="startup_failed_data_too_new_error">Diese Version der App ist zu alt. Bitte führe eine Aktualisierung auf die neueste Version der App durch und versuch es dann noch mal.</string>
|
||||
<string name="startup_failed_service_error">Briar konnte ein benötigtes Plugin nicht starten. Normalerweise kann das Problem durch eine Neuinstallation von Briar gelöst werden. Eine Neuinstallation führt jedoch zum Verlust deines Kontos und aller dazugehörigen Daten, da Briar deine Daten nicht auf zentralen Servern speichert.</string>
|
||||
<plurals name="expiry_warning">
|
||||
<item quantity="one">Dies ist eine Testversion von Briar. Dein Konto läuft in %d Tag ab und kann nicht verlängert werden.</item>
|
||||
@@ -39,6 +43,11 @@
|
||||
</plurals>
|
||||
<string name="expiry_update">Das Ablaufdatum des Tests wurde verlängert. Dein Konto läuft nun in %d Tagen ab.</string>
|
||||
<string name="expiry_date_reached">Diese Software ist abgelaufen.\nDanke dass du Briar getestet hast!</string>
|
||||
<string name="download_briar">Lade bitte Version 1.0 herunter, um Briar weiterhin zu nutzen.</string>
|
||||
<string name="create_new_account">Du wirst ein neues Konto erstellen müssen, wobei du jedoch wieder den selben Benutzernamen verwenden kannst.</string>
|
||||
<string name="download_briar_button">Lade Briar 1.0 herunter</string>
|
||||
<string name="startup_open_database">Datenbank wird entschlüsselt...</string>
|
||||
<string name="startup_migrate_database">Datenbank wird aktualisiert...</string>
|
||||
<!--Navigation Drawer-->
|
||||
<string name="nav_drawer_open_description">Navigationsleiste öffnen</string>
|
||||
<string name="nav_drawer_close_description">Navigationsleiste schliessen</string>
|
||||
@@ -94,6 +103,7 @@
|
||||
<string name="fix">Behoben</string>
|
||||
<string name="help">Hilfe</string>
|
||||
<!--Contacts and Private Conversations-->
|
||||
<string name="no_contacts">Du hast noch keine Kontakte\n\nTippe auf das +-Symbol um einen Kontakt hinzuzufügen</string>
|
||||
<string name="date_no_private_messages">Keine Nachrichten.</string>
|
||||
<string name="message_hint">Nachricht eingeben</string>
|
||||
<string name="delete_contact">Kontakt löschen</string>
|
||||
@@ -112,6 +122,7 @@
|
||||
<string name="contact_already_exists">Kontakt %s existiert bereits</string>
|
||||
<string name="contact_exchange_failed">Kontaktaustausch fehlgeschlagen</string>
|
||||
<string name="qr_code_invalid">Der QR-Code ist ungültig</string>
|
||||
<string name="qr_code_unsupported">Der QR-Code, den du versuchst zu scannen, gehört zu einer alten Version von %s welche nicht mehr unterstützt wird.\n\nBitte versichert euch, dass bei euch beiden die neueste Version läuft und versucht es dann erneut.</string>
|
||||
<string name="camera_error">Kamerafehler</string>
|
||||
<string name="connecting_to_device">Verbinde mit Gerät\u2026</string>
|
||||
<string name="authenticating_with_device">Authentifiziere Gerät\u2026</string>
|
||||
@@ -208,7 +219,9 @@
|
||||
<string name="btn_reply">Antworten</string>
|
||||
<string name="forum_leave">Forum verlassen</string>
|
||||
<string name="dialog_title_leave_forum">Verlassen des Forums bestätigen</string>
|
||||
<string name="dialog_message_leave_forum">Bist du sicher, dass du dieses Forum verlassen willst?\n\nKontakte, mit denen du dieses Forum geteilt hast, werden keine Updates mehr von diesem Forum bekommen.</string>
|
||||
<string name="dialog_button_leave">Verlassen</string>
|
||||
<string name="forum_left_toast">Forum wurde verlassen</string>
|
||||
<!--Forum Sharing-->
|
||||
<string name="forum_share_button">Forum teilen</string>
|
||||
<string name="contacts_selected">Ausgewählte Kontakte</string>
|
||||
@@ -219,6 +232,9 @@
|
||||
<string name="forum_invitation_received">%1$s hat das Forum \"%2$s\" mit dir geteilt.</string>
|
||||
<string name="forum_invitation_sent">Du hast das Forum \"%1$s\" mit %2$s geteilt.</string>
|
||||
<string name="forum_invitations_title">Foreneinladungen</string>
|
||||
<string name="forum_invitation_exists">Du hast bereits eine Einladung zu diesem Forum angenommen.\n\nMehrere Einladungen anzunehmen, wird deine Verbindung zu diesem Forum schneller und zuverlässiger machen.</string>
|
||||
<string name="forum_joined_toast">Dem Forum beigetreten</string>
|
||||
<string name="forum_declined_toast">Einladung abgelehnt</string>
|
||||
<string name="shared_by_format">Geteilt durch %s</string>
|
||||
<string name="forum_invitation_already_sharing">Bereits geteilt.</string>
|
||||
<string name="forum_invitation_response_accepted_sent">Du hast die Forumseinladung von %s akzeptiert.</string>
|
||||
@@ -236,12 +252,14 @@
|
||||
<!--Blogs-->
|
||||
<string name="read_more">weiterlesen</string>
|
||||
<string name="blogs_write_blog_post">Blogbeitrag erstellen</string>
|
||||
<string name="blogs_write_blog_post_body_hint">Gib deinen Blogbeitrag ein</string>
|
||||
<string name="blogs_publish_blog_post">Veröffentlichen</string>
|
||||
<string name="blogs_blog_post_created">Blogbeitrag erstellt</string>
|
||||
<string name="blogs_blog_post_received">Neuen Blogbeitrag empfangen</string>
|
||||
<string name="blogs_blog_post_scroll_to">Scrolle zu</string>
|
||||
<string name="blogs_remove_blog">Blog entfernen</string>
|
||||
<string name="blogs_remove_blog_ok">Aufheben</string>
|
||||
<string name="blogs_blog_removed">Blog wurde entfernt</string>
|
||||
<string name="blogs_reblog_comment_hint">Kommentar hinzufügen (optional)</string>
|
||||
<string name="blogs_reblog_button">Rebloggen</string>
|
||||
<!--Blog Sharing-->
|
||||
@@ -256,6 +274,8 @@
|
||||
<string name="blogs_sharing_invitation_received">%1$shat den Blog \"%2$s\" mit dir geteilt.</string>
|
||||
<string name="blogs_sharing_invitation_sent">Du teilst den Blog \"%1$s\" mit %2$s.</string>
|
||||
<string name="blogs_sharing_invitations_title">Blogeinladungen</string>
|
||||
<string name="blogs_sharing_joined_toast">Blog abonniert</string>
|
||||
<string name="blogs_sharing_declined_toast">Einladung abgelehnt</string>
|
||||
<string name="sharing_status_blog">Jeder Abonnent eines Blogs kann diesen mit seinen Kontakten teilen. Du teilst diesen Blog mit den folgenden Kontakten. Möglicherweise gibt es Abonnenten die nicht sichtbar sind.</string>
|
||||
<!--RSS Feeds-->
|
||||
<string name="blogs_rss_feeds_import">RSS-Feed importieren</string>
|
||||
@@ -355,5 +375,4 @@
|
||||
<string name="permission_camera_denied_body">Du hast den Zugriff auf die Kamera verweigert, aber das Hinzufügen von Kontakten erfordert die Verwendung der Kamera.\n\nBitte gewähre den Zugriff.</string>
|
||||
<string name="permission_camera_denied_toast">Berechtigung für Kamera wurde nicht gewährt</string>
|
||||
<string name="qr_code">QR-Code</string>
|
||||
<!--Low Memory Notification-->
|
||||
</resources>
|
||||
|
||||
@@ -386,7 +386,4 @@
|
||||
<string name="permission_camera_denied_toast">No se concedió el permiso de cámara</string>
|
||||
<string name="qr_code">Código QR</string>
|
||||
<string name="show_qr_code_fullscreen">Mostrar código QR a pantalla completa</string>
|
||||
<!--Low Memory Notification-->
|
||||
<string name="low_memory_shutdown_notification_title">Desconectado de Briar</string>
|
||||
<string name="low_memory_shutdown_notification_text">Desconectado por falta de memoria.</string>
|
||||
</resources>
|
||||
|
||||
@@ -134,6 +134,7 @@
|
||||
<string name="introduction_onboarding_title">Aurkeztu zure kontaktuak</string>
|
||||
<string name="introduction_onboarding_text">Zure kontaktuak bata besteari aurkeztu ditzakezu, horrela ez dute aurrez aurre elkartzeko beharra Briar erabiltzeko.</string>
|
||||
<string name="introduction_activity_title">Hautatu kontaktua</string>
|
||||
<string name="introduction_not_possible">Baduzu aurkezpen bat abian kontaktu hauekin. Baimendu hura bukatzea aurretik. Zure kontaktuak gutxitan konektatzen badira honek denbora bat behar lezake.</string>
|
||||
<string name="introduction_message_title">Aurkeztu kontaktuak</string>
|
||||
<string name="introduction_message_hint">Gehitu mezu bat (aukerazkoa)</string>
|
||||
<string name="introduction_button">Egin aurkezpena</string>
|
||||
@@ -145,6 +146,7 @@
|
||||
<string name="introduction_request_exists_received">%1$s erabiltzaileak %2$s zuri aurkeztea eskatu dizu, baina %2$s badago zure kontaktuen zerrendan. Agian %1$s erabiltzaileak ez daki, erantzun dezakezu:</string>
|
||||
<string name="introduction_request_answered_received">%1$s erabiltzaileak %2$s zuri aurkeztea eskatu du.</string>
|
||||
<string name="introduction_response_accepted_sent">%1$s erabiltzailearen aurkezpena onartu duzu.</string>
|
||||
<string name="introduction_response_accepted_sent_info">%1$s zure kontaktuetara gehitu aurretik, aurkezpena onartu behar dute ere. Honek denbora bat behar lezake.</string>
|
||||
<string name="introduction_response_declined_sent">%1$s erabiltzailearen aurkezpena ukatu duzu.</string>
|
||||
<string name="introduction_response_accepted_received">%1$s erabiltzaileak %2$s erabiltzailearen aurkezpena onartu du.</string>
|
||||
<string name="introduction_response_declined_received">%1$s erabiltzaileak %2$s erabiltzailearen aurkezpena ukatu du.</string>
|
||||
@@ -154,6 +156,7 @@
|
||||
<item quantity="other">%d kontaktu berri gehituta.</item>
|
||||
</plurals>
|
||||
<!--Private Groups-->
|
||||
<string name="groups_list_empty">Ez dago talderik erakusteko\n\nSakatu + ikonoa talde bat sortzeko, edo eskatu zure kontaktuei taldeak zurekin partekatzea</string>
|
||||
<string name="groups_created_by">%s erabiltzaileak sortuta</string>
|
||||
<plurals name="messages">
|
||||
<item quantity="one">mezu %d</item>
|
||||
@@ -206,6 +209,7 @@
|
||||
<string name="groups_reveal_visible_revealed_by_contact">Kontaktuen erlazioa taldean ikusgai dago (%s erabiltzaileak argitara emana)</string>
|
||||
<string name="groups_reveal_invisible">Kontaktuen erlazioa ez dago taldean ikusgai</string>
|
||||
<!--Forums-->
|
||||
<string name="no_forums">Ez dago fororik erakusteko\n\nSakatu + ikonoa talde bat sortzeko, edo eskatu zure kontaktuei foroak zurekin partekatzea</string>
|
||||
<string name="create_forum_title">Sortu foroa</string>
|
||||
<string name="choose_forum_hint">Hautatu zure foroaren izena</string>
|
||||
<string name="create_forum_button">Sortu foroa</string>
|
||||
@@ -222,17 +226,21 @@
|
||||
<string name="btn_reply">Erantzun</string>
|
||||
<string name="forum_leave">Utzi foroa</string>
|
||||
<string name="dialog_title_leave_forum">Berretsi foroa uztea</string>
|
||||
<string name="dialog_message_leave_forum">Ziur foro hau utzi nahi duzula?\n\nForo hau beste norbaitekin partekatu baduzu agian ez dituzte eguneraketak jasoko.</string>
|
||||
<string name="dialog_button_leave">Utzi</string>
|
||||
<string name="forum_left_toast">Foroa utzi du</string>
|
||||
<!--Forum Sharing-->
|
||||
<string name="forum_share_button">Partekatu foroa</string>
|
||||
<string name="contacts_selected">Kontaktuak hautatuta</string>
|
||||
<string name="activity_share_toolbar_header">Hautatu kontaktuak</string>
|
||||
<string name="no_contacts_selector">Ez dago kontakturik erakusteko\n\nItzuli hona kontakturen bat gehitu eta gero</string>
|
||||
<string name="forum_shared_snackbar">Foroa partekatuta hautatutako kontaktuekin</string>
|
||||
<string name="forum_share_message">Gehitu mezua (aukerazkoa)</string>
|
||||
<string name="forum_share_error">Errore bat egon da foro hau partekatzean.</string>
|
||||
<string name="forum_invitation_received">%1$s erabiltzaileak \"%2$s\" foroa partekatu du zurekin.</string>
|
||||
<string name="forum_invitation_sent">\"%1$s\" foroa partekatu duzu %2$s erabiltzailearekin.</string>
|
||||
<string name="forum_invitations_title">Forora gonbidapenak</string>
|
||||
<string name="forum_invitation_exists">Foro honetarako gonbidapen bat onartu duzu jada. Gonbidapen gehiago onartzeak foroarekin konexioa azkarrago eta egonkorragoa egingo du.</string>
|
||||
<string name="forum_joined_toast">Forora elkartuta</string>
|
||||
<string name="forum_declined_toast">Gonbidapena ukatuta</string>
|
||||
<string name="shared_by_format">%s erabiltzaileak partekatuta</string>
|
||||
@@ -258,7 +266,9 @@
|
||||
<string name="blogs_blog_post_created">Blog sarrera sortuta</string>
|
||||
<string name="blogs_blog_post_received">Blog sarrera berria jasota</string>
|
||||
<string name="blogs_blog_post_scroll_to">Korritu hona</string>
|
||||
<string name="blogs_feed_empty_state">Ez dago sarrerarik erakusteko\n\nZure kontaktuen eta harpidetutako sarrerak bidalketak hemen agertuko dira\n\nSakatu arkatzaren ikonoa sarrera bat idazteko</string>
|
||||
<string name="blogs_remove_blog">Kendu bloga</string>
|
||||
<string name="blogs_remove_blog_dialog_message">Ziur blog hau kendu nahi duzula?\n\nSarrerak zure gailutik kenduko dira baina ez besteen gailuetatik.\n\nBlog hau beste inorekin partekatu baduzu agian eguneraketak jasotzeari utziko diote.</string>
|
||||
<string name="blogs_remove_blog_ok">Kendu</string>
|
||||
<string name="blogs_blog_removed">Blog-a kenduta</string>
|
||||
<string name="blogs_reblog_comment_hint">Gehitu iruzkina (aukerazkoa)</string>
|
||||
@@ -288,6 +298,7 @@
|
||||
<string name="blogs_rss_feeds_manage_author">Egilea:</string>
|
||||
<string name="blogs_rss_feeds_manage_updated">Azken eguneraketa:</string>
|
||||
<string name="blogs_rss_remove_feed">Kendu jarioa:</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">Ziur jario hau kendu nahi duzula?\n\nSarrerak zure gailutik kenduko dira baina ez besteen gailuetatik.\n\nJario hau beste inorekin partekatu baduzu agian eguneraketak jasotzeari utziko diote.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">Kendu</string>
|
||||
<string name="blogs_rss_feeds_manage_delete_error">Ezin izan da jarioa ezabatu!</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">RSS jariorik ez erakusteko\n\nSakatu + ikonoa jario bat inportatzeko</string>
|
||||
@@ -382,7 +393,4 @@
|
||||
<string name="permission_camera_denied_toast">Eza kameraren baimenik eman</string>
|
||||
<string name="qr_code">QR kodea</string>
|
||||
<string name="show_qr_code_fullscreen">Erakutsi QR kodea pantaila osoan</string>
|
||||
<!--Low Memory Notification-->
|
||||
<string name="low_memory_shutdown_notification_title">Briar saioa amaituta</string>
|
||||
<string name="low_memory_shutdown_notification_text">Saioa amaituta memoria faltagatik</string>
|
||||
</resources>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user