Compare commits

..

1 Commits

Author SHA1 Message Date
Santiago Torres
8bb55d2580 WIP: BluetoothPlugin: adds abstract superclass
The droidtooth plugin and bluetooth plugins share a lot of common code.
Add an abstract superclass that shares the common code between both
classes.
2017-05-01 01:16:38 -04:00
800 changed files with 26071 additions and 42439 deletions

1
.gitignore vendored
View File

@@ -20,7 +20,6 @@ local.properties
.idea/* .idea/*
!.idea/runConfigurations/ !.idea/runConfigurations/
!.idea/codeStyleSettings.xml !.idea/codeStyleSettings.xml
!.idea/codeStyles
.gradle .gradle
build/ build/
*.iml *.iml

View File

@@ -1,27 +0,0 @@
image: registry.gitlab.com/fdroid/ci-images-base:latest
cache:
paths:
- .gradle/wrapper
- .gradle/caches
before_script:
- set -e
- export GRADLE_USER_HOME=$PWD/.gradle
# 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
after_script:
# this file changes 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/

View File

@@ -1,261 +0,0 @@
<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>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -1,23 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="H2 Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="bramble-core" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" value="org.briarproject.bramble.db" />
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.db.H2DatabasePerformanceTest" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
</component>

View File

@@ -1,23 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="HyperSQL Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="bramble-core" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" value="org.briarproject.bramble.db" />
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.db.HyperSqlDatabasePerformanceTest" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
</component>

View File

@@ -1,192 +1,104 @@
import de.undercouch.gradle.tasks.download.Download import de.undercouch.gradle.tasks.download.Download
import de.undercouch.gradle.tasks.download.Verify import de.undercouch.gradle.tasks.download.Verify
import java.security.NoSuchAlgorithmException
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'witness' apply plugin: 'witness'
apply plugin: 'de.undercouch.download' apply plugin: 'de.undercouch.download'
android { android {
compileSdkVersion 27 compileSdkVersion 23
buildToolsVersion '27.0.3' buildToolsVersion "23.0.3"
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 26 targetSdkVersion 22
versionCode 10002 versionCode 1
versionName "1.0.2" versionName "1.0"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_7
} }
} }
dependencies { dependencies {
implementation project(path: ':bramble-core', configuration: 'default') compile project(':bramble-core')
implementation fileTree(dir: 'libs', include: '*.jar') compile fileTree(dir: 'libs', include: ['*.jar'])
provided 'javax.annotation:jsr250-api:1.0'
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
compileOnly 'javax.annotation:jsr250-api:1.0'
} }
dependencyVerification { def torBinaryDir = 'src/main/res/raw'
verify = [
'com.android.tools.analytics-library:protos:26.1.2:protos-26.1.2.jar:52672a0b42b572a06aecc3535d5068eb46c0e15d129b9f1085d3c16a1da5cdbb', task downloadTorGeoIp(type: Download) {
'com.android.tools.analytics-library:shared:26.1.2:shared-26.1.2.jar:5c7e0eda18c6f87feeb83628c707e8aaa3298b41fb72e38efe31ad1675f9e8e9', src 'https://briarproject.org/build/geoip-2015-12-01.zip'
'com.android.tools.analytics-library:tracker:26.1.2:tracker-26.1.2.jar:06f97aa0adf44ffb06f8681c6a79d9be153a08f61d21eddc42b8d3db96df4282', dest "$torBinaryDir/geoip.zip"
'com.android.tools.build:apksig:3.1.2:apksig-3.1.2.jar:40696a4559124d1d57873d208857eee059d48859239d569c7d18374ac644a8be', onlyIfNewer true
'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.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',
'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',
]
} }
ext.torBinaryDir = 'src/main/res/raw' task downloadTorBinaryArm(type: Download) {
ext.torVersion = '0.2.9.14' src 'https://briarproject.org/build/tor-0.2.7.6-arm.zip'
ext.geoipVersion = '2017-11-06' dest "$torBinaryDir/tor_arm.zip"
ext.torDownloadUrl = 'https://briarproject.org/build/' onlyIfNewer true
def torBinaries = [
"tor_arm" : '1710ea6c47b7f4c1a88bdf4858c7893837635db10e8866854eed8d61629f50e8',
"tor_arm_pie": '974e6949507db8fa2ea45231817c2c3677ed4ccf5488a2252317d744b0be1917',
"tor_x86" : '3a5e45b3f051fcda9353b098b7086e762ffe7ba9242f7d7c8bf6523faaa8b1e9',
"tor_x86_pie": 'd1d96d8ce1a4b68accf04850185780d10cd5563d3552f7e1f040f8ca32cb4e51',
"geoip" : '8239b98374493529a29096e45fc5877d4d6fdad0146ad8380b291f90d61484ea'
]
def verifyOrDeleteBinary(name, chksum, alreadyVerified) {
return tasks.create("verifyOrDeleteBinary${name}", VerifyOrDelete) {
src "${torBinaryDir}/${name}.zip"
algorithm 'SHA-256'
checksum chksum
result alreadyVerified
onlyIf {
src.exists()
}
}
} }
def downloadBinary(name, chksum, alreadyVerified) { task downloadTorBinaryArmPie(type: Download) {
return tasks.create([ src 'https://briarproject.org/build/tor-0.2.7.6-arm-pie.zip'
name: "downloadBinary${name}", dest "$torBinaryDir/tor_arm_pie.zip"
type: Download, onlyIfNewer true
dependsOn: verifyOrDeleteBinary(name, chksum, alreadyVerified)]) {
src "${torDownloadUrl}${name}.zip"
.replace('tor_', "tor-${torVersion}-")
.replace('geoip', "geoip-${geoipVersion}")
.replaceAll('_', '-')
dest "${torBinaryDir}/${name}.zip"
onlyIf {
!dest.exists()
}
}
} }
def verifyBinary(name, chksum) { task downloadTorBinaryX86(type: Download) {
boolean[] alreadyVerified = [false] src 'https://briarproject.org/build/tor-0.2.7.6-x86.zip'
return tasks.create([ dest "$torBinaryDir/tor_x86.zip"
name : "verifyBinary${name}", onlyIfNewer true
type : Verify, }
dependsOn: downloadBinary(name, chksum, alreadyVerified)]) {
src "${torBinaryDir}/${name}.zip" task downloadTorBinaryX86Pie(type: Download) {
algorithm 'SHA-256' src 'https://briarproject.org/build/tor-0.2.7.6-x86-pie.zip'
checksum chksum dest "$torBinaryDir/tor_x86_pie.zip"
onlyIf { onlyIfNewer true
!alreadyVerified[0] }
}
} task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
src "$torBinaryDir/geoip.zip"
algorithm 'SHA-256'
checksum '9bcdaf0a7ba0933735328d8ec466c25c25dbb459efc2bce9e55c774eabea5162'
}
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
src "$torBinaryDir/tor_arm.zip"
algorithm 'SHA-256'
checksum '83272962eda701cd5d74d2418651c4ff0f0b1dff51f558a292d1a1c42bf12146'
}
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
src "$torBinaryDir/tor_arm_pie.zip"
algorithm 'SHA-256'
checksum 'd0300d1e45de11ebb24ed62b9c492be9c2e88590b7822195ab38c7a76ffcf646'
}
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
src "$torBinaryDir/tor_x86.zip"
algorithm 'SHA-256'
checksum 'b8813d97b01ee1b9c9a4233c1b9bbe9f9f6b494ae6f9cbd84de8a3911911615e'
}
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
src "$torBinaryDir/tor_x86_pie.zip"
algorithm 'SHA-256'
checksum '9c66e765aa196dc089951a1b2140cc8290305c2fcbf365121f99e01a233baf4e'
} }
project.afterEvaluate { project.afterEvaluate {
torBinaries.every { name, checksum -> preBuild.dependsOn {
preBuild.dependsOn.add(verifyBinary(name, checksum)) [
} 'verifyTorGeoIp',
} 'verifyTorBinaryArm',
'verifyTorBinaryArmPie',
class VerifyOrDelete extends Verify { 'verifyTorBinaryX86',
'verifyTorBinaryX86Pie'
boolean[] result ]
@TaskAction
@Override
void verify() throws IOException, NoSuchAlgorithmException {
try {
super.verify()
result[0] = true
} catch (Exception e) {
println "${src} failed verification - deleting"
src.delete()
}
} }
} }

View File

@@ -8,10 +8,6 @@
-dontwarn dagger.** -dontwarn dagger.**
-dontnote dagger.** -dontnote dagger.**
-keep class net.i2p.crypto.eddsa.** { *; }
-keep class org.whispersystems.curve25519.** { *; }
-dontwarn sun.misc.Unsafe -dontwarn sun.misc.Unsafe
-dontnote com.google.common.** -dontnote com.google.common.**

View File

@@ -11,6 +11,8 @@
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_LOGS"/> <uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- Since API 23, this is needed to add contacts via Bluetooth -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application <application
android:allowBackup="false" android:allowBackup="false"

View File

@@ -13,8 +13,7 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevReporter; import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.Scheduler; import org.briarproject.bramble.plugin.droidtooth.DroidtoothPluginFactory;
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory; import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
import org.briarproject.bramble.plugin.tor.TorPluginFactory; import org.briarproject.bramble.plugin.tor.TorPluginFactory;
@@ -23,7 +22,6 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory; import javax.net.SocketFactory;
@@ -35,21 +33,19 @@ public class AndroidPluginModule {
@Provides @Provides
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor, PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
AndroidExecutor androidExecutor, SecureRandom random, AndroidExecutor androidExecutor, SecureRandom random,
SocketFactory torSocketFactory, BackoffFactory backoffFactory, SocketFactory torSocketFactory, BackoffFactory backoffFactory,
Application app, LocationUtils locationUtils, DevReporter reporter, Application app, LocationUtils locationUtils, DevReporter reporter,
EventBus eventBus) { EventBus eventBus) {
Context appContext = app.getApplicationContext(); Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth = DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor, androidExecutor, appContext, random, backoffFactory);
appContext, random, eventBus, backoffFactory); DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler, locationUtils, reporter, eventBus, torSocketFactory,
appContext, locationUtils, reporter, eventBus, backoffFactory);
torSocketFactory, backoffFactory);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
scheduler, backoffFactory, appContext); backoffFactory, appContext);
Collection<DuplexPluginFactory> duplex = final Collection<DuplexPluginFactory> duplex =
Arrays.asList(bluetooth, tor, lan); Arrays.asList(bluetooth, tor, lan);
@NotNullByDefault @NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() { PluginConfig pluginConfig = new PluginConfig() {

View File

@@ -1,209 +0,0 @@
package org.briarproject.bramble.plugin.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.AndroidUtils;
import java.io.Closeable;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private static final Logger LOG =
Logger.getLogger(AndroidBluetoothPlugin.class.getName());
private final AndroidExecutor androidExecutor;
private final Context appContext;
private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null;
// Non-null if the plugin started successfully
private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Backoff backoff,
DuplexPluginCallback callback, int maxLatency) {
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
maxLatency);
this.androidExecutor = androidExecutor;
this.appContext = appContext;
}
@Override
public void start() throws PluginException {
super.start();
// Listen for changes to the Bluetooth state
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED);
receiver = new BluetoothStateReceiver();
appContext.registerReceiver(receiver, filter);
}
@Override
public void stop() {
super.stop();
if (receiver != null) appContext.unregisterReceiver(receiver);
}
@Override
void initialiseAdapter() throws IOException {
// BluetoothAdapter.getDefaultAdapter() must be called on a thread
// with a message queue, so submit it to the AndroidExecutor
try {
adapter = androidExecutor.runOnBackgroundThread(
BluetoothAdapter::getDefaultAdapter).get();
} catch (InterruptedException | ExecutionException e) {
throw new IOException(e);
}
if (adapter == null)
throw new IOException("Bluetooth is not supported");
}
@Override
boolean isAdapterEnabled() {
return adapter != null && adapter.isEnabled();
}
@Override
void enableAdapter() {
if (adapter != null && !adapter.isEnabled()) {
if (adapter.enable()) {
LOG.info("Enabling Bluetooth");
wasEnabledByUs = true;
} else {
LOG.info("Could not enable Bluetooth");
}
}
}
@Override
void disableAdapterIfEnabledByUs() {
if (isAdapterEnabled() && wasEnabledByUs) {
if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth");
wasEnabledByUs = false;
}
}
@Override
void setEnabledByUs() {
wasEnabledByUs = true;
}
@Override
@Nullable
String getBluetoothAddress() {
String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
return address.isEmpty() ? null : address;
}
@Override
BluetoothServerSocket openServerSocket(String uuid) throws IOException {
return adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", UUID.fromString(uuid));
}
@Override
void tryToClose(@Nullable BluetoothServerSocket ss) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
DuplexTransportConnection acceptConnection(BluetoothServerSocket ss)
throws IOException {
return wrapSocket(ss.accept());
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
return new AndroidBluetoothTransportConnection(this,
connectionLimiter, s);
}
@Override
boolean isValidAddress(String address) {
return BluetoothAdapter.checkBluetoothAddress(address);
}
@Override
DuplexTransportConnection connectTo(String address, String uuid)
throws IOException {
BluetoothDevice d = adapter.getRemoteDevice(address);
UUID u = UUID.fromString(uuid);
BluetoothSocket s = null;
try {
s = d.createInsecureRfcommSocketToServiceRecord(u);
s.connect();
return wrapSocket(s);
} catch (IOException e) {
tryToClose(s);
throw e;
}
}
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
private class BluetoothStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
int state = intent.getIntExtra(EXTRA_STATE, 0);
if (state == STATE_ON) onAdapterEnabled();
else if (state == STATE_OFF) onAdapterDisabled();
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
if (scanMode == SCAN_MODE_NONE) {
LOG.info("Scan mode: None");
} else if (scanMode == SCAN_MODE_CONNECTABLE) {
LOG.info("Scan mode: Connectable");
} else if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
LOG.info("Scan mode: Discoverable");
}
}
}
}

View File

@@ -0,0 +1,567 @@
package org.briarproject.bramble.plugin.droidtooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.duplex.AbstractBluetoothPlugin;
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.system.AndroidExecutor;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.StringUtils;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class DroidtoothPlugin<C, S>
extends AbstractBluetoothPlugin<C, S>{
private static final Logger LOG =
Logger.getLogger(DroidtoothPlugin.class.getName());
private static final String FOUND =
"android.bluetooth.device.action.FOUND";
private static final String DISCOVERY_FINISHED =
"android.bluetooth.adapter.action.DISCOVERY_FINISHED";
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null;
private volatile BluetoothServerSocket socket = null;
// Non-null if the plugin started successfully
private volatile BluetoothAdapter adapter = null;
DroidtoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Backoff backoff,
DuplexPluginCallback callback, int maxLatency) {
super(ioExecutor, secureRandom, backoff, maxLatency, callback);
this.androidExecutor = androidExecutor;
this.appContext = appContext;
}
@Override
protected void close(S ss) throws IOException {
((BluetoothServerSocket)ss).close();
}
@Override
public void start() throws PluginException {
if (used.getAndSet(true)) throw new IllegalStateException();
// BluetoothAdapter.getDefaultAdapter() must be called on a thread
// with a message queue, so submit it to the AndroidExecutor
try {
adapter = androidExecutor.runOnBackgroundThread(
new Callable<BluetoothAdapter>() {
@Override
public BluetoothAdapter call() throws Exception {
return BluetoothAdapter.getDefaultAdapter();
}
}).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.warning("Interrupted while getting BluetoothAdapter");
throw new PluginException(e);
} catch (ExecutionException e) {
throw new PluginException(e);
}
if (adapter == null) {
LOG.info("Bluetooth is not supported");
throw new PluginException();
}
running = true;
// Listen for changes to the Bluetooth state
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED);
receiver = new BluetoothStateReceiver();
appContext.registerReceiver(receiver, filter);
// If Bluetooth is enabled, bind a socket
if (adapter.isEnabled()) {
bind();
} else {
// Enable Bluetooth if settings allow
if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) {
wasEnabledByUs = true;
if (adapter.enable()) LOG.info("Enabling Bluetooth");
else LOG.info("Could not enable Bluetooth");
} else {
LOG.info("Not enabling Bluetooth");
}
}
}
private void bind() {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
if (!isRunning()) return;
String address = AndroidUtils.getBluetoothAddress(appContext,
adapter);
if (LOG.isLoggable(INFO))
LOG.info("Local address " + scrubMacAddress(address));
if (!StringUtils.isNullOrEmpty(address)) {
// Advertise the Bluetooth address to contacts
TransportProperties p = new TransportProperties();
p.put(PROP_ADDRESS, address);
callback.mergeLocalProperties(p);
}
// Bind a server socket to accept connections from contacts
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", UUID.fromString(getUuid()));
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
return;
}
if (!isRunning()) {
tryToClose((S)ss);
return;
}
LOG.info("Socket bound");
socket = ss;
backoff.reset();
callback.transportEnabled();
acceptContactConnections();
}
});
}
private void acceptContactConnections() {
while (isRunning()) {
BluetoothSocket s;
try {
s = socket.accept();
} catch (IOException e) {
// This is expected when the socket is closed
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
return;
}
if (LOG.isLoggable(INFO)) {
String address = s.getRemoteDevice().getAddress();
LOG.info("Connection from " + scrubMacAddress(address));
}
backoff.reset();
callback.incomingConnectionCreated(wrapSocket(s));
}
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
return new DroidtoothTransportConnection(this, s);
}
@Override
public void stop() {
this.running = false;
if (receiver != null) appContext.unregisterReceiver(receiver);
tryToClose((S)socket);
// Disable Bluetooth if we enabled it and it's still enabled
if (wasEnabledByUs && adapter.isEnabled()) {
if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth");
}
}
@Override
public boolean isRunning() {
return running && adapter != null && adapter.isEnabled();
}
protected Runnable returnPollRunnable(final String address, final String uuid,
final ContactId c) {
return new Runnable() {
@Override
public void run() {
if (!running) return;
BluetoothSocket s = connect(address, uuid);
if (s != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, wrapSocket(s));
}
}
};
}
@Nullable
private BluetoothSocket connect(String address, String uuid) {
// Validate the address
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
if (LOG.isLoggable(WARNING))
// not scrubbing here to be able to figure out the problem
LOG.warning("Invalid address " + address);
return null;
}
// Validate the UUID
UUID u;
try {
u = UUID.fromString(uuid);
} catch (IllegalArgumentException e) {
if (LOG.isLoggable(WARNING)) LOG.warning("Invalid UUID " + uuid);
return null;
}
// Try to connect
BluetoothDevice d = adapter.getRemoteDevice(address);
BluetoothSocket s = null;
try {
s = d.createInsecureRfcommSocketToServiceRecord(u);
if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubMacAddress(address));
s.connect();
if (LOG.isLoggable(INFO))
LOG.info("Connected to " + scrubMacAddress(address));
return s;
} catch (IOException e) {
if (LOG.isLoggable(INFO)) {
LOG.info("Failed to connect to " + scrubMacAddress(address)
+ ": " + e);
}
tryToClose((S)s);
return null;
}
}
public DuplexTransportConnection connectToAddress(String address, String uuid) {
BluetoothSocket s = connect(address, uuid);
return s == null ? null : wrapSocket(s);
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
if (!isRunning()) return null;
// Use the invitation codes to generate the UUID
byte[] b = r.nextBytes(UUID_BYTES);
UUID uuid = UUID.nameUUIDFromBytes(b);
if (LOG.isLoggable(INFO)) LOG.info("Invitation UUID " + uuid);
// Bind a server socket for receiving invitation connections
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", uuid);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
// Create the background tasks
CompletionService<BluetoothSocket> complete =
new ExecutorCompletionService<>(ioExecutor);
List<Future<BluetoothSocket>> futures = new ArrayList<>();
if (alice) {
// Return the first connected socket
futures.add(complete.submit(new ListeningTask(ss)));
futures.add(complete.submit(new DiscoveryTask(uuid.toString())));
} else {
// Return the first socket with readable data
futures.add(complete.submit(new ReadableTask(
new ListeningTask(ss))));
futures.add(complete.submit(new ReadableTask(
new DiscoveryTask(uuid.toString()))));
}
BluetoothSocket chosen = null;
try {
Future<BluetoothSocket> f = complete.poll(timeout, MILLISECONDS);
if (f == null) return null; // No task completed within the timeout
chosen = f.get();
return new DroidtoothTransportConnection(this, chosen);
} catch (InterruptedException e) {
LOG.info("Interrupted while exchanging invitations");
Thread.currentThread().interrupt();
return null;
} catch (ExecutionException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} finally {
// Closing the socket will terminate the listener task
tryToClose((S)ss);
closeSockets(futures, chosen);
}
}
private void closeSockets(final List<Future<BluetoothSocket>> futures,
@Nullable final BluetoothSocket chosen) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
for (Future<BluetoothSocket> f : futures) {
try {
if (f.cancel(true)) {
LOG.info("Cancelled task");
} else {
BluetoothSocket s = f.get();
if (s != null && s != chosen) {
LOG.info("Closing unwanted socket");
s.close();
}
}
} catch (InterruptedException e) {
LOG.info("Interrupted while closing sockets");
return;
} catch (ExecutionException | IOException e) {
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
}
}
}
});
}
@Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
if (!isRunning()) return null;
// There's no point listening if we can't discover our own address
String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
if (address.isEmpty()) return null;
// No truncation necessary because COMMIT_LENGTH = 16
UUID uuid = UUID.nameUUIDFromBytes(commitment);
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
// Bind a server socket for receiving invitation connections
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", uuid);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
BdfList descriptor = new BdfList();
descriptor.add(TRANSPORT_ID_BLUETOOTH);
descriptor.add(StringUtils.macToBytes(address));
return new BluetoothKeyAgreementListener(descriptor, ss);
}
private class BluetoothStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
int state = intent.getIntExtra(EXTRA_STATE, 0);
if (state == STATE_ON) {
LOG.info("Bluetooth enabled");
bind();
} else if (state == STATE_OFF) {
LOG.info("Bluetooth disabled");
tryToClose((S)socket);
}
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
if (scanMode == SCAN_MODE_NONE) {
LOG.info("Scan mode: None");
} else if (scanMode == SCAN_MODE_CONNECTABLE) {
LOG.info("Scan mode: Connectable");
} else if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
LOG.info("Scan mode: Discoverable");
}
}
}
private class DiscoveryTask implements Callable<BluetoothSocket> {
private final String uuid;
private DiscoveryTask(String uuid) {
this.uuid = uuid;
}
@Override
public BluetoothSocket call() throws Exception {
// Repeat discovery until we connect or get interrupted
while (true) {
// Discover nearby devices
LOG.info("Discovering nearby devices");
List<String> addresses = discoverDevices();
if (addresses.isEmpty()) {
LOG.info("No devices discovered");
continue;
}
// Connect to any device with the right UUID
for (String address : addresses) {
BluetoothSocket s = connect(address, uuid);
if (s != null) {
LOG.info("Outgoing connection");
return s;
}
}
}
}
private List<String> discoverDevices() throws InterruptedException {
IntentFilter filter = new IntentFilter();
filter.addAction(FOUND);
filter.addAction(DISCOVERY_FINISHED);
DiscoveryReceiver disco = new DiscoveryReceiver();
appContext.registerReceiver(disco, filter);
LOG.info("Starting discovery");
adapter.startDiscovery();
return disco.waitForAddresses();
}
}
private static class DiscoveryReceiver extends BroadcastReceiver {
private final CountDownLatch finished = new CountDownLatch(1);
private final List<String> addresses = new CopyOnWriteArrayList<>();
@Override
public void onReceive(Context ctx, Intent intent) {
String action = intent.getAction();
if (action.equals(DISCOVERY_FINISHED)) {
LOG.info("Discovery finished");
ctx.unregisterReceiver(this);
finished.countDown();
} else if (action.equals(FOUND)) {
BluetoothDevice d = intent.getParcelableExtra(EXTRA_DEVICE);
if (LOG.isLoggable(INFO)) {
LOG.info("Discovered device: " +
scrubMacAddress(d.getAddress()));
}
addresses.add(d.getAddress());
}
}
private List<String> waitForAddresses() throws InterruptedException {
finished.await();
List<String> shuffled = new ArrayList<>(addresses);
Collections.shuffle(shuffled);
return shuffled;
}
}
private static class ListeningTask implements Callable<BluetoothSocket> {
private final BluetoothServerSocket serverSocket;
private ListeningTask(BluetoothServerSocket serverSocket) {
this.serverSocket = serverSocket;
}
@Override
public BluetoothSocket call() throws IOException {
BluetoothSocket s = serverSocket.accept();
LOG.info("Incoming connection");
return s;
}
}
private static class ReadableTask implements Callable<BluetoothSocket> {
private final Callable<BluetoothSocket> connectionTask;
private ReadableTask(Callable<BluetoothSocket> connectionTask) {
this.connectionTask = connectionTask;
}
@Override
public BluetoothSocket call() throws Exception {
BluetoothSocket s = connectionTask.call();
InputStream in = s.getInputStream();
while (in.available() == 0) {
LOG.info("Waiting for data");
Thread.sleep(1000);
}
LOG.info("Data available");
return s;
}
}
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
private final BluetoothServerSocket ss;
private BluetoothKeyAgreementListener(BdfList descriptor,
BluetoothServerSocket ss) {
super(descriptor);
this.ss = ss;
}
@Override
public Callable<KeyAgreementConnection> listen() {
return new Callable<KeyAgreementConnection>() {
@Override
public KeyAgreementConnection call() throws IOException {
BluetoothSocket s = ss.accept();
if (LOG.isLoggable(INFO))
LOG.info(ID.getString() + ": Incoming connection");
return new KeyAgreementConnection(
new DroidtoothTransportConnection(
DroidtoothPlugin.this, s), ID);
}
};
}
@Override
public void close() {
try {
ss.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
}
}

View File

@@ -1,8 +1,7 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.droidtooth;
import android.content.Context; import android.content.Context;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -21,7 +20,7 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class AndroidBluetoothPluginFactory implements DuplexPluginFactory { public class DroidtoothPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
@@ -32,18 +31,15 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final Context appContext; private final Context appContext;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final EventBus eventBus;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
public AndroidBluetoothPluginFactory(Executor ioExecutor, public DroidtoothPluginFactory(Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext, AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, EventBus eventBus, SecureRandom secureRandom, BackoffFactory backoffFactory) {
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.appContext = appContext; this.appContext = appContext;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.eventBus = eventBus;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
} }
@@ -59,14 +55,9 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
@Override @Override
public DuplexPlugin createPlugin(DuplexPluginCallback callback) { public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
BluetoothConnectionLimiter connectionLimiter =
new BluetoothConnectionLimiterImpl();
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin( return new DroidtoothPlugin(ioExecutor, androidExecutor, appContext,
connectionLimiter, ioExecutor, androidExecutor, appContext,
secureRandom, backoff, callback, MAX_LATENCY); secureRandom, backoff, callback, MAX_LATENCY);
eventBus.addListener(plugin);
return plugin;
} }
} }

View File

@@ -1,4 +1,4 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.droidtooth;
import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket;
@@ -11,17 +11,12 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@NotNullByDefault @NotNullByDefault
class AndroidBluetoothTransportConnection class DroidtoothTransportConnection extends AbstractDuplexTransportConnection {
extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionManager;
private final BluetoothSocket socket; private final BluetoothSocket socket;
AndroidBluetoothTransportConnection(Plugin plugin, DroidtoothTransportConnection(Plugin plugin, BluetoothSocket socket) {
BluetoothConnectionLimiter connectionManager,
BluetoothSocket socket) {
super(plugin); super(plugin);
this.connectionManager = connectionManager;
this.socket = socket; this.socket = socket;
} }
@@ -37,10 +32,6 @@ class AndroidBluetoothTransportConnection
@Override @Override
protected void closeConnection(boolean exception) throws IOException { protected void closeConnection(boolean exception) throws IOException {
try { socket.close();
socket.close();
} finally {
connectionManager.connectionClosed(this);
}
} }
} }

View File

@@ -5,84 +5,37 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.net.SocketFactory;
import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Context.WIFI_SERVICE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.SECONDS;
@NotNullByDefault @NotNullByDefault
class AndroidLanTcpPlugin extends LanTcpPlugin { class AndroidLanTcpPlugin extends LanTcpPlugin {
// See android.net.wifi.WifiManager
private static final String WIFI_AP_STATE_CHANGED_ACTION =
"android.net.wifi.WIFI_AP_STATE_CHANGED";
private static final int WIFI_AP_STATE_ENABLED = 13;
private static final byte[] WIFI_AP_ADDRESS_BYTES =
{(byte) 192, (byte) 168, 43, 1};
private static final InetAddress WIFI_AP_ADDRESS;
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(AndroidLanTcpPlugin.class.getName()); Logger.getLogger(AndroidLanTcpPlugin.class.getName());
static {
try {
WIFI_AP_ADDRESS = InetAddress.getByAddress(WIFI_AP_ADDRESS_BYTES);
} catch (UnknownHostException e) {
// Should only be thrown if the address has an illegal length
throw new AssertionError(e);
}
}
private final ScheduledExecutorService scheduler;
private final Context appContext; private final Context appContext;
private final ConnectivityManager connectivityManager;
@Nullable
private final WifiManager wifiManager;
@Nullable @Nullable
private volatile BroadcastReceiver networkStateReceiver = null; private volatile BroadcastReceiver networkStateReceiver = null;
private volatile SocketFactory socketFactory;
AndroidLanTcpPlugin(Executor ioExecutor, ScheduledExecutorService scheduler, AndroidLanTcpPlugin(Executor ioExecutor, Backoff backoff,
Backoff backoff, Context appContext, DuplexPluginCallback callback, Context appContext, DuplexPluginCallback callback, int maxLatency,
int maxLatency, int maxIdleTime) { int maxIdleTime) {
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime); super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
this.scheduler = scheduler;
this.appContext = appContext; this.appContext = appContext;
ConnectivityManager connectivityManager = (ConnectivityManager)
appContext.getSystemService(CONNECTIVITY_SERVICE);
if (connectivityManager == null) throw new AssertionError();
this.connectivityManager = connectivityManager;
wifiManager = (WifiManager) appContext.getApplicationContext()
.getSystemService(WIFI_SERVICE);
socketFactory = SocketFactory.getDefault();
} }
@Override @Override
@@ -91,9 +44,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
running = true; running = true;
// Register to receive network status events // Register to receive network status events
networkStateReceiver = new NetworkStateReceiver(); networkStateReceiver = new NetworkStateReceiver();
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION);
filter.addAction(CONNECTIVITY_ACTION);
filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
appContext.registerReceiver(networkStateReceiver, filter); appContext.registerReceiver(networkStateReceiver, filter);
} }
@@ -105,92 +56,21 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
tryToClose(socket); tryToClose(socket);
} }
@Override
protected Socket createSocket() throws IOException {
return socketFactory.createSocket();
}
@Override
protected Collection<InetAddress> getLocalIpAddresses() {
// If the device doesn't have wifi, don't open any sockets
if (wifiManager == null) return emptyList();
// If we're connected to a wifi network, use that network
WifiInfo info = wifiManager.getConnectionInfo();
if (info != null && info.getIpAddress() != 0)
return singletonList(intToInetAddress(info.getIpAddress()));
// If we're running an access point, return its address
if (super.getLocalIpAddresses().contains(WIFI_AP_ADDRESS))
return singletonList(WIFI_AP_ADDRESS);
// No suitable addresses
return emptyList();
}
private InetAddress intToInetAddress(int ip) {
byte[] ipBytes = new byte[4];
ipBytes[0] = (byte) (ip & 0xFF);
ipBytes[1] = (byte) ((ip >> 8) & 0xFF);
ipBytes[2] = (byte) ((ip >> 16) & 0xFF);
ipBytes[3] = (byte) ((ip >> 24) & 0xFF);
try {
return InetAddress.getByAddress(ipBytes);
} catch (UnknownHostException e) {
// Should only be thrown if address has illegal length
throw new AssertionError(e);
}
}
// On API 21 and later, a socket that is not created with the wifi
// network's socket factory may try to connect via another network
private SocketFactory getSocketFactory() {
if (SDK_INT < 21) return SocketFactory.getDefault();
for (Network net : connectivityManager.getAllNetworks()) {
NetworkInfo info = connectivityManager.getNetworkInfo(net);
if (info != null && info.getType() == TYPE_WIFI)
return net.getSocketFactory();
}
LOG.warning("Could not find suitable socket factory");
return SocketFactory.getDefault();
}
private class NetworkStateReceiver extends BroadcastReceiver { private class NetworkStateReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context ctx, Intent i) { public void onReceive(Context ctx, Intent i) {
if (!running) return; if (!running) return;
if (isApEnabledEvent(i)) { Object o = ctx.getSystemService(CONNECTIVITY_SERVICE);
// The state change may be broadcast before the AP address is ConnectivityManager cm = (ConnectivityManager) o;
// visible, so delay handling the event NetworkInfo net = cm.getActiveNetworkInfo();
scheduler.schedule(this::handleConnectivityChange, 1, SECONDS); if (net != null && net.getType() == TYPE_WIFI && net.isConnected()) {
} else { LOG.info("Connected to Wi-Fi");
handleConnectivityChange();
}
}
private void handleConnectivityChange() {
if (!running) return;
Collection<InetAddress> addrs = getLocalIpAddresses();
if (addrs.contains(WIFI_AP_ADDRESS)) {
LOG.info("Providing wifi hotspot");
// There's no corresponding Network object and thus no way
// to get a suitable socket factory, so we won't be able to
// make outgoing connections on API 21+ if another network
// has internet access
socketFactory = SocketFactory.getDefault();
if (socket == null || socket.isClosed()) bind(); if (socket == null || socket.isClosed()) bind();
} else if (addrs.isEmpty()) { } else {
LOG.info("Not connected to wifi"); LOG.info("Not connected to Wi-Fi");
socketFactory = SocketFactory.getDefault();
tryToClose(socket); tryToClose(socket);
} else {
LOG.info("Connected to wifi");
socketFactory = getSocketFactory();
if (socket == null || socket.isClosed()) bind();
} }
} }
private boolean isApEnabledEvent(Intent i) {
return WIFI_AP_STATE_CHANGED_ACTION.equals(i.getAction()) &&
i.getIntExtra(EXTRA_WIFI_STATE, 0) == WIFI_AP_STATE_ENABLED;
}
} }
} }

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@@ -28,15 +27,12 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor; private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
private final Context appContext; private final Context appContext;
public AndroidLanTcpPluginFactory(Executor ioExecutor, public AndroidLanTcpPluginFactory(Executor ioExecutor,
ScheduledExecutorService scheduler, BackoffFactory backoffFactory, BackoffFactory backoffFactory, Context appContext) {
Context appContext) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
this.appContext = appContext; this.appContext = appContext;
} }
@@ -55,7 +51,7 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(DuplexPluginCallback callback) { public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
return new AndroidLanTcpPlugin(ioExecutor, scheduler, backoff, return new AndroidLanTcpPlugin(ioExecutor, backoff, appContext,
appContext, callback, MAX_LATENCY, MAX_IDLE_TIME); callback, MAX_LATENCY, MAX_IDLE_TIME);
} }
} }

View File

@@ -16,8 +16,8 @@ import android.os.PowerManager;
import net.freehaven.tor.control.EventHandler; import net.freehaven.tor.control.EventHandler;
import net.freehaven.tor.control.TorControlConnection; import net.freehaven.tor.control.TorControlConnection;
import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
@@ -56,14 +56,10 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner; import java.util.Scanner;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
@@ -74,15 +70,10 @@ import javax.net.SocketFactory;
import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
import static android.content.Context.POWER_SERVICE; import static android.content.Context.POWER_SERVICE;
import static android.content.Intent.ACTION_SCREEN_OFF;
import static android.content.Intent.ACTION_SCREEN_ON;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
@@ -94,13 +85,13 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WIFI; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WIFI;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION;
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion; import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class TorPlugin implements DuplexPlugin, EventHandler, EventListener { class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final String PROP_ONION = "onion";
private static final String[] EVENTS = { private static final String[] EVENTS = {
"CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR" "CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR"
}; };
@@ -110,8 +101,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(TorPlugin.class.getName()); Logger.getLogger(TorPlugin.class.getName());
private final Executor ioExecutor, connectionStatusExecutor; private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final Context appContext; private final Context appContext;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final DevReporter reporter; private final DevReporter reporter;
@@ -124,8 +114,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final File torDirectory, torFile, geoIpFile, configFile; private final File torDirectory, torFile, geoIpFile, configFile;
private final File doneFile, cookieFile; private final File doneFile, cookieFile;
private final PowerManager.WakeLock wakeLock; private final PowerManager.WakeLock wakeLock;
private final AtomicReference<Future<?>> connectivityCheck =
new AtomicReference<>();
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false; private volatile boolean running = false;
@@ -134,13 +122,12 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private volatile TorControlConnection controlConnection = null; private volatile TorControlConnection controlConnection = null;
private volatile BroadcastReceiver networkStateReceiver = null; private volatile BroadcastReceiver networkStateReceiver = null;
TorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler, TorPlugin(Executor ioExecutor, Context appContext,
Context appContext, LocationUtils locationUtils, LocationUtils locationUtils, DevReporter reporter,
DevReporter reporter, SocketFactory torSocketFactory, SocketFactory torSocketFactory, Backoff backoff,
Backoff backoff, DuplexPluginCallback callback, DuplexPluginCallback callback, String architecture, int maxLatency,
String architecture, int maxLatency, int maxIdleTime) { int maxIdleTime) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.appContext = appContext; this.appContext = appContext;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.reporter = reporter; this.reporter = reporter;
@@ -162,12 +149,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
cookieFile = new File(torDirectory, ".tor/control_auth_cookie"); cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
Object o = appContext.getSystemService(POWER_SERVICE); Object o = appContext.getSystemService(POWER_SERVICE);
PowerManager pm = (PowerManager) o; PowerManager pm = (PowerManager) o;
// This tag will prevent Huawei's powermanager from killing us. wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "TorPlugin");
wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "LocationManagerService");
wakeLock.setReferenceCounted(false); wakeLock.setReferenceCounted(false);
// Don't execute more than one connection status check at a time
connectionStatusExecutor = new PoliteExecutor("TorPlugin",
ioExecutor, 1);
} }
@Override @Override
@@ -220,11 +203,11 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
Scanner stdout = new Scanner(torProcess.getInputStream()); Scanner stdout = new Scanner(torProcess.getInputStream());
Scanner stderr = new Scanner(torProcess.getErrorStream()); Scanner stderr = new Scanner(torProcess.getErrorStream());
while (stdout.hasNextLine() || stderr.hasNextLine()) { while (stdout.hasNextLine() || stderr.hasNextLine()){
if (stdout.hasNextLine()) { if(stdout.hasNextLine()) {
LOG.info(stdout.nextLine()); LOG.info(stdout.nextLine());
} }
if (stderr.hasNextLine()) { if(stderr.hasNextLine()){
LOG.info(stderr.nextLine()); LOG.info(stderr.nextLine());
} }
} }
@@ -273,11 +256,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
// Register to receive network status events // Register to receive network status events
networkStateReceiver = new NetworkStateReceiver(); networkStateReceiver = new NetworkStateReceiver();
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION);
filter.addAction(CONNECTIVITY_ACTION);
filter.addAction(ACTION_SCREEN_ON);
filter.addAction(ACTION_SCREEN_OFF);
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
appContext.registerReceiver(networkStateReceiver, filter); appContext.registerReceiver(networkStateReceiver, filter);
// Bind a server socket to receive incoming hidden service connections // Bind a server socket to receive incoming hidden service connections
bind(); bind();
@@ -390,45 +369,57 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void sendDevReports() { private void sendDevReports() {
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
// TODO: Trigger this with a TransportEnabledEvent @Override
File reportDir = AndroidUtils.getReportDir(appContext); public void run() {
reporter.sendReports(reportDir); // TODO: Trigger this with a TransportEnabledEvent
File reportDir = AndroidUtils.getReportDir(appContext);
reporter.sendReports(reportDir);
}
}); });
} }
private void bind() { private void bind() {
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
// If there's already a port number stored in config, reuse it @Override
String portString = callback.getSettings().get(PREF_TOR_PORT); public void run() {
int port; // If there's already a port number stored in config, reuse it
if (StringUtils.isNullOrEmpty(portString)) port = 0; String portString = callback.getSettings().get(PREF_TOR_PORT);
else port = Integer.parseInt(portString); int port;
// Bind a server socket to receive connections from Tor if (StringUtils.isNullOrEmpty(portString)) port = 0;
ServerSocket ss = null; else port = Integer.parseInt(portString);
try { // Bind a server socket to receive connections from Tor
ss = new ServerSocket(); ServerSocket ss = null;
ss.bind(new InetSocketAddress("127.0.0.1", port)); try {
} catch (IOException e) { ss = new ServerSocket();
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); ss.bind(new InetSocketAddress("127.0.0.1", port));
tryToClose(ss); } catch (IOException e) {
return; if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
tryToClose(ss);
return;
}
if (!running) {
tryToClose(ss);
return;
}
socket = ss;
// Store the port number
final String localPort = String.valueOf(ss.getLocalPort());
Settings s = new Settings();
s.put(PREF_TOR_PORT, localPort);
callback.mergeSettings(s);
// Create a hidden service if necessary
ioExecutor.execute(new Runnable() {
@Override
public void run() {
publishHiddenService(localPort);
}
});
backoff.reset();
// Accept incoming hidden service connections from Tor
acceptContactConnections(ss);
} }
if (!running) {
tryToClose(ss);
return;
}
socket = ss;
// Store the port number
String localPort = String.valueOf(ss.getLocalPort());
Settings s = new Settings();
s.put(PREF_TOR_PORT, localPort);
callback.mergeSettings(s);
// Create a hidden service if necessary
ioExecutor.execute(() -> publishHiddenService(localPort));
backoff.reset();
// Accept incoming hidden service connections from Tor
acceptContactConnections(ss);
}); });
} }
@@ -548,21 +539,20 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
public void poll(Collection<ContactId> connected) { public void poll(Collection<ContactId> connected) {
if (!isRunning()) return; if (!isRunning()) return;
backoff.increment(); backoff.increment();
Map<ContactId, TransportProperties> remote = // TODO: Pass properties to connectAndCallBack()
callback.getRemoteProperties(); for (ContactId c : callback.getRemoteProperties().keySet())
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) { if (!connected.contains(c)) connectAndCallBack(c);
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
}
} }
private void connectAndCallBack(ContactId c, TransportProperties p) { private void connectAndCallBack(final ContactId c) {
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
if (!isRunning()) return; @Override
DuplexTransportConnection d = createConnection(p); public void run() {
if (d != null) { DuplexTransportConnection d = createConnection(c);
backoff.reset(); if (d != null) {
callback.outgoingConnectionCreated(c, d); backoff.reset();
callback.outgoingConnectionCreated(c, d);
}
} }
}); });
} }
@@ -570,11 +560,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null; if (!isRunning()) return null;
return createConnection(callback.getRemoteProperties(c)); TransportProperties p = callback.getRemoteProperties().get(c);
} if (p == null) return null;
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
String onion = p.get(PROP_ONION); String onion = p.get(PROP_ONION);
if (StringUtils.isNullOrEmpty(onion)) return null; if (StringUtils.isNullOrEmpty(onion)) return null;
if (!ONION.matcher(onion).matches()) { if (!ONION.matcher(onion).matches()) {
@@ -602,6 +589,17 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
} }
@Override
public boolean supportsInvitations() {
return false;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
throw new UnsupportedOperationException();
}
@Override @Override
public boolean supportsKeyAgreement() { public boolean supportsKeyAgreement() {
return false; return false;
@@ -614,7 +612,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) { byte[] commitment, BdfList descriptor, long timeout) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@@ -638,8 +636,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public void orConnStatus(String status, String orName) { public void orConnStatus(String status, String orName) {
if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status); if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status);
if (status.equals("CLOSED") || status.equals("FAILED"))
updateConnectionStatus(); // Check whether we've lost connectivity
} }
@Override @Override
@@ -679,7 +675,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public void onEvent(int event, @Nullable String path) { public void onEvent(int event, String path) {
stopWatching(); stopWatching();
latch.countDown(); latch.countDown();
} }
@@ -697,65 +693,60 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void updateConnectionStatus() { private void updateConnectionStatus() {
connectionStatusExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
if (!running) return; @Override
Object o = appContext.getSystemService(CONNECTIVITY_SERVICE); public void run() {
ConnectivityManager cm = (ConnectivityManager) o; if (!running) return;
NetworkInfo net = cm.getActiveNetworkInfo();
boolean online = net != null && net.isConnected();
boolean wifi = online && net.getType() == TYPE_WIFI;
String country = locationUtils.getCurrentCountry();
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
country);
Settings s = callback.getSettings();
int network = s.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_ALWAYS);
if (LOG.isLoggable(INFO)) { Object o = appContext.getSystemService(CONNECTIVITY_SERVICE);
LOG.info("Online: " + online + ", wifi: " + wifi); ConnectivityManager cm = (ConnectivityManager) o;
if ("".equals(country)) LOG.info("Country code unknown"); NetworkInfo net = cm.getActiveNetworkInfo();
else LOG.info("Country code: " + country); boolean online = net != null && net.isConnected();
} boolean wifi = online && net.getType() == TYPE_WIFI;
String country = locationUtils.getCurrentCountry();
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
country);
Settings s = callback.getSettings();
int network = s.getInt(PREF_TOR_NETWORK,
PREF_TOR_NETWORK_ALWAYS);
try { if (LOG.isLoggable(INFO)) {
if (!online) { LOG.info("Online: " + online + ", wifi: " + wifi);
LOG.info("Disabling network, device is offline"); if ("".equals(country)) LOG.info("Country code unknown");
enableNetwork(false); else LOG.info("Country code: " + country);
} else if (blocked) { }
LOG.info("Disabling network, country is blocked");
enableNetwork(false); try {
} else if (network == PREF_TOR_NETWORK_NEVER if (!online) {
|| (network == PREF_TOR_NETWORK_WIFI && !wifi)) { LOG.info("Disabling network, device is offline");
LOG.info("Disabling network due to data setting"); enableNetwork(false);
enableNetwork(false); } else if (blocked) {
} else { LOG.info("Disabling network, country is blocked");
LOG.info("Enabling network"); enableNetwork(false);
enableNetwork(true); } else if (network == PREF_TOR_NETWORK_NEVER
|| (network == PREF_TOR_NETWORK_WIFI && !wifi)) {
LOG.info("Disabling network due to data setting");
enableNetwork(false);
} else {
LOG.info("Enabling network");
enableNetwork(true);
}
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
} }
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
}); });
} }
private void scheduleConnectionStatusUpdate() {
Future<?> newConnectivityCheck =
scheduler.schedule(this::updateConnectionStatus, 1, MINUTES);
Future<?> oldConnectivityCheck =
connectivityCheck.getAndSet(newConnectivityCheck);
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(false);
}
private class NetworkStateReceiver extends BroadcastReceiver { private class NetworkStateReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context ctx, Intent i) { public void onReceive(Context ctx, Intent i) {
if (!running) return; if (!running) return;
String action = i.getAction(); if (CONNECTIVITY_ACTION.equals(i.getAction())) {
if (LOG.isLoggable(INFO)) LOG.info("Received broadcast " + action); LOG.info("Detected connectivity change");
updateConnectionStatus(); updateConnectionStatus();
if (ACTION_SCREEN_ON.equals(action)
|| ACTION_SCREEN_OFF.equals(action)) {
scheduleConnectionStatusUpdate();
} }
} }
} }
@@ -778,7 +769,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private synchronized void enableNetwork(boolean enable) { private synchronized void enableNetwork(boolean enable) {
networkEnabled = enable; networkEnabled = enable;
if (!enable) circuitBuilt = false; circuitBuilt = false;
} }
private synchronized boolean isConnected() { private synchronized boolean isConnected() {

View File

@@ -17,7 +17,6 @@ import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.AndroidUtils;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@@ -37,7 +36,6 @@ public class TorPluginFactory implements DuplexPluginFactory {
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor; private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final Context appContext; private final Context appContext;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final DevReporter reporter; private final DevReporter reporter;
@@ -45,13 +43,11 @@ public class TorPluginFactory implements DuplexPluginFactory {
private final SocketFactory torSocketFactory; private final SocketFactory torSocketFactory;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
public TorPluginFactory(Executor ioExecutor, public TorPluginFactory(Executor ioExecutor, Context appContext,
ScheduledExecutorService scheduler, Context appContext,
LocationUtils locationUtils, DevReporter reporter, LocationUtils locationUtils, DevReporter reporter,
EventBus eventBus, SocketFactory torSocketFactory, EventBus eventBus, SocketFactory torSocketFactory,
BackoffFactory backoffFactory) { BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.appContext = appContext; this.appContext = appContext;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.reporter = reporter; this.reporter = reporter;
@@ -93,9 +89,9 @@ public class TorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorPlugin plugin = new TorPlugin(ioExecutor, scheduler, appContext, TorPlugin plugin = new TorPlugin(ioExecutor, appContext, locationUtils,
locationUtils, reporter, torSocketFactory, backoff, callback, reporter, torSocketFactory, backoff, callback, architecture,
architecture, MAX_LATENCY, MAX_IDLE_TIME); MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
import org.briarproject.bramble.util.IoUtils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -22,12 +21,12 @@ class TorTransportConnection extends AbstractDuplexTransportConnection {
@Override @Override
protected InputStream getInputStream() throws IOException { protected InputStream getInputStream() throws IOException {
return IoUtils.getInputStream(socket); return socket.getInputStream();
} }
@Override @Override
protected OutputStream getOutputStream() throws IOException { protected OutputStream getOutputStream() throws IOException {
return IoUtils.getOutputStream(socket); return socket.getOutputStream();
} }
@Override @Override

View File

@@ -27,11 +27,14 @@ class AndroidExecutorImpl implements AndroidExecutor {
@Inject @Inject
AndroidExecutorImpl(Application app) { AndroidExecutorImpl(Application app) {
uiHandler = new Handler(app.getApplicationContext().getMainLooper()); uiHandler = new Handler(app.getApplicationContext().getMainLooper());
loop = () -> { loop = new Runnable() {
Looper.prepare(); @Override
backgroundHandler = new Handler(); public void run() {
startLatch.countDown(); Looper.prepare();
Looper.loop(); backgroundHandler = new Handler();
startLatch.countDown();
Looper.loop();
}
}; };
} }

View File

@@ -1,39 +1,30 @@
apply plugin: 'java-library' apply plugin: 'java'
sourceCompatibility = 1.8 sourceCompatibility = 1.6
targetCompatibility = 1.8 targetCompatibility = 1.6
apply plugin: 'witness' apply plugin: 'witness'
dependencies { dependencies {
implementation "com.google.dagger:dagger:2.0.2" compile "com.google.dagger:dagger:2.0.2"
implementation 'com.google.code.findbugs:jsr305:3.0.2' compile 'com.google.dagger:dagger-compiler:2.0.2'
compile 'com.google.code.findbugs:jsr305:3.0.1'
testImplementation 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testImplementation "org.jmock:jmock:2.8.2" testCompile "org.jmock:jmock:2.8.1"
testImplementation "org.jmock:jmock-junit4:2.8.2" testCompile "org.jmock:jmock-junit4:2.8.1"
testImplementation "org.jmock:jmock-legacy:2.8.2" testCompile "org.jmock:jmock-legacy:2.8.1"
testImplementation "org.hamcrest:hamcrest-library:1.3" testCompile "org.hamcrest:hamcrest-library:1.3"
testImplementation "org.hamcrest:hamcrest-core:1.3" testCompile "org.hamcrest:hamcrest-core:1.3"
} }
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9', 'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
'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.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
] ]
} }
@@ -48,8 +39,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts { artifacts {
testOutput jarTest 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)
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.api; package org.briarproject.bramble.api;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
@@ -54,12 +53,6 @@ public class Bytes implements Comparable<Bytes> {
return aBytes.length - bBytes.length; return aBytes.length - bBytes.length;
} }
@Override
public String toString() {
return getClass().getSimpleName() +
"(" + StringUtils.toHexString(getBytes()) + ")";
}
public static class BytesComparator implements Comparator<Bytes> { public static class BytesComparator implements Comparator<Bytes> {
@Override @Override

View File

@@ -1,101 +0,0 @@
package org.briarproject.bramble.api;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
@NotNullByDefault
public class Multiset<T> {
private final Map<T, Integer> map = new HashMap<>();
private int total = 0;
/**
* Returns how many items the multiset contains in total.
*/
public int getTotal() {
return total;
}
/**
* Returns how many unique items the multiset contains.
*/
public int getUnique() {
return map.size();
}
/**
* Returns how many of the given item the multiset contains.
*/
public int getCount(T t) {
Integer count = map.get(t);
return count == null ? 0 : count;
}
/**
* Adds the given item to the multiset and returns how many of the item
* the multiset now contains.
*/
public int add(T t) {
Integer count = map.get(t);
if (count == null) count = 0;
map.put(t, count + 1);
total++;
return count + 1;
}
/**
* Removes the given item from the multiset and returns how many of the
* item the multiset now contains.
* @throws NoSuchElementException if the item is not in the multiset.
*/
public int remove(T t) {
Integer count = map.get(t);
if (count == null) throw new NoSuchElementException();
if (count == 1) map.remove(t);
else map.put(t, count - 1);
total--;
return count - 1;
}
/**
* Removes all occurrences of the given item from the multiset.
*/
public int removeAll(T t) {
Integer count = map.remove(t);
if (count == null) return 0;
total -= count;
return count;
}
/**
* Returns true if the multiset contains any occurrences of the given item.
*/
public boolean contains(T t) {
return map.containsKey(t);
}
/**
* Removes all items from the multiset.
*/
public void clear() {
map.clear();
total = 0;
}
/**
* Returns the set of unique items the multiset contains. The returned set
* is unmodifiable.
*/
public Set<T> keySet() {
return Collections.unmodifiableSet(map.keySet());
}
}

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api;
import java.io.IOException;
/**
* An exception that indicates an unrecoverable version mismatch.
*/
public class UnsupportedVersionException extends IOException {
}

View File

@@ -23,7 +23,7 @@ public class BdfMessageContext {
} }
public BdfMessageContext(BdfDictionary dictionary) { public BdfMessageContext(BdfDictionary dictionary) {
this(dictionary, Collections.emptyList()); this(dictionary, Collections.<MessageId>emptyList());
} }
public BdfDictionary getDictionary() { public BdfDictionary getDictionary() {

View File

@@ -5,10 +5,7 @@ import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
@@ -90,30 +87,16 @@ public interface ClientHelper {
BdfDictionary toDictionary(byte[] b, int off, int len) BdfDictionary toDictionary(byte[] b, int off, int len)
throws FormatException; throws FormatException;
BdfDictionary toDictionary(TransportProperties transportProperties);
BdfDictionary toDictionary(Map<TransportId, TransportProperties> map);
BdfList toList(byte[] b, int off, int len) throws FormatException; BdfList toList(byte[] b, int off, int len) throws FormatException;
BdfList toList(byte[] b) throws FormatException; BdfList toList(byte[] b) throws FormatException;
BdfList toList(Message m) throws FormatException; BdfList toList(Message m) throws FormatException;
BdfList toList(Author a);
byte[] sign(String label, BdfList toSign, byte[] privateKey) byte[] sign(String label, BdfList toSign, byte[] privateKey)
throws FormatException, GeneralSecurityException; throws FormatException, GeneralSecurityException;
void verifySignature(byte[] signature, String label, BdfList signed, void verifySignature(String label, byte[] sig, byte[] publicKey,
byte[] publicKey) throws FormatException, GeneralSecurityException; BdfList signed) throws FormatException, GeneralSecurityException;
Author parseAndValidateAuthor(BdfList author) throws FormatException;
TransportProperties parseAndValidateTransportProperties(
BdfDictionary properties) throws FormatException;
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
BdfDictionary properties) throws FormatException;
} }

View File

@@ -12,19 +12,18 @@ public interface ContactGroupFactory {
/** /**
* Creates a group that is not shared with any contacts. * Creates a group that is not shared with any contacts.
*/ */
Group createLocalGroup(ClientId clientId, int majorVersion); Group createLocalGroup(ClientId clientId);
/** /**
* Creates a group for the given client to share with the given contact. * Creates a group for the given client to share with the given contact.
*/ */
Group createContactGroup(ClientId clientId, int majorVersion, Group createContactGroup(ClientId clientId, Contact contact);
Contact contact);
/** /**
* Creates a group for the given client to share between the given authors * Creates a group for the given client to share between the given authors
* identified by their AuthorIds. * identified by their AuthorIds.
*/ */
Group createContactGroup(ClientId clientId, int majorVersion, Group createContactGroup(ClientId clientId, AuthorId authorId1,
AuthorId authorId1, AuthorId authorId2); AuthorId authorId2);
} }

View File

@@ -12,32 +12,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@NotNullByDefault @NotNullByDefault
public interface ContactExchangeTask { public interface ContactExchangeTask {
/**
* The current version of the contact exchange protocol.
*/
byte PROTOCOL_VERSION = 1;
/**
* Label for deriving Alice's header key from the master secret.
*/
String ALICE_KEY_LABEL =
"org.briarproject.bramble.contact/ALICE_HEADER_KEY";
/**
* Label for deriving Bob's header key from the master secret.
*/
String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY";
/**
* Label for deriving Alice's key binding nonce from the master secret.
*/
String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE";
/**
* Label for deriving Bob's key binding nonce from the master secret.
*/
String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE";
/** /**
* Exchanges contact information with a remote peer. * Exchanges contact information with a remote peer.
*/ */

View File

@@ -6,7 +6,7 @@ import javax.annotation.concurrent.Immutable;
/** /**
* Type-safe wrapper for an integer that uniquely identifies a contact within * Type-safe wrapper for an integer that uniquely identifies a contact within
* the scope of the local device. * the scope of a single node.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
@@ -14,36 +13,26 @@ import java.util.Collection;
public interface ContactManager { public interface ContactManager {
/** /**
* Registers a hook to be called whenever a contact is added or removed. * Registers a hook to be called whenever a contact is added.
* This method should be called before
* {@link LifecycleManager#startServices(String)}.
*/ */
void registerContactHook(ContactHook hook); void registerAddContactHook(AddContactHook hook);
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Registers a hook to be called whenever a contact is removed.
* derives and stores transport keys for each transport, and returns an ID */
* for the contact. void registerRemoveContactHook(RemoveContactHook hook);
*
* @param alice true if the local party is Alice /**
* Stores a contact within the given transaction associated with the given
* local and remote pseudonyms, and returns an ID for the contact.
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified, SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException; boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms
* and returns an ID for the contact.
*/
ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException;
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact associated with the given local and remote pseudonyms,
* derives and stores transport keys for each transport, and returns an ID * and returns an ID for the contact.
* for the contact.
*
* @param alice true if the local party is Alice
*/ */
ContactId addContact(Author remote, AuthorId local, ContactId addContact(Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified, SecretKey master, long timestamp, boolean alice, boolean verified,
@@ -105,10 +94,11 @@ public interface ContactManager {
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId) boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException; throws DbException;
interface ContactHook { interface AddContactHook {
void addingContact(Transaction txn, Contact c) throws DbException; void addingContact(Transaction txn, Contact c) throws DbException;
}
interface RemoveContactHook {
void removingContact(Transaction txn, Contact c) throws DbException; void removingContact(Transaction txn, Contact c) throws DbException;
} }
} }

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api.contact;
/**
* Record types for the contact exchange protocol.
*/
public interface RecordTypes {
byte CONTACT_INFO = 0;
}

View File

@@ -1,17 +1,17 @@
package org.briarproject.bramble.api.crypto; package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.SecureRandom; import java.security.SecureRandom;
import javax.annotation.Nullable;
@NotNullByDefault
public interface CryptoComponent { public interface CryptoComponent {
SecretKey generateSecretKey(); SecretKey generateSecretKey();
PseudoRandom getPseudoRandom(int seed1, int seed2);
SecureRandom getSecureRandom(); SecureRandom getSecureRandom();
KeyPair generateAgreementKeyPair(); KeyPair generateAgreementKeyPair();
@@ -24,84 +24,162 @@ public interface CryptoComponent {
KeyParser getMessageKeyParser(); KeyParser getMessageKeyParser();
/** Generates a random invitation code. */
int generateBTInvitationCode();
/** /**
* Derives another secret key from the given secret key. * Derives a confirmation code from the given master secret.
* * @param alice whether the code is for use by Alice or Bob.
* @param label a namespaced label indicating the purpose of the derived
* key, to prevent it from being repurposed or colliding with a key derived
* for another purpose
*/ */
SecretKey deriveKey(String label, SecretKey k, byte[]... inputs); int deriveBTConfirmationCode(SecretKey master, boolean alice);
/**
* Derives a stream header key from the given master secret.
* @param alice whether the key is for use by Alice or Bob.
*/
SecretKey deriveHeaderKey(SecretKey master, boolean alice);
/**
* Derives a message authentication code key from the given master secret.
* @param alice whether the key is for use by Alice or Bob.
*/
SecretKey deriveMacKey(SecretKey master, boolean alice);
/**
* Derives a nonce from the given master secret for one of the parties to
* sign.
* @param alice whether the nonce is for use by Alice or Bob.
*/
byte[] deriveSignatureNonce(SecretKey master, boolean alice);
/**
* Derives a commitment to the provided public key.
* <p/>
* Part of BQP.
*
* @param publicKey the public key
* @return the commitment to the provided public key.
*/
byte[] deriveKeyCommitment(byte[] publicKey);
/** /**
* Derives a common shared secret from two public keys and one of the * Derives a common shared secret from two public keys and one of the
* corresponding private keys. * corresponding private keys.
* <p/>
* Part of BQP.
* *
* @param label a namespaced label indicating the purpose of this shared * @param theirPublicKey the ephemeral public key of the remote party
* secret, to prevent it from being repurposed or colliding with a shared * @param ourKeyPair our ephemeral keypair
* secret derived for another purpose * @param alice true if ourKeyPair belongs to Alice
* @param theirPublicKey the public key of the remote party
* @param ourKeyPair the key pair of the local party
* @return the shared secret * @return the shared secret
* @throws GeneralSecurityException
*/ */
SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey, SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
KeyPair ourKeyPair, byte[]... inputs) boolean alice) throws GeneralSecurityException;
throws GeneralSecurityException;
/** /**
* Signs the given byte[] with the given private key. * Derives the content of a confirmation record.
* <p/>
* Part of BQP.
* *
* @param label a namespaced label indicating the purpose of this * @param sharedSecret the common shared secret
* signature, to prevent it from being repurposed or colliding with a * @param theirPayload the commit payload from the remote party
* signature created for another purpose * @param ourPayload the commit payload we sent
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral keypair
* @param alice true if ourKeyPair belongs to Alice
* @param aliceRecord true if the confirmation record is for use by Alice
* @return the confirmation record
*/
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload,
byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice, boolean aliceRecord);
/**
* Derives a master secret from the given shared secret.
* <p/>
* Part of BQP.
*
* @param sharedSecret the common shared secret
* @return the master secret
*/
SecretKey deriveMasterSecret(SecretKey sharedSecret);
/**
* Derives a master secret from two public keys and one of the corresponding
* private keys.
* <p/>
* This is a helper method that calls
* deriveMasterSecret(deriveSharedSecret(theirPublicKey, ourKeyPair, alice))
*
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral keypair
* @param alice true if ourKeyPair belongs to Alice
* @return the shared secret
* @throws GeneralSecurityException
*/
SecretKey deriveMasterSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice) throws GeneralSecurityException;
/**
* Derives initial transport keys for the given transport in the given
* rotation period from the given master secret.
* @param alice whether the keys are for use by Alice or Bob.
*/
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
long rotationPeriod, boolean alice);
/**
* Rotates the given transport keys to the given rotation period. If the
* keys are for a future rotation period they are not rotated.
*/
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/** Encodes the pseudo-random tag that is used to recognise a stream. */
void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber);
/**
* Signs the given byte[] with the given PrivateKey.
*
* @param label A label specific to this signature
* to ensure that the signature cannot be repurposed
*/ */
byte[] sign(String label, byte[] toSign, byte[] privateKey) byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException; throws GeneralSecurityException;
/** /**
* Verifies that the given signature is valid for the signed data * Verifies that the given signature is valid for the signedData
* and the given public key. * and the given publicKey.
* *
* @param label a namespaced label indicating the purpose of this * @param label A label that was specific to this signature
* signature, to prevent it from being repurposed or colliding with a * to ensure that the signature cannot be repurposed
* signature created for another purpose
* @return true if the signature was valid, false otherwise. * @return true if the signature was valid, false otherwise.
*/ */
boolean verifySignature(byte[] signature, String label, byte[] signed, boolean verify(String label, byte[] signedData, byte[] publicKey,
byte[] publicKey) throws GeneralSecurityException; byte[] signature) throws GeneralSecurityException;
/** /**
* Returns the hash of the given inputs. The inputs are unambiguously * Returns the hash of the given inputs. The inputs are unambiguously
* combined by prefixing each input with its length. * combined by prefixing each input with its length.
* *
* @param label a namespaced label indicating the purpose of this hash, to * @param label A label specific to this hash to ensure that hashes
* prevent it from being repurposed or colliding with a hash created for * calculated for distinct purposes don't collide.
* another purpose
*/ */
byte[] hash(String label, byte[]... inputs); byte[] hash(String label, byte[]... inputs);
/**
* Returns the length of hashes produced by
* the {@link CryptoComponent#hash(String, byte[]...)} method.
*/
int getHashLength();
/** /**
* Returns a message authentication code with the given key over the * Returns a message authentication code with the given key over the
* given inputs. The inputs are unambiguously combined by prefixing each * given inputs. The inputs are unambiguously combined by prefixing each
* input with its length. * input with its length.
*
* @param label a namespaced label indicating the purpose of this MAC, to
* prevent it from being repurposed or colliding with a MAC created for
* another purpose
*/ */
byte[] mac(String label, SecretKey macKey, byte[]... inputs); byte[] mac(SecretKey macKey, byte[]... inputs);
/**
* Verifies that the given message authentication code is valid for the
* given secret key and inputs.
*
* @param label a namespaced label indicating the purpose of this MAC, to
* prevent it from being repurposed or colliding with a MAC created for
* another purpose
* @return true if the MAC was valid, false otherwise.
*/
boolean verifyMac(byte[] mac, String label, SecretKey macKey,
byte[]... inputs);
/** /**
* Encrypts and authenticates the given plaintext so it can be written to * Encrypts and authenticates the given plaintext so it can be written to
@@ -117,7 +195,6 @@ public interface CryptoComponent {
* given password. Returns null if the ciphertext cannot be decrypted and * given password. Returns null if the ciphertext cannot be decrypted and
* authenticated (for example, if the password is wrong). * authenticated (for example, if the password is wrong).
*/ */
@Nullable
byte[] decryptWithPassword(byte[] ciphertext, String password); byte[] decryptWithPassword(byte[] ciphertext, String password);
/** /**

View File

@@ -1,25 +0,0 @@
package org.briarproject.bramble.api.crypto;
public interface CryptoConstants {
/**
* The maximum length of an agreement public key in bytes.
*/
int MAX_AGREEMENT_PUBLIC_KEY_BYTES = 32;
/**
* The maximum length of a signature public key in bytes.
*/
int MAX_SIGNATURE_PUBLIC_KEY_BYTES = 32;
/**
* The maximum length of a signature in bytes.
*/
int MAX_SIGNATURE_BYTES = 64;
/**
* The length of a MAC in bytes.
*/
int MAC_BYTES = SecretKey.LENGTH;
}

View File

@@ -1,50 +0,0 @@
package org.briarproject.bramble.api.crypto;
/**
* Crypto operations for the key agreement protocol - see
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BQP.md
*/
public interface KeyAgreementCrypto {
/**
* Hash label for public key commitment.
*/
String COMMIT_LABEL = "org.briarproject.bramble.keyagreement/COMMIT";
/**
* Key derivation label for confirmation record.
*/
String CONFIRMATION_KEY_LABEL =
"org.briarproject.bramble.keyagreement/CONFIRMATION_KEY";
/**
* MAC label for confirmation record.
*/
String CONFIRMATION_MAC_LABEL =
"org.briarproject.bramble.keyagreement/CONFIRMATION_MAC";
/**
* Derives a commitment to the provided public key.
*
* @param publicKey the public key
* @return the commitment to the provided public key.
*/
byte[] deriveKeyCommitment(PublicKey publicKey);
/**
* Derives the content of a confirmation record.
*
* @param sharedSecret the common shared secret
* @param theirPayload the key exchange payload of the remote party
* @param ourPayload the key exchange payload of the local party
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral key pair of the local party
* @param alice true if the local party is Alice
* @param aliceRecord true if the confirmation record is for use by Alice
* @return the confirmation record
*/
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload,
PublicKey theirPublicKey, KeyPair ourKeyPair,
boolean alice, boolean aliceRecord);
}

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; 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 @Immutable
@NotNullByDefault @NotNullByDefault

View File

@@ -0,0 +1,12 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* A deterministic pseudo-random number generator.
*/
@NotNullByDefault
public interface PseudoRandom {
byte[] nextBytes(int bytes);
}

View File

@@ -14,9 +14,8 @@ public interface StreamDecrypterFactory {
StreamDecrypter createStreamDecrypter(InputStream in, StreamContext ctx); StreamDecrypter createStreamDecrypter(InputStream in, StreamContext ctx);
/** /**
* Creates a {@link StreamDecrypter} for decrypting a contact exchange * Creates a {@link StreamDecrypter} for decrypting an invitation stream.
* stream.
*/ */
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in, StreamDecrypter createInvitationStreamDecrypter(InputStream in,
SecretKey headerKey); SecretKey headerKey);
} }

View File

@@ -14,9 +14,8 @@ public interface StreamEncrypterFactory {
StreamEncrypter createStreamEncrypter(OutputStream out, StreamContext ctx); StreamEncrypter createStreamEncrypter(OutputStream out, StreamContext ctx);
/** /**
* Creates a {@link StreamEncrypter} for encrypting a contact exchange * Creates a {@link StreamEncrypter} for encrypting an invitation stream.
* stream.
*/ */
StreamEncrypter createContactExchangeStreamDecrypter(OutputStream out, StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
SecretKey headerKey); SecretKey headerKey);
} }

View File

@@ -1,33 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.TransportKeys;
/**
* Crypto operations for the transport security protocol - see
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BTP.md
*/
public interface TransportCrypto {
/**
* Derives initial transport keys for the given transport in the given
* rotation period from the given master secret.
*
* @param alice whether the keys are for use by Alice or Bob.
* @param active whether the keys are usable for outgoing streams.
*/
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
long rotationPeriod, boolean alice, boolean active);
/**
* Rotates the given transport keys to the given rotation period. If the
* keys are for the given period or any later period they are not rotated.
*/
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/**
* Encodes the pseudo-random tag that is used to recognise a stream.
*/
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber);
}

View File

@@ -4,14 +4,11 @@ import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.concurrent.ConcurrentSkipListMap;
import java.util.TreeMap;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
public class BdfDictionary extends TreeMap<String, Object> {
public static final Object NULL_VALUE = new Object(); public static final Object NULL_VALUE = new Object();
@@ -24,9 +21,9 @@ public class BdfDictionary extends TreeMap<String, Object> {
* ); * );
* </pre> * </pre>
*/ */
public static BdfDictionary of(Entry<String, ?>... entries) { public static BdfDictionary of(Entry<String, Object>... entries) {
BdfDictionary d = new BdfDictionary(); BdfDictionary d = new BdfDictionary();
for (Entry<String, ?> e : entries) d.put(e.getKey(), e.getValue()); for (Entry<String, Object> e : entries) d.put(e.getKey(), e.getValue());
return d; return d;
} }
@@ -34,7 +31,7 @@ public class BdfDictionary extends TreeMap<String, Object> {
super(); super();
} }
public BdfDictionary(Map<String, ?> m) { public BdfDictionary(Map<String, Object> m) {
super(m); super(m);
} }

View File

@@ -3,17 +3,15 @@ package org.briarproject.bramble.api.data;
import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Vector;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE; import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
@NotThreadSafe public class BdfList extends Vector<Object> {
public class BdfList extends ArrayList<Object> {
/** /**
* Factory method for constructing lists inline. * Factory method for constructing lists inline.

View File

@@ -0,0 +1,11 @@
package org.briarproject.bramble.api.data;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
@NotNullByDefault
public interface ObjectReader<T> {
T readObject(BdfReader r) throws IOException;
}

View File

@@ -1,7 +0,0 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when the database uses a newer schema than the current code.
*/
public class DataTooNewException extends DbException {
}

View File

@@ -1,8 +0,0 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when the database uses an older schema than the current code and
* cannot be migrated.
*/
public class DataTooOldException extends DbException {
}

View File

@@ -18,8 +18,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.transport.KeySet; import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
@@ -38,13 +37,8 @@ public interface DatabaseComponent {
/** /**
* Opens the database and returns true if the database already existed. * Opens the database and returns true if the database already existed.
*
* @throws DataTooNewException if the data uses a newer schema than the
* current code
* @throws DataTooOldException if the data uses an older schema than the
* current code and cannot be migrated
*/ */
boolean open(@Nullable MigrationListener listener) throws DbException; boolean open() throws DbException;
/** /**
* Waits for any open transactions to finish and closes the database. * Waits for any open transactions to finish and closes the database.
@@ -104,17 +98,10 @@ public interface DatabaseComponent {
throws DbException; throws DbException;
/** /**
* Stores the given transport keys, optionally binding them to the given * Stores transport keys for a newly added contact.
* contact, and returns a key set ID.
*/ */
KeySetId addTransportKeys(Transaction txn, @Nullable ContactId c, void addTransportKeys(Transaction txn, ContactId c, TransportKeys k)
TransportKeys k) throws DbException; 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 * Returns true if the database contains the given contact for the given
@@ -135,9 +122,8 @@ public interface DatabaseComponent {
throws DbException; throws DbException;
/** /**
* Deletes the message with the given ID. Unlike * Deletes the message with the given ID. The message ID and any other
* {@link #removeMessage(Transaction, MessageId)}, the message ID, * associated data are not deleted.
* dependencies, metadata, and any other associated state are not deleted.
*/ */
void deleteMessage(Transaction txn, MessageId m) throws DbException; void deleteMessage(Transaction txn, MessageId m) throws DbException;
@@ -241,8 +227,7 @@ public interface DatabaseComponent {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<Group> getGroups(Transaction txn, ClientId c, int majorVersion) Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException;
throws DbException;
/** /**
* Returns the given group's visibility to the given contact, or * Returns the given group's visibility to the given contact, or
@@ -268,38 +253,31 @@ public interface DatabaseComponent {
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException; Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
/** /**
* Returns the IDs of all delivered messages in the given group. * Returns the IDs of any messages that need to be validated by the given
* client.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<MessageId> getMessageIds(Transaction txn, GroupId g) Collection<MessageId> getMessagesToValidate(Transaction txn, ClientId c)
throws DbException;
/**
* Returns the IDs of any messages that need to be validated.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToValidate(Transaction txn)
throws DbException; throws DbException;
/** /**
* Returns the IDs of any messages that are pending delivery due to * Returns the IDs of any messages that are valid but pending delivery due
* dependencies on other messages. * to dependencies on other messages for the given client.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<MessageId> getPendingMessages(Transaction txn) Collection<MessageId> getPendingMessages(Transaction txn, ClientId c)
throws DbException; throws DbException;
/** /**
* Returns the IDs of any messages that have shared dependents but have * Returns the IDs of any messages from the given client
* not yet been shared themselves. * that have a shared dependent, but are still not shared themselves.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<MessageId> getMessagesToShare(Transaction txn) Collection<MessageId> getMessagesToShare(Transaction txn,
throws DbException; ClientId c) throws DbException;
/** /**
* Returns the message with the given ID, in serialised form, or null if * Returns the message with the given ID, in serialised form, or null if
@@ -319,9 +297,9 @@ public interface DatabaseComponent {
throws DbException; throws DbException;
/** /**
* Returns the metadata for any delivered messages in the given group with * Returns the metadata for any messages in the given group with metadata
* metadata that matches all entries in the given query. If the query is * that matches all entries in the given query. If the query is empty, the
* empty, the metadata for all delivered messages is returned. * metadata for all messages is returned.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
@@ -337,8 +315,8 @@ public interface DatabaseComponent {
throws DbException; throws DbException;
/** /**
* Returns the metadata for the given delivered or pending message. * Returns the metadata for the given delivered and pending message.
* This is only meant to be used by the ValidationManager. * This is meant to be only used by the ValidationManager
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
@@ -346,8 +324,8 @@ public interface DatabaseComponent {
throws DbException; throws DbException;
/** /**
* Returns the status of all delivered messages in the given group with * Returns the status of all messages in the given group with respect to
* respect to the given contact. * the given contact.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
@@ -356,8 +334,12 @@ public interface DatabaseComponent {
/** /**
* Returns the IDs and states of all dependencies of the given message. * Returns the IDs and states of all dependencies of the given message.
* For missing dependencies and dependencies in other groups, the state * Missing dependencies have the state
* {@link State UNKNOWN} is returned. * {@link ValidationManager.State UNKNOWN}.
* Dependencies in other groups have the state
* {@link ValidationManager.State INVALID}.
* Note that these states are not set on the dependencies themselves; the
* returned states should only be taken in the context of the given message.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
@@ -365,9 +347,9 @@ public interface DatabaseComponent {
throws DbException; throws DbException;
/** /**
* Returns the IDs and states of all dependents of the given message. * Returns all IDs of messages that depend on the given message.
* Dependents in other groups are not returned. If the given message is * Messages in other groups that declare a dependency on the given message
* missing, no dependents are returned. * will be returned even though such dependencies are invalid.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
@@ -382,24 +364,14 @@ public interface DatabaseComponent {
State getMessageState(Transaction txn, MessageId m) throws DbException; State getMessageState(Transaction txn, MessageId m) throws DbException;
/** /**
* Returns the status of the given delivered message with respect to the * Returns the status of the given message with respect to the given
* given contact. * contact.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m) MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
throws DbException; throws DbException;
/*
* Returns the next time (in milliseconds since the Unix epoch) when a
* message is due to be sent to the given contact. The returned value may
* be zero if a message is due to be sent immediately, or Long.MAX_VALUE if
* no messages are scheduled to be sent.
* <p/>
* Read-only.
*/
long getNextSendTime(Transaction txn, ContactId c) throws DbException;
/** /**
* Returns all settings in the given namespace. * Returns all settings in the given namespace.
* <p/> * <p/>
@@ -412,14 +384,15 @@ public interface DatabaseComponent {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<KeySet> getTransportKeys(Transaction txn, TransportId t) Map<ContactId, TransportKeys> getTransportKeys(Transaction txn,
throws DbException; TransportId t) throws DbException;
/** /**
* Increments the outgoing stream counter for the given transport keys. * Increments the outgoing stream counter for the given contact and
* transport in the given rotation period .
*/ */
void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k) void incrementStreamCounter(Transaction txn, ContactId c, TransportId t,
throws DbException; long rotationPeriod) throws DbException;
/** /**
* Merges the given metadata with the existing metadata for the given * Merges the given metadata with the existing metadata for the given
@@ -479,22 +452,11 @@ public interface DatabaseComponent {
*/ */
void removeLocalAuthor(Transaction txn, AuthorId a) throws DbException; void removeLocalAuthor(Transaction txn, AuthorId a) throws DbException;
/**
* Removes a message (and all associated state) from the database.
*/
void removeMessage(Transaction txn, MessageId m) throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */
void removeTransport(Transaction txn, TransportId t) throws DbException; void removeTransport(Transaction txn, TransportId t) throws DbException;
/**
* Removes the given transport keys from the database.
*/
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
throws DbException;
/** /**
* Marks the given contact as verified. * Marks the given contact as verified.
*/ */
@@ -530,21 +492,15 @@ public interface DatabaseComponent {
Collection<MessageId> dependencies) throws DbException; Collection<MessageId> dependencies) throws DbException;
/** /**
* Sets the reordering window for the given key set and transport in the * Sets the reordering window for the given contact and transport in the
* given rotation period. * given rotation period.
*/ */
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t, void setReorderingWindow(Transaction txn, ContactId c, TransportId t,
long rotationPeriod, long base, byte[] bitmap) throws DbException; long rotationPeriod, long base, byte[] bitmap) throws DbException;
/**
* Marks the given transport keys as usable for outgoing streams.
*/
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
throws DbException;
/** /**
* Stores the given transport keys, deleting any keys they have replaced. * Stores the given transport keys, deleting any keys they have replaced.
*/ */
void updateTransportKeys(Transaction txn, Collection<KeySet> keys) void updateTransportKeys(Transaction txn,
throws DbException; Map<ContactId, TransportKeys> keys) throws DbException;
} }

View File

@@ -1,11 +1,11 @@
package org.briarproject.bramble.api.db; package org.briarproject.bramble.api.db;
import java.util.TreeMap; import java.util.Hashtable;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.ThreadSafe;
@NotThreadSafe @ThreadSafe
public class Metadata extends TreeMap<String, byte[]> { public class Metadata extends Hashtable<String, byte[]> {
/** /**
* Special value to indicate that a key is being removed. * Special value to indicate that a key is being removed.

View File

@@ -1,11 +0,0 @@
package org.briarproject.bramble.api.db;
public interface MigrationListener {
/**
* This is called when a migration is started while opening the database.
* It will be called once for each migration being applied.
*/
void onMigrationRun();
}

View File

@@ -45,7 +45,7 @@ public class Transaction {
* committed. * committed.
*/ */
public void attach(Event e) { public void attach(Event e) {
if (events == null) events = new ArrayList<>(); if (events == null) events = new ArrayList<Event>();
events.add(e); events.add(e);
} }

View File

@@ -1,13 +1,11 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import java.io.UnsupportedEncodingException;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
/** /**
* A pseudonym for a user. * A pseudonym for a user.
*/ */
@@ -19,25 +17,20 @@ public class Author {
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
} }
/**
* The current version of the author structure.
*/
public static final int FORMAT_VERSION = 1;
private final AuthorId id; private final AuthorId id;
private final int formatVersion;
private final String name; private final String name;
private final byte[] publicKey; private final byte[] publicKey;
public Author(AuthorId id, int formatVersion, String name, public Author(AuthorId id, String name, byte[] publicKey) {
byte[] publicKey) { int length;
int nameLength = StringUtils.toUtf8(name).length; try {
if (nameLength == 0 || nameLength > MAX_AUTHOR_NAME_LENGTH) length = name.getBytes("UTF-8").length;
throw new IllegalArgumentException(); } catch (UnsupportedEncodingException e) {
if (publicKey.length == 0 || publicKey.length > MAX_PUBLIC_KEY_LENGTH) throw new RuntimeException(e);
}
if (length == 0 || length > AuthorConstants.MAX_AUTHOR_NAME_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
this.id = id; this.id = id;
this.formatVersion = formatVersion;
this.name = name; this.name = name;
this.publicKey = publicKey; this.publicKey = publicKey;
} }
@@ -49,13 +42,6 @@ public class Author {
return id; return id;
} }
/**
* Returns the version of the author structure used to create the author.
*/
public int getFormatVersion() {
return formatVersion;
}
/** /**
* Returns the author's name. * Returns the author's name.
*/ */

View File

@@ -1,8 +1,5 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_BYTES;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
public interface AuthorConstants { public interface AuthorConstants {
/** /**
@@ -11,14 +8,26 @@ public interface AuthorConstants {
int MAX_AUTHOR_NAME_LENGTH = 50; int MAX_AUTHOR_NAME_LENGTH = 50;
/** /**
* The maximum length of a public key in bytes. This applies to the * The maximum length of a public key in bytes.
* signature algorithm used by the current {@link Author format version}. * <p>
* Public keys use SEC1 format: 0x04 x y, where x and y are unsigned
* big-endian integers.
* <p>
* For a 256-bit elliptic curve, the maximum length is 2 * 256 / 8 + 1.
*/ */
int MAX_PUBLIC_KEY_LENGTH = MAX_SIGNATURE_PUBLIC_KEY_BYTES; int MAX_PUBLIC_KEY_LENGTH = 65;
/** /**
* The maximum length of a signature in bytes. This applies to the * The maximum length of a signature in bytes.
* signature algorithm used by the current {@link Author format version}. * <p>
* A signature is an ASN.1 DER sequence containing two integers, r and s.
* The format is 0x30 len1 0x02 len2 r 0x02 len3 s, where len1 is
* len(0x02 len2 r 0x02 len3 s) as a DER length, len2 is len(r) as a DER
* length, len3 is len(s) as a DER length, and r and s are signed
* big-endian integers of minimal length.
* <p>
* For a 256-bit elliptic curve, the lengths are one byte each, so the
* maximum length is 2 * 256 / 8 + 8.
*/ */
int MAX_SIGNATURE_LENGTH = MAX_SIGNATURE_BYTES; int MAX_SIGNATURE_LENGTH = 72;
} }

View File

@@ -5,27 +5,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface AuthorFactory { public interface AuthorFactory {
/**
* Creates an author with the current format version and the given name and
* public key.
*/
Author createAuthor(String name, byte[] publicKey); Author createAuthor(String name, byte[] publicKey);
/**
* Creates an author with the given format version, name and public key.
*/
Author createAuthor(int formatVersion, String name, byte[] publicKey);
/**
* Creates a local author with the current format version and the given
* name and keys.
*/
LocalAuthor createLocalAuthor(String name, byte[] publicKey, LocalAuthor createLocalAuthor(String name, byte[] publicKey,
byte[] privateKey); byte[] privateKey);
/**
* Creates a local author with the given format version, name and keys.
*/
LocalAuthor createLocalAuthor(int formatVersion, String name,
byte[] publicKey, byte[] privateKey);
} }

View File

@@ -16,7 +16,7 @@ public class AuthorId extends UniqueId {
/** /**
* Label for hashing authors to calculate their identities. * Label for hashing authors to calculate their identities.
*/ */
public static final String LABEL = "org.briarproject.bramble/AUTHOR_ID"; public static final String LABEL = "org.briarproject.bramble.AUTHOR_ID";
public AuthorId(byte[] id) { public AuthorId(byte[] id) {
super(id); super(id);

View File

@@ -14,9 +14,9 @@ public class LocalAuthor extends Author {
private final byte[] privateKey; private final byte[] privateKey;
private final long created; private final long created;
public LocalAuthor(AuthorId id, int formatVersion, String name, public LocalAuthor(AuthorId id, String name, byte[] publicKey,
byte[] publicKey, byte[] privateKey, long created) { byte[] privateKey, long created) {
super(id, formatVersion, name, publicKey); super(id, name, publicKey);
this.privateKey = privateKey; this.privateKey = privateKey;
this.created = created; this.created = created;
} }

View File

@@ -0,0 +1,20 @@
package org.briarproject.bramble.api.invitation;
public interface InvitationConstants {
/**
* The connection timeout in milliseconds.
*/
long CONNECTION_TIMEOUT = 60 * 1000;
/**
* The confirmation timeout in milliseconds.
*/
long CONFIRMATION_TIMEOUT = 60 * 1000;
/**
* The number of bits in an invitation or confirmation code. Codes must fit
* into six decimal digits.
*/
int CODE_BITS = 19;
}

View File

@@ -0,0 +1,47 @@
package org.briarproject.bramble.api.invitation;
/**
* An interface for receiving updates about the state of an
* {@link InvitationTask}.
*/
public interface InvitationListener {
/** Called if a connection to the remote peer is established. */
void connectionSucceeded();
/**
* Called if a connection to the remote peer cannot be established. This
* indicates that the protocol has ended unsuccessfully.
*/
void connectionFailed();
/** Called if key agreement with the remote peer succeeds. */
void keyAgreementSucceeded(int localCode, int remoteCode);
/**
* Called if key agreement with the remote peer fails or the connection is
* lost. This indicates that the protocol has ended unsuccessfully.
*/
void keyAgreementFailed();
/** Called if the remote peer's confirmation check succeeds. */
void remoteConfirmationSucceeded();
/**
* Called if remote peer's confirmation check fails or the connection is
* lost. This indicates that the protocol has ended unsuccessfully.
*/
void remoteConfirmationFailed();
/**
* Called if the exchange of pseudonyms succeeds. This indicates that the
* protocol has ended successfully.
*/
void pseudonymExchangeSucceeded(String remoteName);
/**
* Called if the exchange of pseudonyms fails or the connection is lost.
* This indicates that the protocol has ended unsuccessfully.
*/
void pseudonymExchangeFailed();
}

View File

@@ -0,0 +1,85 @@
package org.briarproject.bramble.api.invitation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A snapshot of the state of an {@link InvitationTask}.
*/
@Immutable
@NotNullByDefault
public class InvitationState {
private final int localInvitationCode, remoteInvitationCode;
private final int localConfirmationCode, remoteConfirmationCode;
private final boolean connected, connectionFailed;
private final boolean localCompared, remoteCompared;
private final boolean localMatched, remoteMatched;
@Nullable
private final String contactName;
public InvitationState(int localInvitationCode, int remoteInvitationCode,
int localConfirmationCode, int remoteConfirmationCode,
boolean connected, boolean connectionFailed, boolean localCompared,
boolean remoteCompared, boolean localMatched,
boolean remoteMatched, @Nullable String contactName) {
this.localInvitationCode = localInvitationCode;
this.remoteInvitationCode = remoteInvitationCode;
this.localConfirmationCode = localConfirmationCode;
this.remoteConfirmationCode = remoteConfirmationCode;
this.connected = connected;
this.connectionFailed = connectionFailed;
this.localCompared = localCompared;
this.remoteCompared = remoteCompared;
this.localMatched = localMatched;
this.remoteMatched = remoteMatched;
this.contactName = contactName;
}
public int getLocalInvitationCode() {
return localInvitationCode;
}
public int getRemoteInvitationCode() {
return remoteInvitationCode;
}
public int getLocalConfirmationCode() {
return localConfirmationCode;
}
public int getRemoteConfirmationCode() {
return remoteConfirmationCode;
}
public boolean getConnected() {
return connected;
}
public boolean getConnectionFailed() {
return connectionFailed;
}
public boolean getLocalCompared() {
return localCompared;
}
public boolean getRemoteCompared() {
return remoteCompared;
}
public boolean getLocalMatched() {
return localMatched;
}
public boolean getRemoteMatched() {
return remoteMatched;
}
@Nullable
public String getContactName() {
return contactName;
}
}

View File

@@ -0,0 +1,38 @@
package org.briarproject.bramble.api.invitation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* A task for exchanging invitations with a remote peer.
*/
@NotNullByDefault
public interface InvitationTask {
/**
* Adds a listener to be informed of state changes and returns the
* task's current state.
*/
InvitationState addListener(InvitationListener l);
/**
* Removes the given listener.
*/
void removeListener(InvitationListener l);
/**
* Asynchronously starts the connection process.
*/
void connect();
/**
* Asynchronously informs the remote peer that the local peer's
* confirmation codes matched.
*/
void localConfirmationSucceeded();
/**
* Asynchronously informs the remote peer that the local peer's
* confirmation codes did not match.
*/
void localConfirmationFailed();
}

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.invitation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Creates tasks for exchanging invitations with remote peers.
*/
@NotNullByDefault
public interface InvitationTaskFactory {
/**
* Creates a task using the given local and remote invitation codes.
*/
InvitationTask createTask(int localCode, int remoteCode);
}

View File

@@ -3,9 +3,9 @@ package org.briarproject.bramble.api.keyagreement;
public interface KeyAgreementConstants { public interface KeyAgreementConstants {
/** /**
* The current version of the BQP protocol. Version number 89 is reserved. * The current version of the BQP protocol.
*/ */
byte PROTOCOL_VERSION = 4; byte PROTOCOL_VERSION = 2;
/** /**
* The length of the record header in bytes. * The length of the record header in bytes.
@@ -22,10 +22,7 @@ public interface KeyAgreementConstants {
*/ */
int COMMIT_LENGTH = 16; int COMMIT_LENGTH = 16;
/** long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds
* The connection timeout in milliseconds.
*/
long CONNECTION_TIMEOUT = 20 * 1000;
/** /**
* The transport identifier for Bluetooth. * The transport identifier for Bluetooth.
@@ -36,16 +33,4 @@ public interface KeyAgreementConstants {
* The transport identifier for LAN. * The transport identifier for LAN.
*/ */
int TRANSPORT_ID_LAN = 1; int TRANSPORT_ID_LAN = 1;
/**
* Label for deriving the shared secret.
*/
String SHARED_SECRET_LABEL =
"org.briarproject.bramble.keyagreement/SHARED_SECRET";
/**
* Label for deriving the master secret.
*/
String MASTER_SECRET_LABEL =
"org.briarproject.bramble.keyagreement/MASTER_SECRET";
} }

View File

@@ -2,7 +2,7 @@ package org.briarproject.bramble.api.keyagreement;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import java.io.IOException; import java.util.concurrent.Callable;
/** /**
* An class for managing a particular key agreement listener. * An class for managing a particular key agreement listener.
@@ -24,11 +24,11 @@ public abstract class KeyAgreementListener {
} }
/** /**
* Blocks until an incoming connection is received and returns it. * Starts listening for incoming connections, and returns a Callable that
* * will return a KeyAgreementConnection when an incoming connection is
* @throws IOException if an error occurs or {@link #close()} is called. * received.
*/ */
public abstract KeyAgreementConnection accept() throws IOException; public abstract Callable<KeyAgreementConnection> listen();
/** /**
* Closes the underlying server socket. * Closes the underlying server socket.

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.keyagreement;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Manages tasks for conducting key agreements with remote peers.
*/
@NotNullByDefault
public interface KeyAgreementTaskFactory {
/**
* Gets the current key agreement task.
*/
KeyAgreementTask createTask();
}

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api.keyagreement.event;
import org.briarproject.bramble.api.event.Event;
/**
* An event that is broadcast when a BQP task stops listening.
*/
public class KeyAgreementStoppedListeningEvent extends Event {
}

View File

@@ -21,42 +21,21 @@ public interface LifecycleManager {
* The result of calling {@link #startServices(String)}. * The result of calling {@link #startServices(String)}.
*/ */
enum StartResult { enum StartResult {
ALREADY_RUNNING, ALREADY_RUNNING, DB_ERROR, SERVICE_ERROR, SUCCESS
DB_ERROR,
DATA_TOO_OLD_ERROR,
DATA_TOO_NEW_ERROR,
SERVICE_ERROR,
SUCCESS
} }
/** /**
* The state the lifecycle can be in. * Registers a {@link Service} to be started and stopped.
* Returned by {@link #getLifecycleState()}
*/
enum LifecycleState {
STARTING, MIGRATING_DATABASE, STARTING_SERVICES, RUNNING, STOPPING;
public boolean isAfter(LifecycleState state) {
return ordinal() > state.ordinal();
}
}
/**
* Registers a {@link Service} to be started and stopped. This method
* should be called before {@link #startServices(String)}.
*/ */
void registerService(Service s); void registerService(Service s);
/** /**
* Registers a {@link Client} to be started. This method should be called * Registers a {@link Client} to be started.
* before {@link #startServices(String)}.
*/ */
void registerClient(Client c); void registerClient(Client c);
/** /**
* Registers an {@link ExecutorService} to be shut down. This method * Registers an {@link ExecutorService} to be shut down.
* should be called before {@link #startServices(String)}.
*/ */
void registerForShutdown(ExecutorService e); void registerForShutdown(ExecutorService e);
@@ -92,10 +71,4 @@ public interface LifecycleManager {
* the {@link DatabaseComponent} to be closed before returning. * the {@link DatabaseComponent} to be closed before returning.
*/ */
void waitForShutdown() throws InterruptedException; void waitForShutdown() throws InterruptedException;
/**
* Returns the current state of the lifecycle.
*/
LifecycleState getLifecycleState();
} }

View File

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.lifecycle.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState;
/**
* An event that is broadcast when the app enters a new lifecycle state.
*/
public class LifecycleEvent extends Event {
private final LifecycleState state;
public LifecycleEvent(LifecycleState state) {
this.state = state;
}
public LifecycleState getLifecycleState() {
return state;
}
}

View File

@@ -0,0 +1,9 @@
package org.briarproject.bramble.api.lifecycle.event;
import org.briarproject.bramble.api.event.Event;
/**
* An event that is broadcast when the app is shutting down.
*/
public class ShutdownEvent extends Event {
}

View File

@@ -4,10 +4,5 @@ public interface LanTcpConstants {
TransportId ID = new TransportId("org.briarproject.bramble.lan"); TransportId ID = new TransportId("org.briarproject.bramble.lan");
// a transport property (shared with contacts)
String PROP_IP_PORTS = "ipPorts";
// a local setting
String PREF_LAN_IP_PORTS = "ipPorts"; String PREF_LAN_IP_PORTS = "ipPorts";
} }

View File

@@ -29,11 +29,6 @@ public interface PluginCallback {
*/ */
Map<ContactId, TransportProperties> getRemoteProperties(); Map<ContactId, TransportProperties> getRemoteProperties();
/**
* Returns the plugin's remote transport properties for the given contact.
*/
TransportProperties getRemoteProperties(ContactId c);
/** /**
* Merges the given settings with the namespaced settings * Merges the given settings with the namespaced settings
*/ */

View File

@@ -32,6 +32,11 @@ public interface PluginManager {
*/ */
Collection<DuplexPlugin> getDuplexPlugins(); Collection<DuplexPlugin> getDuplexPlugins();
/**
* Returns any duplex plugins that support invitations.
*/
Collection<DuplexPlugin> getInvitationPlugins();
/** /**
* Returns any duplex plugins that support key agreement. * Returns any duplex plugins that support key agreement.
*/ */

View File

@@ -4,8 +4,6 @@ public interface TorConstants {
TransportId ID = new TransportId("org.briarproject.bramble.tor"); TransportId ID = new TransportId("org.briarproject.bramble.tor");
String PROP_ONION = "onion";
int SOCKS_PORT = 59050; int SOCKS_PORT = 59050;
int CONTROL_PORT = 59051; int CONTROL_PORT = 59051;
@@ -18,5 +16,4 @@ public interface TorConstants {
int PREF_TOR_NETWORK_NEVER = 0; int PREF_TOR_NETWORK_NEVER = 0;
int PREF_TOR_NETWORK_WIFI = 1; int PREF_TOR_NETWORK_WIFI = 1;
int PREF_TOR_NETWORK_ALWAYS = 2; int PREF_TOR_NETWORK_ALWAYS = 2;
} }

View File

@@ -1,23 +1,22 @@
package org.briarproject.bramble.api.plugin; package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.util.StringUtils; import java.nio.charset.Charset;
/** /**
* Type-safe wrapper for a namespaced string that uniquely identifies a * Type-safe wrapper for a string that uniquely identifies a transport plugin.
* transport plugin.
*/ */
public class TransportId { public class TransportId {
/** /**
* The maximum length of a transport identifier in UTF-8 bytes. * The maximum length of transport identifier in UTF-8 bytes.
*/ */
public static int MAX_TRANSPORT_ID_LENGTH = 100; public static int MAX_TRANSPORT_ID_LENGTH = 64;
private final String id; private final String id;
public TransportId(String id) { public TransportId(String id) {
int length = StringUtils.toUtf8(id).length; byte[] b = id.getBytes(Charset.forName("UTF-8"));
if (length == 0 || length > MAX_TRANSPORT_ID_LENGTH) if (b.length == 0 || b.length > MAX_TRANSPORT_ID_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
this.id = id; this.id = id;
} }

View File

@@ -0,0 +1,190 @@
package org.briarproject.bramble.api.plugin.duplex;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.util.StringUtils;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/**
* Created by Santiago Torres-Arias on 1/10/17.
*/
public abstract class AbstractBluetoothPlugin<C, S> implements DuplexPlugin {
private static final Logger LOG =
Logger.getLogger("Halp");
protected final Executor ioExecutor;
protected final SecureRandom secureRandom;
protected final Backoff backoff;
protected final int maxLatency;
protected final DuplexPluginCallback callback;
protected final S ss = null;
protected volatile boolean running = false;
protected Runnable pollRunnable = null;
public AbstractBluetoothPlugin(Executor ioExecutor,
SecureRandom secureRandom,
Backoff backoff, int maxLatency,
DuplexPluginCallback callback) {
this.ioExecutor = ioExecutor;
this.secureRandom = secureRandom;
this.backoff = backoff;
this.maxLatency = maxLatency;
this.callback = callback;
}
@Override
public boolean supportsInvitations() {
return true;
}
@Override
public boolean supportsKeyAgreement() {
return true;
}
@Override
public boolean isRunning() {
return running;
}
@Override
public boolean shouldPoll() {
return true;
}
@Override
public int getPollingInterval() {
return backoff.getPollingInterval();
}
protected String getUuid() {
String uuid = callback.getLocalProperties().get(PROP_UUID);
if (uuid == null) {
byte[] random = new byte[UUID_BYTES];
secureRandom.nextBytes(random);
uuid = UUID.nameUUIDFromBytes(random).toString();
TransportProperties p = new TransportProperties();
p.put(PROP_UUID, uuid);
callback.mergeLocalProperties(p);
}
return uuid;
}
@Override
public TransportId getId() {
return ID;
}
@Override
public int getMaxLatency() {
return maxLatency;
}
@Override
public int getMaxIdleTime() {
// Bluetooth detects dead connections so we don't need keepalives
return Integer.MAX_VALUE;
}
protected String parseAddress(BdfList descriptor) throws FormatException {
byte[] mac = descriptor.getRaw(1);
if (mac.length != 6) throw new FormatException();
return StringUtils.macToString(mac);
}
protected void tryToClose(@Nullable S ss) {
try {
if (ss != null) close(ss);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} finally {
callback.transportDisabled();
}
}
protected abstract void close(S ss) throws IOException;
public void stop() {
running = false;
tryToClose(ss);
}
@Override
public void poll(final Collection<ContactId> connected) {
if (!running) return;
backoff.increment();
// Try to connect to known devices in parallel
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
for (Map.Entry<ContactId, TransportProperties> e : remote.entrySet()) {
final ContactId c = e.getKey();
if (connected.contains(c)) continue;
final String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
final String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
ioExecutor.execute(returnPollRunnable(address,uuid, c));
}
}
protected abstract Runnable returnPollRunnable(String address, String uuid,
ContactId c);
@Override
public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null;
TransportProperties p = callback.getRemoteProperties().get(c);
if (p == null) return null;
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) return null;
return connectToAddress(address, uuid);
}
protected abstract DuplexTransportConnection connectToAddress(String address, String uuid);
@Override
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor, long timeout) {
if (!isRunning()) return null;
String address;
try {
address = parseAddress(descriptor);
} catch (FormatException e) {
LOG.info("Invalid address in key agreement descriptor");
return null;
}
// No truncation necessary because COMMIT_LENGTH = 16
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
return connectToAddress(address, uuid);
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api.plugin.duplex; package org.briarproject.bramble.api.plugin.duplex;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -22,6 +23,20 @@ public interface DuplexPlugin extends Plugin {
@Nullable @Nullable
DuplexTransportConnection createConnection(ContactId c); DuplexTransportConnection createConnection(ContactId c);
/**
* Returns true if the plugin supports exchanging invitations.
*/
boolean supportsInvitations();
/**
* Attempts to create and return an invitation connection to the remote
* peer. Returns null if no connection can be established within the given
* time.
*/
@Nullable
DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice);
/** /**
* Returns true if the plugin supports short-range key agreement. * Returns true if the plugin supports short-range key agreement.
*/ */
@@ -36,9 +51,9 @@ public interface DuplexPlugin extends Plugin {
/** /**
* Attempts to connect to the remote peer specified in the given descriptor. * Attempts to connect to the remote peer specified in the given descriptor.
* Returns null if no connection can be established. * Returns null if no connection can be established within the given time.
*/ */
@Nullable @Nullable
DuplexTransportConnection createKeyAgreementConnection( DuplexTransportConnection createKeyAgreementConnection(
byte[] remoteCommitment, BdfList descriptor); byte[] remoteCommitment, BdfList descriptor, long timeout);
} }

View File

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

View File

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

View File

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

View File

@@ -15,17 +15,7 @@ public interface TransportPropertyManager {
/** /**
* The unique ID of the transport property client. * The unique ID of the transport property client.
*/ */
ClientId CLIENT_ID = new ClientId("org.briarproject.bramble.properties"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties");
/**
* The current major version of the transport property client.
*/
int MAJOR_VERSION = 0;
/**
* The current minor version of the transport property client.
*/
int MINOR_VERSION = 0;
/** /**
* Stores the given properties received while adding a contact - they will * Stores the given properties received while adding a contact - they will
@@ -42,8 +32,8 @@ public interface TransportPropertyManager {
/** /**
* Returns the local transport properties for all transports. * Returns the local transport properties for all transports.
* <p/> * <br/>
* Read-only. * Read-Only
*/ */
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn) Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
throws DbException; throws DbException;
@@ -59,13 +49,6 @@ public interface TransportPropertyManager {
Map<ContactId, TransportProperties> getRemoteProperties(TransportId t) Map<ContactId, TransportProperties> getRemoteProperties(TransportId t)
throws DbException; throws DbException;
/**
* Returns the remote transport properties for the given contact and
* transport.
*/
TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException;
/** /**
* Merges the given properties with the existing local properties for the * Merges the given properties with the existing local properties for the
* given transport. * given transport.

View File

@@ -1,36 +0,0 @@
package org.briarproject.bramble.api.record;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class Record {
public static final int RECORD_HEADER_BYTES = 4;
public static final int MAX_RECORD_PAYLOAD_BYTES = 48 * 1024; // 48 KiB
private final byte protocolVersion, recordType;
private final byte[] payload;
public Record(byte protocolVersion, byte recordType, byte[] payload) {
if (payload.length > MAX_RECORD_PAYLOAD_BYTES)
throw new IllegalArgumentException();
this.protocolVersion = protocolVersion;
this.recordType = recordType;
this.payload = payload;
}
public byte getProtocolVersion() {
return protocolVersion;
}
public byte getRecordType() {
return recordType;
}
public byte[] getPayload() {
return payload;
}
}

View File

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.record;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.EOFException;
import java.io.IOException;
@NotNullByDefault
public interface RecordReader {
/**
* Reads and returns the next record.
*
* @throws EOFException if the end of the stream is reached without reading
* a complete record
*/
Record readRecord() throws IOException;
void close() throws IOException;
}

View File

@@ -1,8 +0,0 @@
package org.briarproject.bramble.api.record;
import java.io.InputStream;
public interface RecordReaderFactory {
RecordReader createRecordReader(InputStream in);
}

View File

@@ -1,15 +0,0 @@
package org.briarproject.bramble.api.record;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
@NotNullByDefault
public interface RecordWriter {
void writeRecord(Record r) throws IOException;
void flush() throws IOException;
void close() throws IOException;
}

View File

@@ -1,8 +0,0 @@
package org.briarproject.bramble.api.record;
import java.io.OutputStream;
public interface RecordWriterFactory {
RecordWriter createRecordWriter(OutputStream out);
}

View File

@@ -1,29 +1,19 @@
package org.briarproject.bramble.api.sync; package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* Type-safe wrapper for a namespaced string that uniquely identifies a sync * Wrapper for a name-spaced string that uniquely identifies a sync client.
* client.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class ClientId implements Comparable<ClientId> { public class ClientId implements Comparable<ClientId> {
/**
* The maximum length of a client identifier in UTF-8 bytes.
*/
public static int MAX_CLIENT_ID_LENGTH = 100;
private final String id; private final String id;
public ClientId(String id) { public ClientId(String id) {
int length = StringUtils.toUtf8(id).length;
if (length == 0 || length > MAX_CLIENT_ID_LENGTH)
throw new IllegalArgumentException();
this.id = id; this.id = id;
} }
@@ -46,8 +36,4 @@ public class ClientId implements Comparable<ClientId> {
return id.hashCode(); return id.hashCode();
} }
@Override
public String toString() {
return id;
}
} }

View File

@@ -5,43 +5,20 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPT
public class Group { public class Group {
public enum Visibility { public enum Visibility {
INVISIBLE, // The group is not visible
INVISIBLE(0), // The group is not visible VISIBLE, // The group is visible but messages are not shared
VISIBLE(1), // The group is visible, messages are accepted but not sent SHARED // The group is visible and messages are shared
SHARED(2); // The group is visible, messages are accepted and sent
private final int value;
Visibility(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static Visibility min(Visibility a, Visibility b) {
return a.getValue() < b.getValue() ? a : b;
}
} }
/**
* The current version of the group format.
*/
public static final int FORMAT_VERSION = 1;
private final GroupId id; private final GroupId id;
private final ClientId clientId; private final ClientId clientId;
private final int majorVersion;
private final byte[] descriptor; private final byte[] descriptor;
public Group(GroupId id, ClientId clientId, int majorVersion, public Group(GroupId id, ClientId clientId, byte[] descriptor) {
byte[] descriptor) {
if (descriptor.length > MAX_GROUP_DESCRIPTOR_LENGTH) if (descriptor.length > MAX_GROUP_DESCRIPTOR_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
this.id = id; this.id = id;
this.clientId = clientId; this.clientId = clientId;
this.majorVersion = majorVersion;
this.descriptor = descriptor; this.descriptor = descriptor;
} }
@@ -59,13 +36,6 @@ public class Group {
return clientId; return clientId;
} }
/**
* Returns the major version of the client to which the group belongs.
*/
public int getMajorVersion() {
return majorVersion;
}
/** /**
* Returns the group's descriptor. * Returns the group's descriptor.
*/ */

View File

@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
public interface GroupFactory { public interface GroupFactory {
/** /**
* Creates a group with the given client ID, major version and descriptor. * Creates a group with the given client ID and descriptor.
*/ */
Group createGroup(ClientId c, int majorVersion, byte[] descriptor); Group createGroup(ClientId c, byte[] descriptor);
} }

View File

@@ -15,7 +15,7 @@ public class GroupId extends UniqueId {
/** /**
* Label for hashing groups to calculate their identifiers. * Label for hashing groups to calculate their identifiers.
*/ */
public static final String LABEL = "org.briarproject.bramble/GROUP_ID"; public static final String LABEL = "org.briarproject.bramble.GROUP_ID";
public GroupId(byte[] id) { public GroupId(byte[] id) {
super(id); super(id);

View File

@@ -5,11 +5,6 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LEN
public class Message { public class Message {
/**
* The current version of the message format.
*/
public static final int FORMAT_VERSION = 1;
private final MessageId id; private final MessageId id;
private final GroupId groupId; private final GroupId groupId;
private final long timestamp; private final long timestamp;

View File

@@ -22,7 +22,7 @@ public class MessageContext {
} }
public MessageContext(Metadata metadata) { public MessageContext(Metadata metadata) {
this(metadata, Collections.emptyList()); this(metadata, Collections.<MessageId>emptyList());
} }
public Metadata getMetadata() { public Metadata getMetadata() {

View File

@@ -7,7 +7,5 @@ public interface MessageFactory {
Message createMessage(GroupId g, long timestamp, byte[] body); Message createMessage(GroupId g, long timestamp, byte[] body);
Message createMessage(byte[] raw);
Message createMessage(MessageId m, byte[] raw); Message createMessage(MessageId m, byte[] raw);
} }

View File

@@ -16,13 +16,7 @@ public class MessageId extends UniqueId {
/** /**
* Label for hashing messages to calculate their identifiers. * Label for hashing messages to calculate their identifiers.
*/ */
public static final String ID_LABEL = "org.briarproject.bramble/MESSAGE_ID"; public static final String LABEL = "org.briarproject.bramble.MESSAGE_ID";
/**
* Label for hashing blocks of messages.
*/
public static final String BLOCK_LABEL =
"org.briarproject.bramble/MESSAGE_BLOCK";
public MessageId(byte[] id) { public MessageId(byte[] id) {
super(id); super(id);

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException; import java.io.IOException;
@NotNullByDefault @NotNullByDefault
public interface SyncRecordReader { public interface RecordReader {
boolean eof() throws IOException; boolean eof() throws IOException;

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.InputStream; import java.io.InputStream;
@NotNullByDefault @NotNullByDefault
public interface SyncRecordReaderFactory { public interface RecordReaderFactory {
SyncRecordReader createRecordReader(InputStream in); RecordReader createRecordReader(InputStream in);
} }

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException; import java.io.IOException;
@NotNullByDefault @NotNullByDefault
public interface SyncRecordWriter { public interface RecordWriter {
void writeAck(Ack a) throws IOException; void writeAck(Ack a) throws IOException;

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.OutputStream; import java.io.OutputStream;
@NotNullByDefault @NotNullByDefault
public interface SyncRecordWriterFactory { public interface RecordWriterFactory {
SyncRecordWriter createRecordWriter(OutputStream out); RecordWriter createRecordWriter(OutputStream out);
} }

View File

@@ -2,8 +2,6 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.UniqueId; import org.briarproject.bramble.api.UniqueId;
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
public interface SyncConstants { public interface SyncConstants {
/** /**
@@ -12,8 +10,16 @@ public interface SyncConstants {
byte PROTOCOL_VERSION = 0; byte PROTOCOL_VERSION = 0;
/** /**
* The maximum length of a group descriptor in bytes. * The length of the record header in bytes.
*/ */
int RECORD_HEADER_LENGTH = 4;
/**
* The maximum length of the record payload in bytes.
*/
int MAX_RECORD_PAYLOAD_LENGTH = 48 * 1024; // 48 KiB
/** The maximum length of a group descriptor in bytes. */
int MAX_GROUP_DESCRIPTOR_LENGTH = 16 * 1024; // 16 KiB int MAX_GROUP_DESCRIPTOR_LENGTH = 16 * 1024; // 16 KiB
/** /**
@@ -34,5 +40,5 @@ public interface SyncConstants {
/** /**
* The maximum number of message IDs in an ack, offer or request record. * The maximum number of message IDs in an ack, offer or request record.
*/ */
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH; int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_LENGTH / UniqueId.LENGTH;
} }

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/** /**
@@ -34,20 +33,15 @@ public interface ValidationManager {
} }
/** /**
* Registers the message validator for the given client. This method * Sets the message validator for the given client.
* should be called before {@link LifecycleManager#startServices(String)}.
*/ */
void registerMessageValidator(ClientId c, int majorVersion, void registerMessageValidator(ClientId c, MessageValidator v);
MessageValidator v);
/** /**
* Registers the incoming message hook for the given client. The hook will * Sets the incoming message hook for the given client. The hook will be
* be called once for each incoming message that passes validation. This * called once for each incoming message that passes validation.
* method should be called before
* {@link LifecycleManager#startServices(String)}.
*/ */
void registerIncomingMessageHook(ClientId c, int majorVersion, void registerIncomingMessageHook(ClientId c, IncomingMessageHook hook);
IncomingMessageHook hook);
interface MessageValidator { interface MessageValidator {

View File

@@ -7,12 +7,12 @@ package org.briarproject.bramble.api.system;
public interface Clock { public interface Clock {
/** /**
* @see System#currentTimeMillis() * @see {@link System#currentTimeMillis()}
*/ */
long currentTimeMillis(); long currentTimeMillis();
/** /**
* @see Thread#sleep(long) * @see {@link Thread#sleep(long)}
*/ */
void sleep(long milliseconds) throws InterruptedException; void sleep(long milliseconds) throws InterruptedException;
} }

View File

@@ -6,8 +6,6 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
@@ -18,55 +16,13 @@ public interface KeyManager {
/** /**
* Informs the key manager that a new contact has been added. Derives and * 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 * stores transport keys for communicating with the contact.
* each transport.
* <p/>
* {@link StreamContext StreamContexts} for the contact can be created * {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned. * after this method has returned.
*
* @param alice true if the local party is Alice
*/ */
void addContact(Transaction txn, ContactId c, SecretKey master, void addContact(Transaction txn, ContactId c, SecretKey master,
long timestamp, boolean alice) throws DbException; 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)
throws DbException;
/**
* Marks the given transport keys as usable for outgoing streams. Keys must
* be bound before they are activated.
*/
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.
*/
boolean canSendOutgoingStreams(ContactId c, TransportId t);
/** /**
* Returns a {@link StreamContext} for sending a stream to the given * Returns a {@link StreamContext} for sending a stream to the given
* contact over the given transport, or null if an error occurs or the * contact over the given transport, or null if an error occurs or the

View File

@@ -1,51 +0,0 @@
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.
*/
@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,
TransportKeys transportKeys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.transportKeys = transportKeys;
}
public KeySetId getKeySetId() {
return keySetId;
}
@Nullable
public ContactId getContactId() {
return contactId;
}
public TransportKeys getTransportKeys() {
return transportKeys;
}
@Override
public int hashCode() {
return keySetId.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof KeySet && keySetId.equals(((KeySet) o).keySetId);
}
}

View File

@@ -1,36 +0,0 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* Type-safe wrapper for an integer that uniquely identifies a set of transport
* keys within the scope of the local device.
* <p/>
* Key sets created on a given device must have increasing identifiers.
*/
@Immutable
@NotNullByDefault
public class KeySetId {
private final int id;
public KeySetId(int id) {
this.id = id;
}
public int getInt() {
return id;
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object o) {
return o instanceof KeySetId && id == ((KeySetId) o).id;
}
}

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