Compare commits

..

62 Commits

Author SHA1 Message Date
akwizgran
085987febd DO NOT MERGE: Add hacky poller method for testing discovery. 2020-03-10 15:29:05 +00:00
akwizgran
65c96514b5 Add workaround for Android UUID endianness bug. 2020-03-10 15:29:05 +00:00
akwizgran
174e678304 Don't try to add extra UUIDs to emptyList(). 2020-03-10 15:29:05 +00:00
akwizgran
14d7abc823 Don't try to discover peers if discovery is already in progress. 2020-03-10 15:29:05 +00:00
akwizgran
52fca33d0b Restart discovery if it finishes too quickly.
Discovery finishes quickly on some devices if called at the wrong time.
2020-03-10 15:29:05 +00:00
akwizgran
472d560dda Wait for discovery to finish before returning discovered devices. 2020-03-10 15:29:05 +00:00
akwizgran
2522034397 Implement peer discovery for Android Bluetooth plugin. 2020-03-10 15:29:05 +00:00
akwizgran
3e6b65b1a0 Return devices from discoverDevices() method. 2020-03-10 15:29:05 +00:00
akwizgran
155746b24e Add discovery methods to plugin API. 2020-03-10 15:29:04 +00:00
akwizgran
91caff183f Remove unnecessary plugin lookup. 2020-03-05 14:45:35 +00:00
akwizgran
249dcda34e Use tinted icon for plugin settings dialog. 2020-03-05 14:45:35 +00:00
akwizgran
c0a82f96a3 Show dialog from controller. 2020-03-05 14:45:35 +00:00
akwizgran
79f5229230 Add icon, title to Change Settings dialog. 2020-03-05 14:45:35 +00:00
akwizgran
02b4925609 Change Tor settings after asking for confirmation. 2020-03-05 14:45:35 +00:00
akwizgran
0664720680 Transition from one constraint set to another. 2020-03-05 14:45:35 +00:00
akwizgran
f04d32f7f2 Make entire collapsed view clickable. 2020-03-05 14:45:35 +00:00
akwizgran
dfa05fc473 Put the transport toggles in an expandable view (no animations). 2020-03-05 14:45:35 +00:00
akwizgran
cb936d95c5 Add STARTING_STOPPING state, use flags for reasons disabled. 2020-03-05 14:45:35 +00:00
akwizgran
1b402ba0c2 Close small gap between setStarted() and setDisabledBySettings(). 2020-03-05 14:45:34 +00:00
akwizgran
2c6f81a120 Don't show Tor in the enabling state if it's disabled by settings. 2020-03-05 14:45:34 +00:00
akwizgran
b69eb8f203 Remove "don't connect" option from Tor network setting.
This has been replaced by the enable/disable setting and no longer
works.
2020-03-05 14:45:34 +00:00
Torsten Grote
e956f073ae [android] Scroll down when nav drawer chevron is pressed 2020-03-05 14:45:34 +00:00
Torsten Grote
f4b6389163 [android] remove unused strings 2020-03-05 14:45:34 +00:00
Torsten Grote
82bfb4d95e [android] make transport plugin toggles functional 2020-03-05 14:45:34 +00:00
Torsten Grote
93ec646634 [android] Add transport plugin toggles to NavDrawer 2020-03-05 14:45:34 +00:00
Torsten Grote
2420456f25 [bramble] Add method for enabling/disabling plugins to PluginManager 2020-03-05 14:45:32 +00:00
akwizgran
b32417e7d3 Remove another redundant call to pluginStateChanged(). 2020-03-05 14:45:17 +00:00
akwizgran
9efa3cc44e Enable LAN plugin before showing QR code. 2020-03-05 14:45:17 +00:00
akwizgran
90c8603d3a Remove redundant call to pluginStateChanged(). 2020-03-05 14:45:17 +00:00
akwizgran
1ae9750c13 Use XML to specify dependencies between settings. 2020-03-05 14:45:17 +00:00
akwizgran
b0b87fc0db Clean up logic for enabling/disabling settings. 2020-03-05 14:45:17 +00:00
akwizgran
62cb6095ca Don't remove old settings yet.
This avoids an unlikely race condition at startup, where the user opens
the settings screen before the Tor plugin has migrated the settings.
2020-03-05 14:45:17 +00:00
akwizgran
d4a64f4ee3 Enable LAN plugin in unit test. 2020-03-05 14:45:14 +00:00
akwizgran
6886551895 Enable BT plugin before showing QR code. 2020-03-05 14:44:58 +00:00
akwizgran
b50b9f8088 Small code cleanups in key agreement UI. 2020-03-05 14:44:58 +00:00
akwizgran
c1aade221a Make REASON_USER into a generic reason code. 2020-03-05 14:44:57 +00:00
akwizgran
40f2c1923b Add toggle setting for LAN plugin. 2020-03-05 14:44:57 +00:00
akwizgran
cfc640f4ce Update semantics of Bluetooth setting.
The setting now enables/disables the plugin, not just contact
connections. The key agreement UI will need to be updated to change the
setting if the user agrees to use Bluetooth.
2020-03-05 14:44:57 +00:00
akwizgran
c865b90c6c Convert Bluetooth setting to a switch. 2020-03-05 14:44:57 +00:00
akwizgran
4db2d0fda2 Add toggle setting for Tor plugin. 2020-03-05 14:44:57 +00:00
akwizgran
719debc36a Remove redundant casts. 2020-03-05 14:44:57 +00:00
akwizgran
ce1b5eb0d9 Skip fetching RSS feeds if Tor is not active. 2020-03-05 14:44:57 +00:00
akwizgran
5bd9a29eab Use amber icon when enabling transports. 2020-03-05 14:44:57 +00:00
akwizgran
e6d093c52f Only update bridge and padding settings if network is enabled. 2020-03-05 14:44:57 +00:00
akwizgran
fe5bbfdd17 Notify callback of state changes while holding lock. 2020-03-05 14:44:57 +00:00
akwizgran
e6ac6913a7 Update javadocs for lock-safe methods. 2020-03-05 14:44:57 +00:00
akwizgran
54068a9e24 Remove redundant logging. 2020-03-05 14:44:57 +00:00
akwizgran
4bb14f51d2 Remove debug logging. 2020-03-05 14:44:57 +00:00
akwizgran
37ea59a89e Close server socket when BT is disabled. 2020-03-05 14:44:56 +00:00
akwizgran
f19dbf144a Remove unnecessary inner class, state checks. 2020-03-05 14:44:56 +00:00
akwizgran
4b94bd0f1b Reset backoff before notifying of new state.
The new state may cause the poller to poll the
plugin. Let's avoid a race between updating and
querying the polling interval.
2020-03-05 14:44:56 +00:00
akwizgran
0b29e3ce11 Move to enabling state earlier in Tor startup. 2020-03-05 14:44:56 +00:00
akwizgran
6a9dbcf482 Add TransportStateEvent, rename existing events. 2020-03-05 14:44:56 +00:00
akwizgran
f5a21d8c07 Ensure server socket is closed. 2020-03-05 14:44:54 +00:00
akwizgran
b6a73f2c98 Add method for getting reason why plugin is disabled. 2020-03-05 14:44:28 +00:00
akwizgran
d084f6dd8d Fix test expectations. 2020-03-05 14:44:28 +00:00
akwizgran
0259c23cb4 Rename available/unavailable states. 2020-03-05 14:44:26 +00:00
akwizgran
341382cfa8 Update tests. 2020-03-05 14:44:08 +00:00
akwizgran
49baf1020b If adapter is disabled, forget that we enabled it. 2020-03-05 14:44:08 +00:00
akwizgran
6b33c5b913 Check that server sockets are closed as expected. 2020-03-05 14:44:08 +00:00
akwizgran
53889436fc Provide more information about plugin states. 2020-03-05 14:44:05 +00:00
akwizgran
e35d1763bc Avoid NPE if there's no TelephonyManager. 2020-03-05 14:42:43 +00:00
2066 changed files with 30670 additions and 104786 deletions

1
.gitignore vendored
View File

@@ -18,7 +18,6 @@ local.properties
# Android Studio # Android Studio
.idea/* .idea/*
!.idea/inspectionProfiles/
!.idea/runConfigurations/ !.idea/runConfigurations/
!.idea/codeStyleSettings.xml !.idea/codeStyleSettings.xml
!.idea/codeStyles !.idea/codeStyles

View File

@@ -1,107 +1,33 @@
image: briar/ci-image-android:latest image: briar/ci-image-android:latest
stages: stages:
- test - test
- optional_tests - check_reproducibility
- check_reproducibility
variables: test:
GIT_SUBMODULE_STRATEGY: recursive stage: test
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
workflow:
# when to create a CI pipeline
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
when: never # avoids duplicate jobs for branch and MR
- if: '$CI_COMMIT_BRANCH'
- if: '$CI_COMMIT_TAG'
.base-test:
before_script: before_script:
- set -e - set -e
- export GRADLE_USER_HOME=$PWD/.gradle - export GRADLE_USER_HOME=$PWD/.gradle
cache: cache:
key: "$CI_COMMIT_REF_SLUG"
paths: paths:
- .gradle/wrapper - .gradle/wrapper
- .gradle/caches - .gradle/caches
script:
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom check compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources
after_script: after_script:
# these file change every time and should not be cached # these file change every time but should not be cached
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock - rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/ - rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
test:
extends: .base-test
stage: test
script:
- git submodule update
- ./gradlew -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
- ./gradlew -Djava.security.egd=file:/dev/urandom assembleOfficialDebug :briar-headless:linuxJars
- ./gradlew -Djava.security.egd=file:/dev/urandom compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources check
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: always
- when: always
android test:
extends: .base-test
stage: optional_tests
image: briar/ci-image-android-emulator:latest
script:
# start emulator first, so it can fail early
- start-emulator.sh
# run normal and screenshot tests together (exclude Large tests)
- ./gradlew -Djava.security.egd=file:/dev/urandom connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.package=org.briarproject.briar.android -Pandroid.testInstrumentationRunnerArguments.notAnnotation=androidx.test.filters.LargeTest
after_script:
- adb pull /sdcard/Pictures/screenshots
artifacts:
name: "${CI_PROJECT_PATH}_${CI_JOB_STAGE}_${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHA}"
paths:
- kernel.log
- logcat.txt
- briar-android/build/reports/androidTests/connected/flavors/*
- screenshots
expire_in: 3 days
when: on_failure
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: manual
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
changes:
- briar-android/**/*
when: manual
allow_failure: true
- if: '$CI_COMMIT_TAG == null'
when: manual
allow_failure: true
retry:
max: 1
tags:
- kvm
test_reproducible: test_reproducible:
stage: check_reproducibility stage: check_reproducibility
script: script:
- "curl -X POST -F token=${RELEASE_CHECK_TOKEN} -F ref=master -F variables[APP]='briar' -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/61/trigger/pipeline" - "curl -X POST -F token=${RELEASE_CHECK_TOKEN} -F ref=master -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/61/trigger/pipeline"
- "curl -X POST -F token=${RELEASE_JAR_CHECK_TOKEN} -F ref=main -F variables[APP]='briar-headless' -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/307/trigger/pipeline"
only: only:
- tags - tags
mailbox integration test:
stage: optional_tests
extends: .base-test
rules:
- changes:
- mailbox-integration-tests/**/*
when: on_success
allow_failure: false
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: on_success
- if: '$CI_COMMIT_TAG == null'
when: manual
allow_failure: true # TODO figure out how not to allow failure while leaving this optional
script:
- (cd briar-mailbox; git fetch; git reset --hard origin/main)
- MAILBOX_INTEGRATION_TESTS=true ./gradlew --info mailbox-integration-tests:test

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "briar-mailbox"]
path = briar-mailbox
url = https://code.briarproject.org/briar/briar-mailbox.git

View File

@@ -1,10 +1,12 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings>
<JavaCodeStyleSettings> <JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="1" /> <option name="ANNOTATION_PARAMETER_WRAP" value="1" />
<option name="IMPORT_LAYOUT_TABLE"> <option name="IMPORT_LAYOUT_TABLE">
<value> <value>
<package name="" withSubpackages="true" static="false" module="true" />
<package name="android" withSubpackages="true" static="false" /> <package name="android" withSubpackages="true" static="false" />
<emptyLine /> <emptyLine />
<package name="com" withSubpackages="true" static="false" /> <package name="com" withSubpackages="true" static="false" />
@@ -31,9 +33,11 @@
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="Groovy"> <codeStyleSettings language="Groovy">
<indentOptions> <indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" /> <option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" /> <option name="SMART_TABS" value="true" />
</indentOptions> </indentOptions>
@@ -182,9 +186,9 @@
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="kotlin"> <codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<indentOptions> <option name="PARAMETER_ANNOTATION_WRAP" value="1" />
<option name="CONTINUATION_INDENT_SIZE" value="4" /> <option name="VARIABLE_ANNOTATION_WRAP" value="1" />
</indentOptions> <option name="ENUM_CONSTANTS_WRAP" value="1" />
</codeStyleSettings> </codeStyleSettings>
</code_scheme> </code_scheme>
</component> </component>

View File

@@ -1,15 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="briar">
<words>
<w>briar</w>
<w>briarproject</w>
<w>emoji</w>
<w>encrypter</w>
<w>identicon</w>
<w>introducee</w>
<w>introducees</w>
<w>introducer</w>
<w>onboarding</w>
</words>
</dictionary>
</component>

View File

@@ -1,14 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="MissingOverrideAnnotation" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreObjectMethods" value="true" />
<option name="ignoreAnonymousClassMethods" value="false" />
</inspection_tool>
<inspection_tool class="WeakerAccess" enabled="true" level="WARNING" enabled_by_default="true">
<option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="true" />
<option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="true" />
<option name="SUGGEST_PRIVATE_FOR_INNERS" value="true" />
</inspection_tool>
</profile>
</component>

View File

@@ -1,32 +1,20 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests" type="GradleRunConfiguration" factoryName="Gradle"> <configuration default="false" name="All tests" type="AndroidJUnit" factoryName="Android JUnit">
<ExternalSystemSettings> <module name="briar-android" />
<option name="executionName" /> <option name="PACKAGE_NAME" value="" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="MAIN_CLASS_NAME" value="" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="METHOD_NAME" value="" />
<option name="scriptParameters" value="" /> <option name="TEST_OBJECT" value="package" />
<option name="taskDescriptions"> <option name="PARAMETERS" value="" />
<list /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/briar-android" />
</option>
<option name="taskNames">
<list>
<option value=":briar-android:testOfficialDebugUnitTest" />
<option value=":briar-android:testScreenshotDebugUnitTest" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2"> <method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="GradleRunConfiguration" /> <option name="Android.Gradle.BeforeRunTask" enabled="true" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="GradleRunConfiguration" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-java" run_configuration_type="GradleRunConfiguration" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-android" run_configuration_type="GradleRunConfiguration" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-android" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-api" run_configuration_type="GradleRunConfiguration" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-java" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="GradleRunConfiguration" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-headless" run_configuration_type="GradleRunConfiguration" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-headless" run_configuration_type="AndroidJUnit" />
</method> </method>
</configuration> </configuration>
</component> </component>

View File

@@ -1,23 +1,20 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-android" type="GradleRunConfiguration" factoryName="Gradle"> <configuration default="false" name="All tests in bramble-android" type="AndroidJUnit" factoryName="Android JUnit">
<ExternalSystemSettings> <module name="bramble-android" />
<option name="executionName" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="ALTERNATIVE_JRE_PATH" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="PACKAGE_NAME" value="" />
<option name="scriptParameters" value="" /> <option name="MAIN_CLASS_NAME" value="" />
<option name="taskDescriptions"> <option name="METHOD_NAME" value="" />
<list /> <option name="TEST_OBJECT" value="package" />
</option> <option name="VM_PARAMETERS" value="-ea" />
<option name="taskNames"> <option name="PARAMETERS" value="" />
<list> <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-android" />
<option value=":bramble-android:testDebugUnitTest" /> <option name="PASS_PARENT_ENVS" value="true" />
</list> <option name="TEST_SEARCH_SCOPE">
</option> <value defaultName="singleModule" />
<option name="vmOptions" value="" /> </option>
</ExternalSystemSettings> <patterns />
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess> <method />
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration> </configuration>
</component> </component>

View File

@@ -1,25 +1,20 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-api" type="GradleRunConfiguration" factoryName="Gradle"> <configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
<ExternalSystemSettings> <module name="bramble-api" />
<option name="executionName" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="ALTERNATIVE_JRE_PATH" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="PACKAGE_NAME" value="" />
<option name="scriptParameters" value="" /> <option name="MAIN_CLASS_NAME" value="" />
<option name="taskDescriptions"> <option name="METHOD_NAME" value="" />
<list /> <option name="TEST_OBJECT" value="package" />
</option> <option name="VM_PARAMETERS" value="-ea" />
<option name="taskNames"> <option name="PARAMETERS" value="" />
<list> <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
<option value=":bramble-api:animalSnifferMain" /> <option name="PASS_PARENT_ENVS" value="true" />
<option value=":bramble-api:animalSnifferTest" /> <option name="TEST_SEARCH_SCOPE">
<option value=":bramble-api:test" /> <value defaultName="singleModule" />
</list> </option>
</option> <patterns />
<option name="vmOptions" value="" /> <method />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration> </configuration>
</component> </component>

View File

@@ -1,25 +1,20 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-core" type="GradleRunConfiguration" factoryName="Gradle"> <configuration default="false" name="All tests in bramble-core" type="AndroidJUnit" factoryName="Android JUnit">
<ExternalSystemSettings> <module name="bramble-core" />
<option name="executionName" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="ALTERNATIVE_JRE_PATH" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="PACKAGE_NAME" value="" />
<option name="scriptParameters" value="" /> <option name="MAIN_CLASS_NAME" value="" />
<option name="taskDescriptions"> <option name="METHOD_NAME" value="" />
<list /> <option name="TEST_OBJECT" value="package" />
</option> <option name="VM_PARAMETERS" value="-ea" />
<option name="taskNames"> <option name="PARAMETERS" value="" />
<list> <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
<option value=":bramble-core:animalSnifferMain" /> <option name="PASS_PARENT_ENVS" value="true" />
<option value=":bramble-core:animalSnifferTest" /> <option name="TEST_SEARCH_SCOPE">
<option value=":bramble-core:test" /> <value defaultName="singleModule" />
</list> </option>
</option> <patterns />
<option name="vmOptions" value="" /> <method />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration> </configuration>
</component> </component>

View File

@@ -1,23 +1,20 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-java" type="GradleRunConfiguration" factoryName="Gradle"> <configuration default="false" name="All tests in bramble-java" type="AndroidJUnit" factoryName="Android JUnit">
<ExternalSystemSettings> <module name="bramble-java" />
<option name="executionName" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="ALTERNATIVE_JRE_PATH" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="PACKAGE_NAME" value="" />
<option name="scriptParameters" value="" /> <option name="MAIN_CLASS_NAME" value="" />
<option name="taskDescriptions"> <option name="METHOD_NAME" value="" />
<list /> <option name="TEST_OBJECT" value="package" />
</option> <option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
<option name="taskNames"> <option name="PARAMETERS" value="" />
<list> <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-java" />
<option value=":bramble-java:test" /> <option name="PASS_PARENT_ENVS" value="true" />
</list> <option name="TEST_SEARCH_SCOPE">
</option> <value defaultName="singleModule" />
<option name="vmOptions" value="" /> </option>
</ExternalSystemSettings> <patterns />
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess> <method />
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration> </configuration>
</component> </component>

View File

@@ -1,24 +1,20 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in briar-android" type="GradleRunConfiguration" factoryName="Gradle"> <configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
<ExternalSystemSettings> <module name="briar-android" />
<option name="executionName" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="ALTERNATIVE_JRE_PATH" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="PACKAGE_NAME" value="" />
<option name="scriptParameters" value="" /> <option name="MAIN_CLASS_NAME" value="" />
<option name="taskDescriptions"> <option name="METHOD_NAME" value="" />
<list /> <option name="TEST_OBJECT" value="package" />
</option> <option name="VM_PARAMETERS" value="-ea" />
<option name="taskNames"> <option name="PARAMETERS" value="" />
<list> <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
<option value=":briar-android:testOfficialDebugUnitTest" /> <option name="PASS_PARENT_ENVS" value="true" />
<option value=":briar-android:testScreenshotDebugUnitTest" /> <option name="TEST_SEARCH_SCOPE">
</list> <value defaultName="singleModule" />
</option> </option>
<option name="vmOptions" value="" /> <patterns />
</ExternalSystemSettings> <method />
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration> </configuration>
</component> </component>

View File

@@ -1,25 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in briar-api" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":briar-api:animalSnifferMain" />
<option value=":briar-api:animalSnifferTest" />
<option value=":briar-api:test" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration>
</component>

View File

@@ -1,25 +1,20 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in briar-core" type="GradleRunConfiguration" factoryName="Gradle"> <configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
<ExternalSystemSettings> <module name="briar-core" />
<option name="executionName" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="ALTERNATIVE_JRE_PATH" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="PACKAGE_NAME" value="" />
<option name="scriptParameters" value="" /> <option name="MAIN_CLASS_NAME" value="" />
<option name="taskDescriptions"> <option name="METHOD_NAME" value="" />
<list /> <option name="TEST_OBJECT" value="package" />
</option> <option name="VM_PARAMETERS" value="-ea" />
<option name="taskNames"> <option name="PARAMETERS" value="" />
<list> <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
<option value=":briar-core:animalSnifferMain" /> <option name="PASS_PARENT_ENVS" value="true" />
<option value=":briar-core:animalSnifferTest" /> <option name="TEST_SEARCH_SCOPE">
<option value=":briar-core:test" /> <value defaultName="singleModule" />
</list> </option>
</option> <patterns />
<option name="vmOptions" value="" /> <method />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration> </configuration>
</component> </component>

View File

@@ -1,23 +1,15 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in briar-headless" type="GradleRunConfiguration" factoryName="Gradle"> <configuration default="false" name="All tests in briar-headless" type="AndroidJUnit" factoryName="Android JUnit">
<ExternalSystemSettings> <module name="briar-headless" />
<option name="executionName" /> <option name="PACKAGE_NAME" value="org.briarproject.briar.headless" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="MAIN_CLASS_NAME" value="" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="METHOD_NAME" value="" />
<option name="scriptParameters" value="" /> <option name="TEST_OBJECT" value="package" />
<option name="taskDescriptions"> <option name="VM_PARAMETERS" />
<list /> <option name="PARAMETERS" value="" />
</option> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/briar-headless" />
<option name="taskNames"> <method v="2">
<list> <option name="Android.Gradle.BeforeRunTask" enabled="true" />
<option value=":briar-headless:test" /> </method>
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration> </configuration>
</component> </component>

View File

@@ -1,29 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in mailbox-integration-tests" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="env">
<map>
<entry key="MAILBOX_INTEGRATION_TESTS" value="true" />
</map>
</option>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":mailbox-integration-tests:test" />
<option value=":mailbox-integration-tests:cleanTest" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration>
</component>

View File

@@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="H2 Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
<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="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<patterns />
<method />
</configuration>
</component>

View File

@@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="HyperSQL Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
<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="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<patterns />
<method />
</configuration>
</component>

View File

@@ -1,51 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Instrumentation Tests (destroys DB)" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
<module name="briar.briar-android" />
<option name="TESTING_TYPE" value="1" />
<option name="METHOD_NAME" value="" />
<option name="CLASS_NAME" value="" />
<option name="PACKAGE_NAME" value="org.briarproject.briar.android" />
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
<option name="EXTRA_OPTIONS" value="-e notAnnotation androidx.test.filters.LargeTest" />
<option name="INCLUDE_GRADLE_EXTRA_OPTIONS" value="true" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
<option name="FORCE_STOP_RUNNING_APP" value="true" />
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
<option name="DEBUGGER_TYPE" value="Auto" />
<Auto>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Auto>
<Hybrid>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Hybrid>
<Java />
<Native>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Native>
<Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="briar-headless" type="JetRunConfigurationType" singleton="true"> <configuration default="false" name="briar-headless" type="JetRunConfigurationType" factoryName="Kotlin" singleton="true">
<module name="briar.briar-headless" /> <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="VM_PARAMETERS" value="" /> <option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="-v" /> <option name="PROGRAM_PARAMETERS" value="-v" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
@@ -8,8 +8,9 @@
<option name="PASS_PARENT_ENVS" value="true" /> <option name="PASS_PARENT_ENVS" value="true" />
<option name="MAIN_CLASS_NAME" value="org.briarproject.briar.headless.MainKt" /> <option name="MAIN_CLASS_NAME" value="org.briarproject.briar.headless.MainKt" />
<option name="WORKING_DIRECTORY" value="" /> <option name="WORKING_DIRECTORY" value="" />
<method v="2"> <module name="briar-headless" />
<option name="Make" enabled="true" /> <envs />
<method>
<option name="Gradle.BeforeRunTask" enabled="true" tasks="jar" externalProjectPath="$PROJECT_DIR$/briar-headless" vmOptions="" scriptParameters="" /> <option name="Gradle.BeforeRunTask" enabled="true" tasks="jar" externalProjectPath="$PROJECT_DIR$/briar-headless" vmOptions="" scriptParameters="" />
</method> </method>
</configuration> </configuration>

View File

@@ -1,10 +0,0 @@
Folder-Description:
===================
* `briar-*`: Specifically for the Briar app (Phone/Desktop/Headless)
* `bramble-*`: The protocol stack - not necessarily Briar-dependent
---
* `*-api`: public stuff that can be referenced from other packages and modules - mostly interfaces + a few utility classes
* `*-core`: implementations of api that are portable across Android/Desktop/Headless
* `*-java`: implementations of api that are specific to Desktop & Headless

View File

@@ -1,39 +1 @@
# Briar Briar is a messaging app designed for activists, journalists, and anyone else who needs a safe, easy and robust way to communicate. Unlike traditional messaging tools such as email, Twitter or Telegram, Briar doesn't rely on a central server - messages are synchronized directly between the users' devices. If the Internet's down, Briar can sync via Bluetooth or Wi-Fi, keeping the information flowing in a crisis. If the Internet's up, Briar can sync via the Tor network, protecting users and their relationships from surveillance.
Briar is a messaging app designed for activists, journalists, and anyone else who needs a safe, easy and robust way to communicate.
Unlike traditional messaging apps, Briar doesn't rely on a central server - messages are synchronized directly between the users' devices.
If the Internet's down, Briar can sync via Bluetooth or Wi-Fi, keeping information flowing in a crisis. If the Internet's up, Briar can sync via the Tor network, protecting users and their relationships from surveillance.
## Download Briar
[<img src="https://briarproject.org//img/fdroid_badge.png" width="240">](https://briarproject.org/fdroid)
[<img src="https://briarproject.org/img/google_play_badge_web_generic.png" width="240">](https://play.google.com/store/apps/details?id=org.briarproject.briar.android)
You can also [download the APK file](https://briarproject.org/apk) directly from
our site.
## Useful links
[Project website](https://briarproject.org/)
[Source code](https://code.briarproject.org/briar/briar/tree/master)
[User manual](https://briarproject.org/manual/)
[Wiki](https://code.briarproject.org/briar/briar/-/wikis/home)
[Privacy policy](https://briarproject.org/privacy)
## Reproducible builds
We provide [docker images](https://code.briarproject.org/briar/briar-reproducer#briar-reproducer)
to ease the task of verifying that the published APK binaries
include nothing but our publicly available source code.
You can either use those images or use them as a blueprint to build your own environment
for reproduction.
## Donate
[![Donate using Liberapay](https://briarproject.org/img/liberapay.svg)](https://liberapay.com/Briar/donate)
Bitcoin and BCH: 1NZCKkUCtJV2U2Y9hDb9uq8S7ksFCFGR6K

View File

@@ -1,9 +0,0 @@
Translations for this project are managed through Transifex:
https://transifex.com/otf/briar
If you'd like to volunteer as a translator, please create a Transifex account and request to be
added to the project's translation team. The Localization Lab has some instructions and advice for
translators here:
https://wiki.localizationlab.org/index.php/Briar

View File

@@ -3,4 +3,3 @@ gen
build build
.settings .settings
src/main/res/raw/*.zip src/main/res/raw/*.zip
src/main/jniLibs

View File

@@ -1,35 +1,35 @@
import com.android.build.gradle.tasks.MergeResources
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'witness' apply plugin: 'witness'
apply from: 'witness.gradle' apply from: 'witness.gradle'
android { android {
compileSdkVersion 35 compileSdkVersion 29
buildToolsVersion '35.0.0' buildToolsVersion '29.0.2'
packagingOptions {
jniLibs {
keepDebugSymbols += ['**/*.so']
}
}
defaultConfig { defaultConfig {
minSdkVersion 21 minSdkVersion 16
targetSdkVersion 35 targetSdkVersion 28
versionCode 10207
versionName "1.2.7"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments disableAnalytics: 'true'
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
namespace 'org.briarproject.bramble'
lint {
warning 'LintError', 'InvalidPackage', 'MissingPermission', 'InlinedApi', 'ObsoleteSdkInt', 'Override', 'NewApi', 'UnusedAttribute'
}
lintOptions {
// FIXME
warning "LintError"
warning "InvalidPackage"
warning "MissingPermission"
warning "InlinedApi", "ObsoleteSdkInt", "Override", "NewApi", "UnusedAttribute"
}
} }
configurations { configurations {
@@ -37,54 +37,44 @@ configurations {
} }
dependencies { dependencies {
api 'org.briarproject:dont-kill-me-lib:0.2.8' implementation project(path: ':bramble-core', configuration: 'default')
tor 'org.briarproject:tor-android:0.3.5.9@zip'
tor 'org.briarproject:obfs4proxy-android:0.0.11-2@zip'
// In theory this dependency shouldn't be needed, but without it Android Studio's linter will annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
// complain about unresolved symbols for bramble-api test classes in bramble-android tests,
// even though the bramble-api test classes are provided by the testImplementation dependency
// below and the compiler can find them
implementation project(':bramble-api')
implementation project(':bramble-core')
implementation 'androidx.annotation:annotation:1.5.0'
implementation "org.briarproject:onionwrapper-android:$onionwrapper_version"
tor "org.briarproject:tor-android:$tor_version"
tor "org.briarproject:lyrebird-android:$lyrebird_version"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'javax.annotation:jsr250-api:1.0'
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation 'junit:junit:4.12'
testImplementation "junit:junit:$junit_version" testImplementation "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock:$jmock_version" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-junit4:$jmock_version" testImplementation "org.jmock:jmock-legacy:2.8.2"
testImplementation "org.jmock:jmock-imposters:$jmock_version"
} }
def torLibsDir = 'src/main/jniLibs' def torBinariesDir = 'src/main/res/raw'
task cleanTorBinaries { task cleanTorBinaries {
outputs.dir torLibsDir
doLast { doLast {
delete fileTree(torLibsDir) delete fileTree(torBinariesDir) { include '*.zip' }
} }
} }
clean.dependsOn cleanTorBinaries clean.dependsOn cleanTorBinaries
task unpackTorBinaries { task unpackTorBinaries {
outputs.dir torLibsDir
doLast { doLast {
copy { copy {
from configurations.tor.collect { zipTree(it) } from configurations.tor.collect { zipTree(it) }
into torLibsDir into torBinariesDir
// TODO: Remove after next Tor upgrade, which won't include non-PIE binaries
include 'geoip.zip', '*_pie.zip'
} }
} }
dependsOn cleanTorBinaries dependsOn cleanTorBinaries
} }
preBuild.dependsOn unpackTorBinaries tasks.withType(MergeResources) {
inputs.dir torBinariesDir
dependsOn unpackTorBinaries
}

View File

@@ -1,8 +1,6 @@
# Keep the H2 classes that are loaded via reflection -keep,includedescriptorclasses class org.briarproject.** { *; }
-keep class org.h2.Driver { *; }
-keep class org.h2.engine.Engine { *; } -keep class org.h2.** { *; }
-keep class org.h2.store.fs.** { *; }
# Don't warn about unused dependencies of H2 classes
-dontwarn org.h2.** -dontwarn org.h2.**
-dontnote org.h2.** -dontnote org.h2.**
@@ -17,8 +15,5 @@
-dontwarn sun.misc.Unsafe -dontwarn sun.misc.Unsafe
-dontnote com.google.common.** -dontnote com.google.common.**
-dontwarn com.fasterxml.jackson.databind.ext.Java7SupportImpl # UPnP library isn't used
-dontwarn org.bitlet.weupnp.**
# Keep all Jackson-serialisable classes and their members
-keep interface com.fasterxml.jackson.databind.annotation.JsonSerialize
-keep @com.fasterxml.jackson.databind.annotation.JsonSerialize class * { *; }

View File

@@ -1,33 +1,21 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest
xmlns:tools="http://schemas.android.com/tools"> package="org.briarproject.bramble"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature <uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
android:name="android.hardware.bluetooth"
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!-- The BLUETOOTH permission was supposed to be removed in API 31 but is still needed on some Xiaomi/Redmi/POCO devices running API 31 and Nubia devices running API 32/33 --> <uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission <uses-permission android:name="android.permission.INTERNET"/>
android:name="android.permission.BLUETOOTH_ADMIN" <uses-permission android:name="android.permission.WAKE_LOCK"/>
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="31" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application <application
android:allowBackup="false" android:allowBackup="false"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true"> android:supportsRtl="true">
<receiver android:name=".system.AlarmReceiver" />
</application> </application>
</manifest> </manifest>

View File

@@ -1,16 +1,11 @@
package org.briarproject.bramble; package org.briarproject.bramble;
import org.briarproject.bramble.battery.AndroidBatteryModule; import org.briarproject.bramble.battery.AndroidBatteryModule;
import org.briarproject.bramble.io.DnsModule;
import org.briarproject.bramble.network.AndroidNetworkModule; import org.briarproject.bramble.network.AndroidNetworkModule;
import org.briarproject.bramble.plugin.tor.CircumventionModule; import org.briarproject.bramble.plugin.tor.CircumventionModule;
import org.briarproject.bramble.reporting.ReportingModule; import org.briarproject.bramble.reporting.ReportingModule;
import org.briarproject.bramble.socks.SocksModule; import org.briarproject.bramble.socks.SocksModule;
import org.briarproject.bramble.system.AndroidSystemModule; import org.briarproject.bramble.system.AndroidSystemModule;
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
import org.briarproject.bramble.system.AndroidWakeLockModule;
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
import org.briarproject.bramble.system.DefaultThreadFactoryModule;
import dagger.Module; import dagger.Module;
@@ -18,12 +13,7 @@ import dagger.Module;
AndroidBatteryModule.class, AndroidBatteryModule.class,
AndroidNetworkModule.class, AndroidNetworkModule.class,
AndroidSystemModule.class, AndroidSystemModule.class,
AndroidTaskSchedulerModule.class,
AndroidWakefulIoExecutorModule.class,
AndroidWakeLockModule.class,
DefaultThreadFactoryModule.class,
CircumventionModule.class, CircumventionModule.class,
DnsModule.class,
ReportingModule.class, ReportingModule.class,
SocksModule.class SocksModule.class
}) })

View File

@@ -1,8 +0,0 @@
package org.briarproject.bramble;
import org.briarproject.bramble.api.system.AlarmListener;
public interface BrambleAppComponent {
AlarmListener alarmListener();
}

View File

@@ -1,6 +0,0 @@
package org.briarproject.bramble;
public interface BrambleApplication {
BrambleAppComponent getBrambleAppComponent();
}

View File

@@ -20,6 +20,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.inject.Inject; import javax.inject.Inject;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir; import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
@@ -104,11 +105,15 @@ class AndroidAccountManager extends AccountManagerImpl
} }
files.add(appContext.getFilesDir()); files.add(appContext.getFilesDir());
addIfNotNull(files, appContext.getExternalCacheDir()); addIfNotNull(files, appContext.getExternalCacheDir());
for (File file : appContext.getExternalCacheDirs()) { if (SDK_INT >= 19) {
addIfNotNull(files, file); for (File file : appContext.getExternalCacheDirs()) {
addIfNotNull(files, file);
}
} }
for (File file : appContext.getExternalMediaDirs()) { if (SDK_INT >= 21) {
addIfNotNull(files, file); for (File file : appContext.getExternalMediaDirs()) {
addIfNotNull(files, file);
}
} }
// Clear the cache directory but don't delete it // Clear the cache directory but don't delete it
File cacheDir = appContext.getCacheDir(); File cacheDir = appContext.getCacheDir();

View File

@@ -1,11 +0,0 @@
package org.briarproject.bramble.api.system;
import android.content.Intent;
import org.briarproject.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface AlarmListener {
void onAlarm(Intent intent);
}

View File

@@ -5,7 +5,6 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.os.PowerManager;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.battery.event.BatteryEvent; import org.briarproject.bramble.api.battery.event.BatteryEvent;
@@ -17,20 +16,12 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.annotation.RequiresApi;
import static android.content.Intent.ACTION_BATTERY_CHANGED; import static android.content.Intent.ACTION_BATTERY_CHANGED;
import static android.content.Intent.ACTION_POWER_CONNECTED; import static android.content.Intent.ACTION_POWER_CONNECTED;
import static android.content.Intent.ACTION_POWER_DISCONNECTED; import static android.content.Intent.ACTION_POWER_DISCONNECTED;
import static android.os.BatteryManager.EXTRA_PLUGGED; import static android.os.BatteryManager.EXTRA_PLUGGED;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
import static android.os.PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED;
import static android.os.PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED;
import static android.os.PowerManager.ACTION_POWER_SAVE_MODE_CHANGED;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.AndroidUtils.registerReceiver;
class AndroidBatteryManager implements BatteryManager, Service { class AndroidBatteryManager implements BatteryManager, Service {
@@ -53,7 +44,7 @@ class AndroidBatteryManager implements BatteryManager, Service {
public boolean isCharging() { public boolean isCharging() {
// Get the sticky intent for ACTION_BATTERY_CHANGED // Get the sticky intent for ACTION_BATTERY_CHANGED
IntentFilter filter = new IntentFilter(ACTION_BATTERY_CHANGED); IntentFilter filter = new IntentFilter(ACTION_BATTERY_CHANGED);
Intent i = registerReceiver(appContext, null, filter, false); Intent i = appContext.registerReceiver(null, filter);
if (i == null) return false; if (i == null) return false;
int status = i.getIntExtra(EXTRA_PLUGGED, 0); int status = i.getIntExtra(EXTRA_PLUGGED, 0);
return status != 0; return status != 0;
@@ -66,13 +57,7 @@ class AndroidBatteryManager implements BatteryManager, Service {
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_POWER_CONNECTED); filter.addAction(ACTION_POWER_CONNECTED);
filter.addAction(ACTION_POWER_DISCONNECTED); filter.addAction(ACTION_POWER_DISCONNECTED);
filter.addAction(ACTION_POWER_SAVE_MODE_CHANGED); appContext.registerReceiver(batteryReceiver, filter);
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
if (SDK_INT >= 33) {
filter.addAction(ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
filter.addAction(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED);
}
registerReceiver(appContext, batteryReceiver, filter, false);
} }
@Override @Override
@@ -91,33 +76,6 @@ class AndroidBatteryManager implements BatteryManager, Service {
eventBus.broadcast(new BatteryEvent(true)); eventBus.broadcast(new BatteryEvent(true));
else if (ACTION_POWER_DISCONNECTED.equals(action)) else if (ACTION_POWER_DISCONNECTED.equals(action))
eventBus.broadcast(new BatteryEvent(false)); eventBus.broadcast(new BatteryEvent(false));
else if (SDK_INT >= 23 &&
ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action) &&
LOG.isLoggable(INFO)) {
LOG.info("Device idle mode changed to: " +
getPowerManager(ctx).isDeviceIdleMode());
} else if (SDK_INT >= 23 &&
ACTION_POWER_SAVE_MODE_CHANGED.equals(action) &&
LOG.isLoggable(INFO)) {
LOG.info("Power save mode changed to: " +
getPowerManager(ctx).isPowerSaveMode());
} else if (SDK_INT >= 33 && LOG.isLoggable(INFO) &&
ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED.equals(action)) {
PowerManager powerManager =
ctx.getSystemService(PowerManager.class);
LOG.info("Low power standby now is: " +
powerManager.isLowPowerStandbyEnabled());
} else if (SDK_INT >= 33 && LOG.isLoggable(INFO) &&
ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED.equals(action)) {
PowerManager powerManager = getPowerManager(ctx);
LOG.info("Light idle mode now is: " +
powerManager.isDeviceLightIdleMode());
}
} }
} }
@RequiresApi(api = 23)
private PowerManager getPowerManager(Context ctx) {
return ctx.getSystemService(PowerManager.class);
}
} }

View File

@@ -1,36 +1,24 @@
package org.briarproject.bramble.network; package org.briarproject.bramble.network;
import android.annotation.TargetApi;
import android.app.Application; import android.app.Application;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import org.briarproject.bramble.api.Cancellable;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.network.NetworkStatus; import org.briarproject.bramble.api.network.NetworkStatus;
import org.briarproject.bramble.api.network.event.NetworkStatusEvent; import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
import org.briarproject.bramble.api.system.TaskScheduler; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.system.Scheduler;
import java.net.Inet4Address; import java.util.concurrent.Future;
import java.net.InetAddress; import java.util.concurrent.ScheduledExecutorService;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@@ -40,7 +28,6 @@ import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Context.WIFI_SERVICE;
import static android.content.Intent.ACTION_SCREEN_OFF; import static android.content.Intent.ACTION_SCREEN_OFF;
import static android.content.Intent.ACTION_SCREEN_ON; import static android.content.Intent.ACTION_SCREEN_ON;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
@@ -48,48 +35,36 @@ import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION; import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED; import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
import static java.net.NetworkInterface.getNetworkInterfaces;
import static java.util.Collections.list;
import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.AndroidUtils.registerReceiver;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class AndroidNetworkManager implements NetworkManager, Service { class AndroidNetworkManager implements NetworkManager, Service {
private static final Logger LOG = private static final Logger LOG =
getLogger(AndroidNetworkManager.class.getName()); Logger.getLogger(AndroidNetworkManager.class.getName());
// See android.net.wifi.WifiManager // See android.net.wifi.WifiManager
private static final String WIFI_AP_STATE_CHANGED_ACTION = private static final String WIFI_AP_STATE_CHANGED_ACTION =
"android.net.wifi.WIFI_AP_STATE_CHANGED"; "android.net.wifi.WIFI_AP_STATE_CHANGED";
private final TaskScheduler scheduler; private final ScheduledExecutorService scheduler;
private final EventBus eventBus; private final EventBus eventBus;
private final Executor eventExecutor; private final Context appContext;
private final Application app; private final AtomicReference<Future<?>> connectivityCheck =
private final ConnectivityManager connectivityManager;
private final AtomicReference<Cancellable> connectivityCheck =
new AtomicReference<>(); new AtomicReference<>();
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private volatile BroadcastReceiver networkStateReceiver = null; private volatile BroadcastReceiver networkStateReceiver = null;
@Inject @Inject
AndroidNetworkManager(TaskScheduler scheduler, EventBus eventBus, AndroidNetworkManager(@Scheduler ScheduledExecutorService scheduler,
@EventExecutor Executor eventExecutor, Application app) { EventBus eventBus, Application app) {
this.scheduler = scheduler; this.scheduler = scheduler;
this.eventBus = eventBus; this.eventBus = eventBus;
this.eventExecutor = eventExecutor; this.appContext = app.getApplicationContext();
this.app = app;
connectivityManager = (ConnectivityManager)
requireNonNull(app.getSystemService(CONNECTIVITY_SERVICE));
} }
@Override @Override
@@ -104,115 +79,24 @@ class AndroidNetworkManager implements NetworkManager, Service {
filter.addAction(WIFI_AP_STATE_CHANGED_ACTION); filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); filter.addAction(WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED); if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
registerReceiver(app, networkStateReceiver, filter, false); appContext.registerReceiver(networkStateReceiver, filter);
} }
@Override @Override
public void stopService() { public void stopService() {
if (networkStateReceiver != null) if (networkStateReceiver != null)
app.unregisterReceiver(networkStateReceiver); appContext.unregisterReceiver(networkStateReceiver);
} }
@Override @Override
public NetworkStatus getNetworkStatus() { public NetworkStatus getNetworkStatus() {
// https://issuetracker.google.com/issues/175055271 ConnectivityManager cm = (ConnectivityManager)
try { appContext.getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo net = connectivityManager.getActiveNetworkInfo(); if (cm == null) throw new AssertionError();
boolean connected = net != null && net.isConnected(); NetworkInfo net = cm.getActiveNetworkInfo();
// Research into Android's behavior to check network connectivity boolean connected = net != null && net.isConnected();
// (https://code.briarproject.org/briar/public-mesh-research/-/issues/19) boolean wifi = connected && net.getType() == TYPE_WIFI;
// has shown that NetworkInfo#isConnected() returns true if the device return new NetworkStatus(connected, wifi);
// is connected to any Wifi, independent of whether any specific IP
// address can be reached using it or any domain names can be resolved.
boolean wifi = false, ipv6Only = false;
if (connected) {
wifi = net.getType() == TYPE_WIFI;
if (SDK_INT >= 23) ipv6Only = isActiveNetworkIpv6Only();
else ipv6Only = areAllAvailableNetworksIpv6Only();
}
return new NetworkStatus(connected, wifi, ipv6Only);
} catch (SecurityException e) {
logException(LOG, WARNING, e);
// Without the ConnectivityManager we can't detect whether we have
// internet access. Assume we do, which is probably less harmful
// than assuming we don't. Likewise, assume the connection is
// IPv6-only. Fall back to the WifiManager to detect whether we
// have a wifi connection.
LOG.info("ConnectivityManager is broken, guessing connectivity");
boolean connected = true, wifi = false, ipv6Only = true;
WifiManager wm = (WifiManager) app.getSystemService(WIFI_SERVICE);
if (wm != null) {
WifiInfo info = wm.getConnectionInfo();
if (info != null && info.getIpAddress() != 0) {
LOG.info("Connected to wifi");
wifi = true;
ipv6Only = false;
}
}
return new NetworkStatus(connected, wifi, ipv6Only);
}
}
/**
* Returns true if the
* {@link ConnectivityManager#getActiveNetwork() active network} has an
* IPv6 unicast address and no IPv4 addresses. The active network is
* assumed not to be a loopback interface.
*/
@TargetApi(23)
private boolean isActiveNetworkIpv6Only() {
// https://issuetracker.google.com/issues/175055271
try {
Network net = connectivityManager.getActiveNetwork();
if (net == null) {
LOG.info("No active network");
return false;
}
LinkProperties props = connectivityManager.getLinkProperties(net);
if (props == null) {
LOG.info("No link properties for active network");
return false;
}
boolean hasIpv6Unicast = false;
for (LinkAddress linkAddress : props.getLinkAddresses()) {
InetAddress addr = linkAddress.getAddress();
if (addr instanceof Inet4Address) return false;
if (!addr.isMulticastAddress()) hasIpv6Unicast = true;
}
return hasIpv6Unicast;
} catch (SecurityException e) {
logException(LOG, WARNING, e);
return false;
}
}
/**
* Returns true if the device has at least one network interface with an
* IPv6 unicast address and no interfaces with IPv4 addresses, excluding
* loopback interfaces and interfaces that are
* {@link NetworkInterface#isUp() down}. If this method returns true and
* the device has internet access then it's via IPv6 only.
*/
private boolean areAllAvailableNetworksIpv6Only() {
try {
Enumeration<NetworkInterface> interfaces = getNetworkInterfaces();
if (interfaces == null) {
LOG.info("No network interfaces");
return false;
}
boolean hasIpv6Unicast = false;
for (NetworkInterface i : list(interfaces)) {
if (i.isLoopback() || !i.isUp()) continue;
for (InetAddress addr : list(i.getInetAddresses())) {
if (addr instanceof Inet4Address) return false;
if (!addr.isMulticastAddress()) hasIpv6Unicast = true;
}
}
return hasIpv6Unicast;
} catch (SocketException e) {
logException(LOG, WARNING, e);
return false;
}
} }
private void updateConnectionStatus() { private void updateConnectionStatus() {
@@ -220,12 +104,11 @@ class AndroidNetworkManager implements NetworkManager, Service {
} }
private void scheduleConnectionStatusUpdate(int delay, TimeUnit unit) { private void scheduleConnectionStatusUpdate(int delay, TimeUnit unit) {
Cancellable newConnectivityCheck = Future<?> newConnectivityCheck =
scheduler.schedule(this::updateConnectionStatus, eventExecutor, scheduler.schedule(this::updateConnectionStatus, delay, unit);
delay, unit); Future<?> oldConnectivityCheck =
Cancellable oldConnectivityCheck =
connectivityCheck.getAndSet(newConnectivityCheck); connectivityCheck.getAndSet(newConnectivityCheck);
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(); if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(false);
} }
private class NetworkStateReceiver extends BroadcastReceiver { private class NetworkStateReceiver extends BroadcastReceiver {

View File

@@ -1,36 +0,0 @@
package org.briarproject.bramble.plugin.bluetooth;
import android.bluetooth.BluetoothSocket;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.nullsafety.NotNullByDefault;
import java.io.IOException;
@NotNullByDefault
class AndroidBluetoothConnectionFactory
implements BluetoothConnectionFactory<BluetoothSocket> {
private final BluetoothConnectionLimiter connectionLimiter;
private final AndroidWakeLockManager wakeLockManager;
private final TimeoutMonitor timeoutMonitor;
AndroidBluetoothConnectionFactory(
BluetoothConnectionLimiter connectionLimiter,
AndroidWakeLockManager wakeLockManager,
TimeoutMonitor timeoutMonitor) {
this.connectionLimiter = connectionLimiter;
this.wakeLockManager = wakeLockManager;
this.timeoutMonitor = timeoutMonitor;
}
@Override
public DuplexTransportConnection wrapSocket(DuplexPlugin plugin,
BluetoothSocket s) throws IOException {
return new AndroidBluetoothTransportConnection(plugin,
connectionLimiter, wakeLockManager, timeoutMonitor, s);
}
}

View File

@@ -1,7 +1,5 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import android.annotation.SuppressLint;
import android.app.Application;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothServerSocket;
@@ -10,23 +8,34 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.os.ParcelUuid;
import android.os.Parcelable;
import org.briarproject.bramble.api.Pair;
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.Backoff;
import org.briarproject.bramble.api.plugin.DiscoveryHandler;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; 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.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@@ -48,63 +57,58 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF; import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON; import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothDevice.ACTION_FOUND; import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
import static android.bluetooth.BluetoothDevice.ACTION_UUID;
import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_LE; import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_LE;
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE; import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static android.bluetooth.BluetoothDevice.EXTRA_UUID;
import static android.os.Build.VERSION.SDK_INT;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.shuffle; import static java.util.Collections.shuffle;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.util.AndroidUtils.registerReceiver; 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.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@SuppressLint("MissingPermission") class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
class AndroidBluetoothPlugin extends
AbstractBluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
private static final Logger LOG = private static final Logger LOG =
getLogger(AndroidBluetoothPlugin.class.getName()); getLogger(AndroidBluetoothPlugin.class.getName());
private static final int MAX_DISCOVERY_MS = 10_000; private static final int MIN_DEVICE_DISCOVERY_MS = 2_000;
private static final int MAX_DEVICE_DISCOVERY_MS = 30_000;
private static final int MAX_SERVICE_DISCOVERY_MS = 15_000;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final Application app; private final Context appContext;
private final Clock clock; private final Clock clock;
private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null; private volatile BluetoothStateReceiver receiver = null;
// Non-null if the plugin started successfully // Non-null if the plugin started successfully
private volatile BluetoothAdapter adapter = null; private volatile BluetoothAdapter adapter = null;
private volatile boolean stopDiscoverAndConnect;
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
BluetoothConnectionFactory<BluetoothSocket> connectionFactory, Executor ioExecutor, AndroidExecutor androidExecutor,
Executor ioExecutor, Context appContext, SecureRandom secureRandom, Clock clock,
Executor wakefulIoExecutor, Backoff backoff, PluginCallback callback, int maxLatency) {
SecureRandom secureRandom, super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
AndroidExecutor androidExecutor, maxLatency);
Application app,
Clock clock,
Backoff backoff,
PluginCallback callback,
long maxLatency,
int maxIdleTime) {
super(connectionLimiter, connectionFactory, ioExecutor,
wakefulIoExecutor, secureRandom, backoff, callback,
maxLatency, maxIdleTime);
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.app = app; this.appContext = appContext;
this.clock = clock; this.clock = clock;
} }
@Override
protected boolean isBluetoothAccessible() {
return hasBtConnectPermission(app);
}
@Override @Override
public void start() throws PluginException { public void start() throws PluginException {
super.start(); super.start();
@@ -113,13 +117,13 @@ class AndroidBluetoothPlugin extends
filter.addAction(ACTION_STATE_CHANGED); filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED); filter.addAction(ACTION_SCAN_MODE_CHANGED);
receiver = new BluetoothStateReceiver(); receiver = new BluetoothStateReceiver();
registerReceiver(app, receiver, filter, true); appContext.registerReceiver(receiver, filter);
} }
@Override @Override
public void stop() { public void stop() {
super.stop(); super.stop();
if (receiver != null) app.unregisterReceiver(receiver); if (receiver != null) appContext.unregisterReceiver(receiver);
} }
@Override @Override
@@ -141,11 +145,61 @@ class AndroidBluetoothPlugin extends
return adapter != null && adapter.isEnabled(); 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) {
cancelDiscoverability();
if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth");
wasEnabledByUs = false;
}
}
private void cancelDiscoverability() {
if (adapter.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
try {
Method setDiscoverableTimeout = BluetoothAdapter.class
.getDeclaredMethod("setDiscoverableTimeout", int.class);
setDiscoverableTimeout.setAccessible(true);
setDiscoverableTimeout.invoke(adapter, 1);
LOG.info("Cancelled discoverability");
} catch (NoSuchMethodException e) {
logException(LOG, WARNING, e);
} catch (IllegalAccessException e) {
logException(LOG, WARNING, e);
} catch (InvocationTargetException e) {
logException(LOG, WARNING, e);
}
}
}
@Override
void setEnabledByUs() {
wasEnabledByUs = true;
}
@Override
void onAdapterDisabled() {
super.onAdapterDisabled();
wasEnabledByUs = false;
}
@Override @Override
@Nullable @Nullable
String getBluetoothAddress() { String getBluetoothAddress() {
if (adapter == null) return null; String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
String address = AndroidUtils.getBluetoothAddress(app, adapter);
return address.isEmpty() ? null : address; return address.isEmpty() ? null : address;
} }
@@ -163,7 +217,12 @@ class AndroidBluetoothPlugin extends
@Override @Override
DuplexTransportConnection acceptConnection(BluetoothServerSocket ss) DuplexTransportConnection acceptConnection(BluetoothServerSocket ss)
throws IOException { throws IOException {
return connectionFactory.wrapSocket(this, ss.accept()); return wrapSocket(ss.accept());
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
return new AndroidBluetoothTransportConnection(this,
connectionLimiter, s);
} }
@Override @Override
@@ -180,15 +239,10 @@ class AndroidBluetoothPlugin extends
try { try {
s = d.createInsecureRfcommSocketToServiceRecord(u); s = d.createInsecureRfcommSocketToServiceRecord(u);
s.connect(); s.connect();
return connectionFactory.wrapSocket(this, s); return wrapSocket(s);
} catch (IOException e) { } catch (IOException e) {
IoUtils.tryToClose(s, LOG, WARNING); IoUtils.tryToClose(s, LOG, WARNING);
throw e; throw e;
} catch (NullPointerException e) {
// BluetoothSocket#connect() may throw an NPE under unknown
// circumstances
IoUtils.tryToClose(s, LOG, WARNING);
throw new IOException(e);
} }
} }
@@ -196,53 +250,211 @@ class AndroidBluetoothPlugin extends
@Nullable @Nullable
DuplexTransportConnection discoverAndConnect(String uuid) { DuplexTransportConnection discoverAndConnect(String uuid) {
if (adapter == null) return null; if (adapter == null) return null;
if (!discoverSemaphore.tryAcquire()) { for (BluetoothDevice d : discoverDevices()) {
LOG.info("Discover already running"); String address = d.getAddress();
return null; try {
} if (LOG.isLoggable(INFO))
try { LOG.info("Connecting to " + scrubMacAddress(address));
stopDiscoverAndConnect = false; return connectTo(address, uuid);
for (String address : discoverDevices()) { } catch (IOException e) {
if (stopDiscoverAndConnect) { if (LOG.isLoggable(INFO)) {
break; LOG.info("Could not connect to "
} + scrubMacAddress(address));
try {
if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubMacAddress(address));
return connectTo(address, uuid);
} catch (IOException e) {
if (LOG.isLoggable(INFO)) {
LOG.info("Could not connect to "
+ scrubMacAddress(address));
}
} }
} }
} finally {
discoverSemaphore.release();
} }
LOG.info("Could not connect to any devices"); LOG.info("Could not connect to any devices");
return null; return null;
} }
@Override @Override
public void stopDiscoverAndConnect() { public boolean supportsDiscovery() {
stopDiscoverAndConnect = true; return true;
adapter.cancelDiscovery();
} }
private Collection<String> discoverDevices() { @Override
List<String> addresses = new ArrayList<>(); public void discoverPeers(
List<Pair<TransportProperties, DiscoveryHandler>> properties) {
// Discover all nearby devices
List<BluetoothDevice> devices = discoverDevices();
if (devices.isEmpty()) {
LOG.info("No devices discovered");
return;
}
List<Pair<TransportProperties, DiscoveryHandler>> discovered =
new ArrayList<>();
Map<String, Pair<TransportProperties, DiscoveryHandler>> byUuid =
new HashMap<>();
Map<String, Pair<TransportProperties, DiscoveryHandler>> byAddress =
new HashMap<>();
for (Pair<TransportProperties, DiscoveryHandler> pair : properties) {
TransportProperties p = pair.getFirst();
String uuid = p.get(PROP_UUID);
if (!isNullOrEmpty(uuid)) {
byUuid.put(uuid, pair);
String address = p.get(PROP_ADDRESS);
if (!isNullOrEmpty(address)) byAddress.put(address, pair);
}
}
List<BluetoothDevice> unknown = new ArrayList<>(devices);
for (BluetoothDevice d : devices) {
Pair<TransportProperties, DiscoveryHandler> pair =
byAddress.remove(d.getAddress());
if (pair == null) {
// Try cached UUIDs
for (String uuid : getUuids(d)) {
pair = byUuid.remove(uuid);
if (pair != null) {
if (LOG.isLoggable(INFO)) {
LOG.info("Matched "
+ scrubMacAddress(d.getAddress())
+ " by cached UUID");
}
TransportProperties p =
new TransportProperties(pair.getFirst());
p.put(PROP_ADDRESS, d.getAddress());
discovered.add(new Pair<>(p, pair.getSecond()));
unknown.remove(d);
break;
}
}
} else {
if (LOG.isLoggable(INFO)) {
LOG.info("Matched " + scrubMacAddress(d.getAddress())
+ " by address");
}
discovered.add(pair);
unknown.remove(d);
}
}
if (unknown.isEmpty()) {
LOG.info("All discovered devices are known, not fetching UUIDs");
return;
}
// Fetch up-to-date UUIDs
if (LOG.isLoggable(INFO))
LOG.info("Fetching UUIDs for " + unknown.size() + " devices");
BlockingQueue<Intent> intents = new LinkedBlockingQueue<>(); BlockingQueue<Intent> intents = new LinkedBlockingQueue<>();
DiscoveryReceiver receiver = new DiscoveryReceiver(intents); QueueingReceiver receiver = new QueueingReceiver(intents);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_UUID);
appContext.registerReceiver(receiver, filter);
try {
List<BluetoothDevice> pending = new ArrayList<>();
for (BluetoothDevice d : unknown) {
if (d.fetchUuidsWithSdp()) {
if (LOG.isLoggable(INFO)) {
LOG.info("Fetching UUIDs for "
+ scrubMacAddress(d.getAddress()));
}
pending.add(d);
} else {
if (LOG.isLoggable(INFO)) {
LOG.info("Failed to fetch UUIDs for "
+ scrubMacAddress(d.getAddress()));
}
}
}
long now = clock.currentTimeMillis();
long end = now + MAX_SERVICE_DISCOVERY_MS;
while (now < end && !pending.isEmpty()) {
Intent i = intents.poll(end - now, MILLISECONDS);
if (i == null) break;
BluetoothDevice d = requireNonNull(
i.getParcelableExtra(EXTRA_DEVICE));
if (LOG.isLoggable(INFO)) {
LOG.info("Fetched UUIDs for "
+ scrubMacAddress(d.getAddress()));
}
Set<String> uuids = getUuids(d);
Parcelable[] extra = i.getParcelableArrayExtra(EXTRA_UUID);
if (extra != null) {
for (Parcelable p : extra) {
uuids.addAll(getUuidStrings((ParcelUuid) p));
}
}
for (String uuid : uuids) {
Pair<TransportProperties, DiscoveryHandler> pair =
byUuid.remove(uuid);
if (pair != null) {
if (LOG.isLoggable(INFO)) {
LOG.info("Matched "
+ scrubMacAddress(d.getAddress())
+ " by fetched UUID");
}
TransportProperties p =
new TransportProperties(pair.getFirst());
p.put(PROP_ADDRESS, d.getAddress());
discovered.add(new Pair<>(p, pair.getSecond()));
break;
}
}
pending.remove(d);
now = clock.currentTimeMillis();
}
if (LOG.isLoggable(INFO)) {
if (pending.isEmpty()) {
LOG.info("Finished fetching UUIDs");
} else {
LOG.info("Failed to fetch UUIDs for " + pending.size()
+ " devices");
}
}
} catch (InterruptedException e) {
LOG.info("Interrupted while fetching UUIDs");
Thread.currentThread().interrupt();
} finally {
appContext.unregisterReceiver(receiver);
}
if (LOG.isLoggable(INFO)) {
LOG.info("Discovered " + discovered.size() + " contacts");
}
for (Pair<TransportProperties, DiscoveryHandler> pair : discovered) {
pair.getSecond().handleDevice(pair.getFirst());
}
}
private Set<String> getUuids(BluetoothDevice d) {
Set<String> strings = new TreeSet<>();
ParcelUuid[] uuids = d.getUuids();
if (uuids == null) return strings;
for (ParcelUuid u : uuids) strings.addAll(getUuidStrings(u));
return strings;
}
// Workaround for https://code.google.com/p/android/issues/detail?id=197341
private List<String> getUuidStrings(ParcelUuid u) {
UUID forwards = u.getUuid();
ByteBuffer buf = ByteBuffer.allocate(16);
buf.putLong(forwards.getLeastSignificantBits());
buf.putLong(forwards.getMostSignificantBits());
buf.rewind();
buf.order(LITTLE_ENDIAN);
UUID backwards = new UUID(buf.getLong(), buf.getLong());
return asList(forwards.toString(), backwards.toString());
}
private List<BluetoothDevice> discoverDevices() {
if (adapter.isDiscovering()) {
LOG.info("Already discovering");
return emptyList();
}
List<BluetoothDevice> devices = new ArrayList<>();
BlockingQueue<Intent> intents = new LinkedBlockingQueue<>();
QueueingReceiver receiver = new QueueingReceiver(intents);
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_DISCOVERY_STARTED); filter.addAction(ACTION_DISCOVERY_STARTED);
filter.addAction(ACTION_DISCOVERY_FINISHED); filter.addAction(ACTION_DISCOVERY_FINISHED);
filter.addAction(ACTION_FOUND); filter.addAction(ACTION_FOUND);
registerReceiver(app, receiver, filter, true); appContext.registerReceiver(receiver, filter);
try { try {
if (adapter.startDiscovery()) { if (adapter.startDiscovery()) {
long now = clock.currentTimeMillis(); long start = clock.currentTimeMillis();
long end = now + MAX_DISCOVERY_MS; long end = start + MAX_DEVICE_DISCOVERY_MS;
long now = start;
while (now < end) { while (now < end) {
Intent i = intents.poll(end - now, MILLISECONDS); Intent i = intents.poll(end - now, MILLISECONDS);
if (i == null) break; if (i == null) break;
@@ -251,17 +463,26 @@ class AndroidBluetoothPlugin extends
LOG.info("Discovery started"); LOG.info("Discovery started");
} else if (ACTION_DISCOVERY_FINISHED.equals(action)) { } else if (ACTION_DISCOVERY_FINISHED.equals(action)) {
LOG.info("Discovery finished"); LOG.info("Discovery finished");
break; now = clock.currentTimeMillis();
if (now - start < MIN_DEVICE_DISCOVERY_MS) {
LOG.info("Discovery finished quickly, retrying");
if (!adapter.startDiscovery()) {
LOG.info("Could not restart discovery");
break;
}
} else {
break;
}
} else if (ACTION_FOUND.equals(action)) { } else if (ACTION_FOUND.equals(action)) {
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE); BluetoothDevice d = requireNonNull(
i.getParcelableExtra(EXTRA_DEVICE));
// Ignore Bluetooth LE devices // Ignore Bluetooth LE devices
if (d.getType() != DEVICE_TYPE_LE) { if (SDK_INT < 18 || d.getType() != DEVICE_TYPE_LE) {
String address = d.getAddress(); if (LOG.isLoggable(INFO)) {
if (LOG.isLoggable(INFO)) LOG.info("Discovered "
LOG.info("Discovered " + + scrubMacAddress(d.getAddress()));
scrubMacAddress(address)); }
if (!addresses.contains(address)) if (!devices.contains(d)) devices.add(d);
addresses.add(address);
} }
} }
now = clock.currentTimeMillis(); now = clock.currentTimeMillis();
@@ -275,11 +496,11 @@ class AndroidBluetoothPlugin extends
} finally { } finally {
LOG.info("Cancelling discovery"); LOG.info("Cancelling discovery");
adapter.cancelDiscovery(); adapter.cancelDiscovery();
app.unregisterReceiver(receiver); appContext.unregisterReceiver(receiver);
} }
// Shuffle the addresses so we don't always try the same one first // Shuffle the devices so we don't always try the same one first
shuffle(addresses); shuffle(devices);
return addresses; return devices;
} }
private class BluetoothStateReceiver extends BroadcastReceiver { private class BluetoothStateReceiver extends BroadcastReceiver {
@@ -300,11 +521,11 @@ class AndroidBluetoothPlugin extends
} }
} }
private static class DiscoveryReceiver extends BroadcastReceiver { private static class QueueingReceiver extends BroadcastReceiver {
private final BlockingQueue<Intent> intents; private final BlockingQueue<Intent> intents;
private DiscoveryReceiver(BlockingQueue<Intent> intents) { private QueueingReceiver(BlockingQueue<Intent> intents) {
this.intents = intents; this.intents = intents;
} }

View File

@@ -1,12 +1,9 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import android.app.Application; import android.content.Context;
import android.bluetooth.BluetoothSocket;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
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;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
@@ -15,14 +12,11 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import org.briarproject.nullsafety.NotNullByDefault;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
@@ -31,41 +25,28 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
public class AndroidBluetoothPluginFactory implements DuplexPluginFactory { public class AndroidBluetoothPluginFactory 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 MAX_IDLE_TIME = 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
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final AndroidWakeLockManager wakeLockManager; private final Context appContext;
private final Application app;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final EventBus eventBus; private final EventBus eventBus;
private final Clock clock; private final Clock clock;
private final TimeoutMonitor timeoutMonitor;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
@Inject public AndroidBluetoothPluginFactory(Executor ioExecutor,
AndroidBluetoothPluginFactory(@IoExecutor Executor ioExecutor, AndroidExecutor androidExecutor, Context appContext,
@WakefulIoExecutor Executor wakefulIoExecutor, SecureRandom secureRandom, EventBus eventBus, Clock clock,
AndroidExecutor androidExecutor,
AndroidWakeLockManager wakeLockManager,
Application app,
SecureRandom secureRandom,
EventBus eventBus,
Clock clock,
TimeoutMonitor timeoutMonitor,
BackoffFactory backoffFactory) { BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.wakeLockManager = wakeLockManager; this.appContext = appContext;
this.app = app;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.eventBus = eventBus; this.eventBus = eventBus;
this.clock = clock; this.clock = clock;
this.timeoutMonitor = timeoutMonitor;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
} }
@@ -75,23 +56,19 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
} }
@Override @Override
public long getMaxLatency() { public int getMaxLatency() {
return MAX_LATENCY; return MAX_LATENCY;
} }
@Override @Override
public DuplexPlugin createPlugin(PluginCallback callback) { public DuplexPlugin createPlugin(PluginCallback callback) {
BluetoothConnectionLimiter connectionLimiter = BluetoothConnectionLimiter connectionLimiter =
new BluetoothConnectionLimiterImpl(eventBus); new BluetoothConnectionLimiterImpl();
BluetoothConnectionFactory<BluetoothSocket> connectionFactory =
new AndroidBluetoothConnectionFactory(connectionLimiter,
wakeLockManager, timeoutMonitor);
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( AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, connectionFactory, ioExecutor, connectionLimiter, ioExecutor, androidExecutor, appContext,
wakefulIoExecutor, secureRandom, androidExecutor, app, secureRandom, clock, backoff, callback, MAX_LATENCY);
clock, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -2,67 +2,45 @@ package org.briarproject.bramble.plugin.bluetooth;
import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLock; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
import org.briarproject.bramble.api.io.TimeoutMonitor;
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.nullsafety.NotNullByDefault;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress;
@NotNullByDefault @NotNullByDefault
class AndroidBluetoothTransportConnection class AndroidBluetoothTransportConnection
extends AbstractDuplexTransportConnection { extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionLimiter; private final BluetoothConnectionLimiter connectionManager;
private final BluetoothSocket socket; private final BluetoothSocket socket;
private final InputStream in;
private final AndroidWakeLock wakeLock;
AndroidBluetoothTransportConnection(Plugin plugin, AndroidBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionLimiter, BluetoothConnectionLimiter connectionManager,
AndroidWakeLockManager wakeLockManager, BluetoothSocket socket) {
TimeoutMonitor timeoutMonitor,
BluetoothSocket socket) throws IOException {
super(plugin); super(plugin);
this.connectionLimiter = connectionLimiter; this.connectionManager = connectionManager;
this.socket = socket; this.socket = socket;
InputStream socketIn = socket.getInputStream();
if (socketIn == null) throw new IOException();
in = timeoutMonitor.createTimeoutInputStream(socketIn,
plugin.getMaxIdleTime() * 2L);
wakeLock = wakeLockManager.createWakeLock("BluetoothConnection");
wakeLock.acquire();
String address = socket.getRemoteDevice().getAddress();
if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address);
} }
@Override @Override
protected InputStream getInputStream() { protected InputStream getInputStream() throws IOException {
return in; return socket.getInputStream();
} }
@Override @Override
protected OutputStream getOutputStream() throws IOException { protected OutputStream getOutputStream() throws IOException {
OutputStream socketOut = socket.getOutputStream(); return socket.getOutputStream();
if (socketOut == null) throw new IOException();
return socketOut;
} }
@Override @Override
protected void closeConnection(boolean exception) throws IOException { protected void closeConnection(boolean exception) throws IOException {
try { try {
socket.close(); socket.close();
in.close();
} finally { } finally {
wakeLock.release(); connectionManager.connectionClosed(this);
connectionLimiter.connectionClosed(this);
} }
} }
} }

View File

@@ -1,53 +0,0 @@
package org.briarproject.bramble.plugin.file;
import android.app.Application;
import android.net.Uri;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.nullsafety.NotNullByDefault;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.PROP_URI;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@Immutable
@NotNullByDefault
class AndroidRemovableDrivePlugin extends RemovableDrivePlugin {
private final Application app;
AndroidRemovableDrivePlugin(Application app, PluginCallback callback,
long maxLatency) {
super(callback, maxLatency);
this.app = app;
}
@Override
InputStream openInputStream(TransportProperties p) throws IOException {
String uri = p.get(PROP_URI);
if (isNullOrEmpty(uri)) throw new IllegalArgumentException();
try {
return app.getContentResolver().openInputStream(Uri.parse(uri));
} catch (SecurityException e) {
throw new IOException(e);
}
}
@Override
OutputStream openOutputStream(TransportProperties p) throws IOException {
String uri = p.get(PROP_URI);
if (isNullOrEmpty(uri)) throw new IllegalArgumentException();
try {
return app.getContentResolver()
.openOutputStream(Uri.parse(uri), "wt");
} catch (SecurityException e) {
throw new IOException(e);
}
}
}

View File

@@ -1,47 +0,0 @@
package org.briarproject.bramble.plugin.file;
import android.app.Application;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static java.util.concurrent.TimeUnit.DAYS;
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.ID;
@Immutable
@NotNullByDefault
public class AndroidRemovableDrivePluginFactory implements
SimplexPluginFactory {
private static final long MAX_LATENCY = DAYS.toMillis(28);
private final Application app;
@Inject
AndroidRemovableDrivePluginFactory(Application app) {
this.app = app;
}
@Override
public TransportId getId() {
return ID;
}
@Override
public long getMaxLatency() {
return MAX_LATENCY;
}
@Nullable
@Override
public SimplexPlugin createPlugin(PluginCallback callback) {
return new AndroidRemovableDrivePlugin(app, callback, MAX_LATENCY);
}
}

View File

@@ -1,53 +1,42 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
import android.app.Application; import android.content.Context;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkInfo;
import android.net.wifi.WifiInfo; import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import org.briarproject.bramble.PoliteExecutor; import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.network.event.NetworkStatusEvent; import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
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.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.nullsafety.NotNullByDefault;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.net.SocketFactory; 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.content.Context.WIFI_SERVICE;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.list;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.DEFAULT_PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.NetworkUtils.getNetworkInterfaces;
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
@NotNullByDefault @NotNullByDefault
class AndroidLanTcpPlugin extends LanTcpPlugin { class AndroidLanTcpPlugin extends LanTcpPlugin {
@@ -55,13 +44,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
private static final Logger LOG = private static final Logger LOG =
getLogger(AndroidLanTcpPlugin.class.getName()); getLogger(AndroidLanTcpPlugin.class.getName());
/**
* The interface name is used as a heuristic for deciding whether the
* device is providing a wifi access point.
*/
private static final Pattern AP_INTERFACE_NAME =
Pattern.compile("^(wlan|ap|p2p)[-0-9]");
private final Executor connectionStatusExecutor; private final Executor connectionStatusExecutor;
private final ConnectivityManager connectivityManager; private final ConnectivityManager connectivityManager;
@Nullable @Nullable
@@ -69,22 +51,20 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
private volatile SocketFactory socketFactory; private volatile SocketFactory socketFactory;
AndroidLanTcpPlugin(Executor ioExecutor, AndroidLanTcpPlugin(Executor ioExecutor, Context appContext,
Executor wakefulIoExecutor, Backoff backoff, PluginCallback callback, int maxLatency,
Application app, int maxIdleTime, int connectionTimeout) {
Backoff backoff, super(ioExecutor, backoff, callback, maxLatency, maxIdleTime,
PluginCallback callback, connectionTimeout);
long maxLatency,
int maxIdleTime,
int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,
maxIdleTime, connectionTimeout);
// Don't execute more than one connection status check at a time // Don't execute more than one connection status check at a time
connectionStatusExecutor = connectionStatusExecutor =
new PoliteExecutor("AndroidLanTcpPlugin", ioExecutor, 1); new PoliteExecutor("AndroidLanTcpPlugin", ioExecutor, 1);
connectivityManager = (ConnectivityManager) ConnectivityManager connectivityManager = (ConnectivityManager)
requireNonNull(app.getSystemService(CONNECTIVITY_SERVICE)); appContext.getSystemService(CONNECTIVITY_SERVICE);
wifiManager = (WifiManager) app.getSystemService(WIFI_SERVICE); if (connectivityManager == null) throw new AssertionError();
this.connectivityManager = connectivityManager;
wifiManager = (WifiManager) appContext.getApplicationContext()
.getSystemService(WIFI_SERVICE);
socketFactory = SocketFactory.getDefault(); socketFactory = SocketFactory.getDefault();
} }
@@ -93,8 +73,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
initialisePortProperty(); initialisePortProperty();
Settings settings = callback.getSettings(); Settings settings = callback.getSettings();
state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, false));
DEFAULT_PREF_PLUGIN_ENABLE));
updateConnectionStatus(); updateConnectionStatus();
} }
@@ -104,115 +83,21 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
} }
@Override @Override
protected List<InetAddress> getUsableLocalInetAddresses(boolean ipv4) { protected List<InetAddress> getUsableLocalInetAddresses() {
InetAddress addr = getWifiAddress(ipv4); // If the device doesn't have wifi, don't open any sockets
return addr == null ? emptyList() : singletonList(addr); if (wifiManager == null) return emptyList();
}
@Nullable
private InetAddress getWifiAddress(boolean ipv4) {
Pair<InetAddress, Boolean> wifi = getWifiIpv4Address();
if (ipv4) return wifi == null ? null : wifi.getFirst();
// If there's no wifi IPv4 address, we might be a client on an
// IPv6-only wifi network. We can only detect this on API 21+
if (wifi == null) {
return getWifiClientIpv6Address();
}
// Use the wifi IPv4 address to determine which interface's IPv6
// address we should return (if the interface has a suitable address)
return getIpv6AddressForInterface(wifi.getFirst());
}
/**
* Returns a {@link Pair} where the first element is the IPv4 address of
* the wifi interface and the second element is true if this device is
* providing an access point, or false if this device is a client. Returns
* null if this device isn't connected to wifi as an access point or client.
*/
@Nullable
private Pair<InetAddress, Boolean> getWifiIpv4Address() {
if (wifiManager == null) return null;
// If we're connected to a wifi network, return its address // If we're connected to a wifi network, return its address
WifiInfo info = wifiManager.getConnectionInfo(); WifiInfo info = wifiManager.getConnectionInfo();
if (info != null && info.getIpAddress() != 0) { if (info != null && info.getIpAddress() != 0) {
return new Pair<>(intToInetAddress(info.getIpAddress()), false); return singletonList(intToInetAddress(info.getIpAddress()));
} }
// If we're providing an access point, return its address // If we're running an access point, return its address
for (NetworkInterface iface : getNetworkInterfaces()) { for (InetAddress addr : getLocalInetAddresses()) {
if (AP_INTERFACE_NAME.matcher(iface.getName()).find()) { if (addr.equals(WIFI_AP_ADDRESS)) return singletonList(addr);
for (InterfaceAddress ifAddr : iface.getInterfaceAddresses()) { if (addr.equals(WIFI_DIRECT_AP_ADDRESS)) return singletonList(addr);
if (isPossibleWifiApInterface(ifAddr)) {
return new Pair<>(ifAddr.getAddress(), true);
}
}
}
}
// Not connected to wifi
return null;
}
/**
* Returns true if the given address may belong to an interface providing
* a wifi access point (including wifi direct legacy mode access points).
* <p>
* This method may return true for wifi client interfaces as well, but
* we've already checked for a wifi client connection above.
*/
private boolean isPossibleWifiApInterface(InterfaceAddress ifAddr) {
if (ifAddr.getNetworkPrefixLength() != 24) return false;
byte[] ip = ifAddr.getAddress().getAddress();
return ip.length == 4
&& ip[0] == (byte) 192
&& ip[1] == (byte) 168;
}
/**
* Returns a link-local IPv6 address for the wifi client interface, or null
* if there's no such interface or it doesn't have a suitable address.
*/
@Nullable
private InetAddress getWifiClientIpv6Address() {
// https://issuetracker.google.com/issues/175055271
try {
for (Network net : connectivityManager.getAllNetworks()) {
NetworkCapabilities caps =
connectivityManager.getNetworkCapabilities(net);
if (caps == null || !caps.hasTransport(TRANSPORT_WIFI)) {
continue;
}
LinkProperties props =
connectivityManager.getLinkProperties(net);
if (props == null) continue;
for (LinkAddress linkAddress : props.getLinkAddresses()) {
InetAddress addr = linkAddress.getAddress();
if (isIpv6LinkLocalAddress(addr)) return addr;
}
}
} catch (SecurityException e) {
logException(LOG, WARNING, e);
}
return null;
}
/**
* Returns a link-local IPv6 address for the interface with the given IPv4
* address, or null if the interface doesn't have a suitable address.
*/
@Nullable
private InetAddress getIpv6AddressForInterface(InetAddress ipv4) {
try {
// We may get an NPE from getByInetAddress() on Android 11
NetworkInterface iface = NetworkInterface.getByInetAddress(ipv4);
if (iface == null) return null;
for (InetAddress addr : list(iface.getInetAddresses())) {
if (isIpv6LinkLocalAddress(addr)) return addr;
}
// No suitable address
return null;
} catch (SocketException | NullPointerException e) {
logException(LOG, WARNING, e);
return null;
} }
// No suitable addresses
return emptyList();
} }
private InetAddress intToInetAddress(int ip) { private InetAddress intToInetAddress(int ip) {
@@ -232,17 +117,11 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
// On API 21 and later, a socket that is not created with the wifi // 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 // network's socket factory may try to connect via another network
private SocketFactory getSocketFactory() { private SocketFactory getSocketFactory() {
// https://issuetracker.google.com/issues/175055271 if (SDK_INT < 21) return SocketFactory.getDefault();
try { for (Network net : connectivityManager.getAllNetworks()) {
for (Network net : connectivityManager.getAllNetworks()) { NetworkInfo info = connectivityManager.getNetworkInfo(net);
NetworkCapabilities caps = if (info != null && info.getType() == TYPE_WIFI)
connectivityManager.getNetworkCapabilities(net); return net.getSocketFactory();
if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) {
return net.getSocketFactory();
}
}
} catch (SecurityException e) {
logException(LOG, WARNING, e);
} }
LOG.warning("Could not find suitable socket factory"); LOG.warning("Could not find suitable socket factory");
return SocketFactory.getDefault(); return SocketFactory.getDefault();
@@ -258,51 +137,31 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
connectionStatusExecutor.execute(() -> { connectionStatusExecutor.execute(() -> {
State s = getState(); State s = getState();
if (s != ACTIVE && s != INACTIVE) return; if (s != ACTIVE && s != INACTIVE) return;
Pair<InetAddress, Boolean> wifi = getPreferredWifiAddress(); List<InetAddress> addrs = getLocalInetAddresses();
if (wifi == null) { if (addrs.contains(WIFI_AP_ADDRESS)
LOG.info("Not connected to wifi"); || addrs.contains(WIFI_DIRECT_AP_ADDRESS)) {
socketFactory = SocketFactory.getDefault();
// Server sockets may not have been closed automatically when
// interface was taken down. If any sockets are open, closing
// them here will cause the sockets to be cleared and the state
// to be updated in acceptContactConnections()
if (s == ACTIVE) {
LOG.info("Closing server sockets");
tryToClose(state.getServerSocket(true), LOG, WARNING);
tryToClose(state.getServerSocket(false), LOG, WARNING);
}
} else if (wifi.getSecond()) {
LOG.info("Providing wifi hotspot"); LOG.info("Providing wifi hotspot");
// There's no corresponding Network object and thus no way // There's no corresponding Network object and thus no way
// to get a suitable socket factory, so we won't be able to // to get a suitable socket factory, so we won't be able to
// make outgoing connections on API 21+ if another network // make outgoing connections on API 21+ if another network
// has internet access // has internet access
socketFactory = SocketFactory.getDefault(); socketFactory = SocketFactory.getDefault();
bind(); if (s == INACTIVE) bind();
} else if (addrs.isEmpty()) {
LOG.info("Not connected to wifi");
socketFactory = SocketFactory.getDefault();
// Server socket may not have been closed automatically when
// interface was taken down. Socket will be cleared and state
// updated in acceptContactConnections()
if (s == ACTIVE) {
LOG.info("Closing server socket");
tryToClose(state.getServerSocket(), LOG, WARNING);
}
} else { } else {
LOG.info("Connected to wifi"); LOG.info("Connected to wifi");
socketFactory = getSocketFactory(); socketFactory = getSocketFactory();
bind(); if (s == INACTIVE) bind();
} }
}); });
} }
/**
* Returns a {@link Pair} where the first element is an IP address (IPv4 if
* available, otherwise IPv6) of the wifi interface and the second element
* is true if this device is providing an access point, or false if this
* device is a client. Returns null if this device isn't connected to wifi
* as an access point or client.
*/
@Nullable
private Pair<InetAddress, Boolean> getPreferredWifiAddress() {
Pair<InetAddress, Boolean> wifi = getWifiIpv4Address();
// If there's no wifi IPv4 address, we might be a client on an
// IPv6-only wifi network. We can only detect this on API 21+
if (wifi == null) {
InetAddress ipv6 = getWifiClientIpv6Address();
if (ipv6 != null) return new Pair<>(ipv6, false);
}
return wifi;
}
} }

View File

@@ -1,22 +1,19 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
import android.app.Application; import android.content.Context;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor; 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;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import org.briarproject.nullsafety.NotNullByDefault;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID; import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@@ -31,22 +28,17 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
private final Application app; private final Context appContext;
@Inject public AndroidLanTcpPluginFactory(Executor ioExecutor, EventBus eventBus,
AndroidLanTcpPluginFactory(@IoExecutor Executor ioExecutor, BackoffFactory backoffFactory, Context appContext) {
@WakefulIoExecutor Executor wakefulIoExecutor,
EventBus eventBus,
BackoffFactory backoffFactory,
Application app) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
this.app = app; this.appContext = appContext;
} }
@Override @Override
@@ -55,7 +47,7 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
} }
@Override @Override
public long getMaxLatency() { public int getMaxLatency() {
return MAX_LATENCY; return MAX_LATENCY;
} }
@@ -64,8 +56,8 @@ public class AndroidLanTcpPluginFactory 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);
AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor, AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor,
wakefulIoExecutor, app, backoff, callback, appContext, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME,
MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT); CONNECTION_TIMEOUT);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -0,0 +1,100 @@
package org.briarproject.bramble.plugin.tor;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.PowerManager;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.network.NetworkManager;
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.PluginCallback;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.util.RenewableWakeLock;
import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory;
import static android.content.Context.MODE_PRIVATE;
import static android.content.Context.POWER_SERVICE;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.concurrent.TimeUnit.MINUTES;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class AndroidTorPlugin extends TorPlugin {
private final Context appContext;
private final RenewableWakeLock wakeLock;
AndroidTorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
Context appContext, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory,
Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
backoff, torRendezvousCrypto, callback, architecture, maxLatency, maxIdleTime,
appContext.getDir("tor", MODE_PRIVATE));
this.appContext = appContext;
PowerManager pm = (PowerManager)
appContext.getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError();
wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK,
getWakeLockTag(), 1, MINUTES);
}
@Override
protected int getProcessId() {
return android.os.Process.myPid();
}
@Override
protected long getLastUpdateTime() {
try {
PackageManager pm = appContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(appContext.getPackageName(), 0);
return pi.lastUpdateTime;
} catch (NameNotFoundException e) {
throw new AssertionError(e);
}
}
@Override
protected void enableNetwork(boolean enable) throws IOException {
if (enable) wakeLock.acquire();
super.enableNetwork(enable);
if (!enable) wakeLock.release();
}
@Override
public void stop() {
super.stop();
wakeLock.release();
}
private String getWakeLockTag() {
PackageManager pm = appContext.getPackageManager();
for (PackageInfo info : pm.getInstalledPackages(0)) {
String name = info.packageName.toLowerCase();
if (name.startsWith("com.huawei.powergenie")) {
return "LocationManagerService";
} else if (name.startsWith("com.evenwell.powermonitor")) {
return "AudioIn";
}
}
return getClass().getSimpleName();
}
}

View File

@@ -1,94 +1,123 @@
package org.briarproject.bramble.plugin.tor; package org.briarproject.bramble.plugin.tor;
import android.app.Application; import android.content.Context;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
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;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TorControlPort; import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.TorDirectory; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.TorSocksPort; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.WakefulIoExecutor; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.onionwrapper.AndroidTorWrapper; import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.onionwrapper.CircumventionProvider;
import org.briarproject.onionwrapper.LocationUtils;
import org.briarproject.onionwrapper.TorWrapper;
import java.io.File;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import static org.briarproject.bramble.util.AndroidUtils.getSupportedArchitectures;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class AndroidTorPluginFactory extends TorPluginFactory { public class AndroidTorPluginFactory implements DuplexPluginFactory {
private final Application app; private static final Logger LOG =
private final AndroidWakeLockManager wakeLockManager; Logger.getLogger(AndroidTorPluginFactory.class.getName());
@Inject private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
AndroidTorPluginFactory(@IoExecutor Executor ioExecutor, private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
@EventExecutor Executor eventExecutor, private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
@WakefulIoExecutor Executor wakefulIoExecutor, private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
NetworkManager networkManager, private static final double BACKOFF_BASE = 1.2;
LocationUtils locationUtils,
EventBus eventBus, private final Executor ioExecutor;
SocketFactory torSocketFactory, private final ScheduledExecutorService scheduler;
BackoffFactory backoffFactory, private final Context appContext;
private final NetworkManager networkManager;
private final LocationUtils locationUtils;
private final EventBus eventBus;
private final SocketFactory torSocketFactory;
private final BackoffFactory backoffFactory;
private final ResourceProvider resourceProvider;
private final CircumventionProvider circumventionProvider;
private final BatteryManager batteryManager;
private final Clock clock;
public AndroidTorPluginFactory(Executor ioExecutor,
ScheduledExecutorService scheduler, Context appContext,
NetworkManager networkManager, LocationUtils locationUtils,
EventBus eventBus, SocketFactory torSocketFactory,
BackoffFactory backoffFactory, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, BatteryManager batteryManager, Clock clock) {
Clock clock, this.ioExecutor = ioExecutor;
CryptoComponent crypto, this.scheduler = scheduler;
@TorDirectory File torDirectory, this.appContext = appContext;
@TorSocksPort int torSocksPort, this.networkManager = networkManager;
@TorControlPort int torControlPort, this.locationUtils = locationUtils;
Application app, this.eventBus = eventBus;
AndroidWakeLockManager wakeLockManager) { this.torSocketFactory = torSocketFactory;
super(ioExecutor, eventExecutor, wakefulIoExecutor, networkManager, this.backoffFactory = backoffFactory;
locationUtils, eventBus, torSocketFactory, backoffFactory, this.resourceProvider = resourceProvider;
circumventionProvider, batteryManager, clock, crypto, this.circumventionProvider = circumventionProvider;
torDirectory, torSocksPort, torControlPort); this.batteryManager = batteryManager;
this.app = app; this.clock = clock;
this.wakeLockManager = wakeLockManager;
} }
@Nullable
@Override @Override
String getArchitectureForTorBinary() { public TransportId getId() {
for (String abi : getSupportedArchitectures()) { return TorConstants.ID;
if (abi.startsWith("x86_64")) return "x86_64_pie"; }
else if (abi.startsWith("x86")) return "x86_pie";
else if (abi.startsWith("arm64")) return "arm64_pie"; @Override
else if (abi.startsWith("armeabi")) return "arm_pie"; public int getMaxLatency() {
return MAX_LATENCY;
}
@Override
public DuplexPlugin createPlugin(PluginCallback callback) {
// Check that we have a Tor binary for this architecture
String architecture = null;
for (String abi : AndroidUtils.getSupportedArchitectures()) {
if (abi.startsWith("x86_64")) {
architecture = "x86_64";
break;
} else if (abi.startsWith("x86")) {
architecture = "x86";
break;
} else if (abi.startsWith("arm64")) {
architecture = "arm64";
break;
} else if (abi.startsWith("armeabi")) {
architecture = "arm";
break;
}
} }
return null; if (architecture == null) {
} LOG.info("Tor is not supported on this architecture");
return null;
}
// Use position-independent executable
architecture += "_pie";
@Override Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
TorPlugin createPluginInstance(Backoff backoff, MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback, TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
String architecture) { AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, scheduler,
TorWrapper tor = new AndroidTorWrapper(app, wakeLockManager, appContext, networkManager, locationUtils, torSocketFactory,
ioExecutor, eventExecutor, architecture, torDirectory, clock, resourceProvider, circumventionProvider, batteryManager,
torSocksPort, torControlPort); backoff, torRendezvousCrypto, callback, architecture,
return new TorPlugin(ioExecutor, wakefulIoExecutor, MAX_LATENCY, MAX_IDLE_TIME);
networkManager, locationUtils, torSocketFactory, eventBus.addListener(plugin);
circumventionProvider, batteryManager, backoff, return plugin;
torRendezvousCrypto, tor, callback, MAX_LATENCY,
MAX_IDLE_TIME);
} }
} }

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.nullsafety.NotNullByDefault;
@NotNullByDefault
interface AlarmConstants {
/**
* Request code for the broadcast intent attached to the periodic alarm.
*/
int REQUEST_ALARM = 1;
/**
* Key for storing the process ID in the extras of the periodic alarm's
* intent. This allows us to ignore alarms scheduled by dead processes.
*/
String EXTRA_PID = "org.briarproject.bramble.EXTRA_PID";
}

View File

@@ -1,17 +0,0 @@
package org.briarproject.bramble.system;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import org.briarproject.bramble.BrambleApplication;
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
BrambleApplication app =
(BrambleApplication) ctx.getApplicationContext();
app.getBrambleAppComponent().alarmListener().onAlarm(intent);
}
}

View File

@@ -0,0 +1,72 @@
package org.briarproject.bramble.system;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.LocationUtils;
import java.util.Locale;
import java.util.logging.Logger;
import javax.inject.Inject;
import static android.content.Context.TELEPHONY_SERVICE;
@NotNullByDefault
class AndroidLocationUtils implements LocationUtils {
private static final Logger LOG =
Logger.getLogger(AndroidLocationUtils.class.getName());
private final Context appContext;
@Inject
AndroidLocationUtils(Application app) {
appContext = app.getApplicationContext();
}
/**
* This guesses the current country from the first of these sources that
* succeeds (also in order of likelihood of being correct):
*
* <ul>
* <li>Phone network. This works even when no SIM card is inserted, or a
* foreign SIM card is inserted.</li>
* <li>SIM card. This is only an heuristic and assumes the user is not
* roaming.</li>
* <li>User locale. This is an even worse heuristic.</li>
* </ul>
*
* Note: this is very similar to <a href="https://android.googlesource.com/platform/frameworks/base/+/cd92588%5E/location/java/android/location/CountryDetector.java">
* this API</a> except it seems that Google doesn't want us to use it for
* some reason - both that class and {@code Context.COUNTRY_CODE} are
* annotated {@code @hide}.
*/
@Override
@SuppressLint("DefaultLocale")
public String getCurrentCountry() {
String countryCode = getCountryFromPhoneNetwork();
if (!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase();
LOG.info("Falling back to SIM card country");
countryCode = getCountryFromSimCard();
if (!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase();
LOG.info("Falling back to user-defined locale");
return Locale.getDefault().getCountry();
}
private String getCountryFromPhoneNetwork() {
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
TelephonyManager tm = (TelephonyManager) o;
return tm == null ? "" : tm.getNetworkCountryIso();
}
private String getCountryFromSimCard() {
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
TelephonyManager tm = (TelephonyManager) o;
return tm == null ? "" : tm.getSimCountryIso();
}
}

View File

@@ -4,8 +4,8 @@ import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.nullsafety.NotNullByDefault;
import java.io.InputStream; import java.io.InputStream;

View File

@@ -6,31 +6,32 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Parcel; import android.os.Parcel;
import android.os.StrictMode; import android.os.StrictMode;
import android.provider.Settings; import android.provider.Settings;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.List;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.os.Build.FINGERPRINT; import static android.content.Context.WIFI_SERVICE;
import static android.os.Build.SERIAL;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static android.os.Process.myPid;
import static android.os.Process.myTid;
import static android.os.Process.myUid;
import static android.provider.Settings.Secure.ANDROID_ID; import static android.provider.Settings.Secure.ANDROID_ID;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class AndroidSecureRandomProvider extends UnixSecureRandomProvider { class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
private static final int SEED_LENGTH = 32;
private final Context appContext; private final Context appContext;
@Inject @Inject
@@ -42,27 +43,31 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
@Override @Override
protected void writeToEntropyPool(DataOutputStream out) throws IOException { protected void writeToEntropyPool(DataOutputStream out) throws IOException {
super.writeToEntropyPool(out); super.writeToEntropyPool(out);
out.writeInt(myPid()); out.writeInt(android.os.Process.myPid());
out.writeInt(myTid()); out.writeInt(android.os.Process.myTid());
out.writeInt(myUid()); out.writeInt(android.os.Process.myUid());
if (FINGERPRINT != null) out.writeUTF(FINGERPRINT); if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
if (SERIAL != null) out.writeUTF(SERIAL); if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
ContentResolver contentResolver = appContext.getContentResolver(); ContentResolver contentResolver = appContext.getContentResolver();
String id = Settings.Secure.getString(contentResolver, ANDROID_ID); String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
if (id != null) out.writeUTF(id); if (id != null) out.writeUTF(id);
// On API 31 and higher we need permission to access bonded devices Parcel parcel = Parcel.obtain();
if (SDK_INT < 31) { WifiManager wm = (WifiManager) appContext.getApplicationContext()
Parcel parcel = Parcel.obtain(); .getSystemService(WIFI_SERVICE);
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); if (wm != null) {
if (bt != null) { List<WifiConfiguration> configs = wm.getConfiguredNetworks();
@SuppressLint("MissingPermission") if (configs != null) {
Set<BluetoothDevice> deviceSet = bt.getBondedDevices(); for (WifiConfiguration config : configs)
for (BluetoothDevice device : deviceSet) parcel.writeParcelable(config, 0);
parcel.writeParcelable(device, 0);
} }
out.write(parcel.marshall());
parcel.recycle();
} }
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt != null) {
for (BluetoothDevice device : bt.getBondedDevices())
parcel.writeParcelable(device, 0);
}
out.write(parcel.marshall());
parcel.recycle();
} }
@Override @Override
@@ -70,6 +75,27 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
// Silence strict mode // Silence strict mode
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites(); StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
super.writeSeed(); super.writeSeed();
if (SDK_INT <= 18) applyOpenSslFix();
StrictMode.setThreadPolicy(tp); StrictMode.setThreadPolicy(tp);
} }
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private void applyOpenSslFix() {
byte[] seed = new UnixSecureRandomSpi().engineGenerateSeed(
SEED_LENGTH);
try {
// Seed the OpenSSL PRNG
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class)
.invoke(null, (Object) seed);
// Mix the output of the Linux PRNG into the OpenSSL PRNG
int bytesRead = (Integer) Class.forName(
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_load_file", String.class, long.class)
.invoke(null, "/dev/urandom", 1024);
if (bytesRead != 1024) throw new IOException();
} catch (Exception e) {
throw new SecurityException(e);
}
}
} }

View File

@@ -1,19 +1,12 @@
package org.briarproject.bramble.system; package org.briarproject.bramble.system;
import android.app.Application;
import org.briarproject.bramble.api.event.EventExecutor; import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
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.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.onionwrapper.AndroidLocationUtilsFactory;
import org.briarproject.onionwrapper.LocationUtils;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -23,23 +16,6 @@ import dagger.Provides;
@Module @Module
public class AndroidSystemModule { public class AndroidSystemModule {
private final ScheduledExecutorService scheduledExecutorService;
public AndroidSystemModule() {
// Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy =
new ScheduledThreadPoolExecutor.DiscardPolicy();
scheduledExecutorService = new ScheduledThreadPoolExecutor(1, policy);
}
@Provides
@Singleton
ScheduledExecutorService provideScheduledExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(scheduledExecutorService);
return scheduledExecutorService;
}
@Provides @Provides
@Singleton @Singleton
SecureRandomProvider provideSecureRandomProvider( SecureRandomProvider provideSecureRandomProvider(
@@ -48,9 +24,8 @@ public class AndroidSystemModule {
} }
@Provides @Provides
@Singleton LocationUtils provideLocationUtils(AndroidLocationUtils locationUtils) {
LocationUtils provideLocationUtils(Application app) { return locationUtils;
return AndroidLocationUtilsFactory.createAndroidLocationUtils(app);
} }
@Provides @Provides

View File

@@ -1,247 +0,0 @@
package org.briarproject.bramble.system;
import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.Application;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Process;
import android.os.SystemClock;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
import org.briarproject.bramble.api.Cancellable;
import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.system.AlarmListener;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.api.system.Wakeful;
import org.briarproject.nullsafety.NotNullByDefault;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.INTERVAL_FIFTEEN_MINUTES;
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
import static android.content.Context.ALARM_SERVICE;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.system.AlarmConstants.EXTRA_PID;
import static org.briarproject.bramble.system.AlarmConstants.REQUEST_ALARM;
import static org.briarproject.bramble.util.AndroidUtils.getImmutableFlags;
@ThreadSafe
@NotNullByDefault
class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener {
private static final Logger LOG =
getLogger(AndroidTaskScheduler.class.getName());
private static final long ALARM_MS = INTERVAL_FIFTEEN_MINUTES;
private final Application app;
private final AndroidWakeLockManager wakeLockManager;
private final ScheduledExecutorService scheduledExecutorService;
private final AlarmManager alarmManager;
private final Object lock = new Object();
@GuardedBy("lock")
private final Queue<ScheduledTask> tasks = new PriorityQueue<>();
AndroidTaskScheduler(Application app,
AndroidWakeLockManager wakeLockManager,
ScheduledExecutorService scheduledExecutorService) {
this.app = app;
this.wakeLockManager = wakeLockManager;
this.scheduledExecutorService = scheduledExecutorService;
alarmManager = (AlarmManager)
requireNonNull(app.getSystemService(ALARM_SERVICE));
}
@Override
public void startService() {
scheduleAlarm();
}
@Override
public void stopService() {
cancelAlarm();
}
@Override
public Cancellable schedule(Runnable task, Executor executor, long delay,
TimeUnit unit) {
AtomicBoolean cancelled = new AtomicBoolean(false);
return schedule(task, executor, delay, unit, cancelled);
}
@Override
public Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
long delay, long interval, TimeUnit unit) {
AtomicBoolean cancelled = new AtomicBoolean(false);
return scheduleWithFixedDelay(task, executor, delay, interval, unit,
cancelled);
}
@Override
public void onAlarm(Intent intent) {
wakeLockManager.runWakefully(() -> {
int extraPid = intent.getIntExtra(EXTRA_PID, -1);
int currentPid = Process.myPid();
if (extraPid == currentPid) {
LOG.info("Alarm");
rescheduleAlarm();
runDueTasks();
} else if (LOG.isLoggable(INFO)) {
LOG.info("Ignoring alarm with PID " + extraPid
+ ", current PID is " + currentPid);
}
}, "TaskAlarm");
}
private Cancellable schedule(Runnable task, Executor executor, long delay,
TimeUnit unit, AtomicBoolean cancelled) {
long now = SystemClock.elapsedRealtime();
long dueMillis = now + MILLISECONDS.convert(delay, unit);
Runnable wakeful = () ->
wakeLockManager.executeWakefully(task, executor, "TaskHandoff");
// Acquire the lock before scheduling the check to ensure the check
// doesn't access the task queue before the task has been added
ScheduledTask s;
synchronized (lock) {
Future<?> check = scheduleCheckForDueTasks(delay, unit);
s = new ScheduledTask(wakeful, dueMillis, check, cancelled);
tasks.add(s);
}
return s;
}
private Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
long delay, long interval, TimeUnit unit, AtomicBoolean cancelled) {
// All executions of this periodic task share a cancelled flag
Runnable wrapped = () -> {
task.run();
scheduleWithFixedDelay(task, executor, interval, interval, unit,
cancelled);
};
return schedule(wrapped, executor, delay, unit, cancelled);
}
@GuardedBy("lock")
private Future<?> scheduleCheckForDueTasks(long delay, TimeUnit unit) {
Runnable wakeful = () -> wakeLockManager.runWakefully(
this::runDueTasks, "TaskScheduler");
return scheduledExecutorService.schedule(wakeful, delay, unit);
}
@Wakeful
private void runDueTasks() {
long now = SystemClock.elapsedRealtime();
List<ScheduledTask> due = new ArrayList<>();
synchronized (lock) {
while (true) {
ScheduledTask s = tasks.peek();
if (s == null || s.dueMillis > now) break;
due.add(tasks.remove());
}
}
if (LOG.isLoggable(INFO)) {
LOG.info("Running " + due.size() + " due tasks");
}
for (ScheduledTask s : due) {
if (LOG.isLoggable(INFO)) {
LOG.info("Task is " + (now - s.dueMillis) + " ms overdue");
}
s.run();
}
}
private void scheduleAlarm() {
if (SDK_INT >= 23) scheduleIdleAlarm();
else scheduleInexactRepeatingAlarm();
}
private void rescheduleAlarm() {
// If SDK_INT < 23 the alarm repeats automatically
if (SDK_INT >= 23) scheduleIdleAlarm();
}
private void cancelAlarm() {
alarmManager.cancel(getAlarmPendingIntent());
}
private void scheduleInexactRepeatingAlarm() {
alarmManager.setInexactRepeating(ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + ALARM_MS, ALARM_MS,
getAlarmPendingIntent());
}
@TargetApi(23)
private void scheduleIdleAlarm() {
alarmManager.setAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + ALARM_MS,
getAlarmPendingIntent());
}
private PendingIntent getAlarmPendingIntent() {
Intent i = new Intent(app, AlarmReceiver.class);
i.putExtra(EXTRA_PID, android.os.Process.myPid());
return PendingIntent.getBroadcast(app, REQUEST_ALARM, i,
getImmutableFlags(FLAG_CANCEL_CURRENT));
}
private class ScheduledTask
implements Runnable, Cancellable, Comparable<ScheduledTask> {
private final Runnable task;
private final long dueMillis;
private final Future<?> check;
private final AtomicBoolean cancelled;
private ScheduledTask(Runnable task, long dueMillis,
Future<?> check, AtomicBoolean cancelled) {
this.task = task;
this.dueMillis = dueMillis;
this.check = check;
this.cancelled = cancelled;
}
@Override
public void run() {
if (!cancelled.get()) task.run();
}
@Override
public void cancel() {
// Cancel any future executions of this task
cancelled.set(true);
// Cancel the scheduled check for due tasks
check.cancel(false);
// Remove the task from the queue
synchronized (lock) {
tasks.remove(this);
}
}
@Override
public int compareTo(ScheduledTask s) {
//noinspection UseCompareMethod
if (dueMillis < s.dueMillis) return -1;
if (dueMillis > s.dueMillis) return 1;
return 0;
}
}
}

View File

@@ -1,49 +0,0 @@
package org.briarproject.bramble.system;
import android.app.Application;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.AlarmListener;
import org.briarproject.bramble.api.system.TaskScheduler;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class AndroidTaskSchedulerModule {
public static class EagerSingletons {
@Inject
AndroidTaskScheduler scheduler;
}
@Provides
@Singleton
AndroidTaskScheduler provideAndroidTaskScheduler(
LifecycleManager lifecycleManager, Application app,
AndroidWakeLockManager wakeLockManager,
ScheduledExecutorService scheduledExecutorService) {
AndroidTaskScheduler scheduler = new AndroidTaskScheduler(app,
wakeLockManager, scheduledExecutorService);
lifecycleManager.registerService(scheduler);
return scheduler;
}
@Provides
@Singleton
AlarmListener provideAlarmListener(AndroidTaskScheduler scheduler) {
return scheduler;
}
@Provides
@Singleton
TaskScheduler provideTaskScheduler(AndroidTaskScheduler scheduler) {
return scheduler;
}
}

View File

@@ -1,25 +0,0 @@
package org.briarproject.bramble.system;
import android.app.Application;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManagerFactory;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class AndroidWakeLockModule {
@Provides
@Singleton
AndroidWakeLockManager provideWakeLockManager(Application app,
ScheduledExecutorService scheduledExecutorService) {
return AndroidWakeLockManagerFactory.createAndroidWakeLockManager(app,
scheduledExecutorService);
}
}

View File

@@ -1,23 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.util.concurrent.Executor;
import dagger.Module;
import dagger.Provides;
@Module
public
class AndroidWakefulIoExecutorModule {
@Provides
@WakefulIoExecutor
Executor provideWakefulIoExecutor(@IoExecutor Executor ioExecutor,
AndroidWakeLockManager wakeLockManager) {
return r -> wakeLockManager.executeWakefully(r, ioExecutor,
"WakefulIoExecutor");
}
}

View File

@@ -2,59 +2,45 @@ package org.briarproject.bramble.util;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build; import android.os.Build;
import android.os.Looper;
import android.provider.Settings; import android.provider.Settings;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.logging.Logger; import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
import static android.content.Context.RECEIVER_EXPORTED;
import static android.content.Context.RECEIVER_NOT_EXPORTED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static android.os.Process.myPid;
import static android.os.Process.myUid;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.logging.Level.INFO; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
@NotNullByDefault @NotNullByDefault
public class AndroidUtils { public class AndroidUtils {
private static final Logger LOG =
getLogger(AndroidUtils.class.getName());
// Fake Bluetooth address returned by BluetoothAdapter on API 23 and later // Fake Bluetooth address returned by BluetoothAdapter on API 23 and later
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00"; private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
private static final String STORED_REPORTS = "dev-reports"; private static final String STORED_REPORTS = "dev-reports";
private static final String STORED_LOGCAT = "dev-logcat";
public static Collection<String> getSupportedArchitectures() { public static Collection<String> getSupportedArchitectures() {
return asList(Build.SUPPORTED_ABIS); List<String> abis = new ArrayList<>();
} if (SDK_INT >= 21) {
abis.addAll(asList(Build.SUPPORTED_ABIS));
public static boolean hasBtConnectPermission(Context ctx) { } else {
return SDK_INT < 31 || ctx.checkPermission(BLUETOOTH_CONNECT, myPid(), abis.add(Build.CPU_ABI);
myUid()) == PERMISSION_GRANTED; if (Build.CPU_ABI2 != null) abis.add(Build.CPU_ABI2);
}
return abis;
} }
public static String getBluetoothAddress(Context ctx, public static String getBluetoothAddress(Context ctx,
@@ -64,36 +50,17 @@ public class AndroidUtils {
public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx, public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx,
BluetoothAdapter adapter) { BluetoothAdapter adapter) {
// If we don't have permission to access the adapter's address, let
// the caller know we can't find it
if (!hasBtConnectPermission(ctx)) return new Pair<>("", "");
@SuppressLint("HardwareIds")
String address;
// Return the adapter's address if it's valid and not fake // Return the adapter's address if it's valid and not fake
try { @SuppressLint("HardwareIds")
address = adapter.getAddress(); String address = adapter.getAddress();
if (isValidBluetoothAddress(address)) { if (isValidBluetoothAddress(address)) {
return new Pair<>(address, "adapter"); return new Pair<>(address, "adapter");
}
} catch (SecurityException e) {
if (LOG.isLoggable(INFO)) {
LOG.info("Security exception when getting BT address: " +
e.getMessage());
}
} }
// Return the address from settings if it's valid and not fake // Return the address from settings if it's valid and not fake
if (SDK_INT < 33) { address = Settings.Secure.getString(ctx.getContentResolver(),
try { "bluetooth_address");
address = Settings.Secure.getString(ctx.getContentResolver(), if (isValidBluetoothAddress(address)) {
"bluetooth_address"); return new Pair<>(address, "settings");
if (isValidBluetoothAddress(address)) {
return new Pair<>(address, "settings");
}
} catch (SecurityException e) {
// Some custom ROMs throw this exception on SDK_INT < 33.
// Fall through
}
} }
// Try to get the address via reflection // Try to get the address via reflection
address = getBluetoothAddressByReflection(adapter); address = getBluetoothAddressByReflection(adapter);
@@ -104,7 +71,7 @@ public class AndroidUtils {
return new Pair<>("", ""); return new Pair<>("", "");
} }
public static boolean isValidBluetoothAddress(@Nullable String address) { private static boolean isValidBluetoothAddress(@Nullable String address) {
return !StringUtils.isNullOrEmpty(address) return !StringUtils.isNullOrEmpty(address)
&& BluetoothAdapter.checkBluetoothAddress(address) && BluetoothAdapter.checkBluetoothAddress(address)
&& !address.equals(FAKE_BLUETOOTH_ADDRESS); && !address.equals(FAKE_BLUETOOTH_ADDRESS);
@@ -140,42 +107,14 @@ public class AndroidUtils {
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE); return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
} }
public static File getLogcatFile(Context ctx) {
return new File(ctx.getFilesDir(), STORED_LOGCAT);
}
/** /**
* Returns an array of supported content types for image attachments. * Returns an array of supported content types for image attachments.
* GIFs can't be compressed on API < 24 so they're not supported.
* <p>
* TODO: Remove this restriction when large message support is added
*/ */
public static String[] getSupportedImageContentTypes() { public static String[] getSupportedImageContentTypes() {
return new String[] {"image/jpeg", "image/png", "image/gif"}; if (SDK_INT < 24) return new String[] {"image/jpeg", "image/png"};
} else return new String[] {"image/jpeg", "image/png", "image/gif"};
public static boolean isUiThread() {
return Looper.myLooper() == Looper.getMainLooper();
}
public static int getImmutableFlags(int flags) {
if (SDK_INT >= 23) {
return FLAG_IMMUTABLE | flags;
}
return flags;
}
/**
* Could be replaced to a similar call in ContextCompat once we
* use and upgrade to version 1.9.0 or higher of the AndroidX Core library.
*/
@Nullable
@SuppressLint("UnspecifiedRegisterReceiverFlag") // we specify where needed
public static Intent registerReceiver(Context ctx,
@Nullable BroadcastReceiver receiver, IntentFilter filter,
boolean export) {
if (SDK_INT >= 33) {
return ctx.registerReceiver(receiver, filter,
export ? RECEIVER_EXPORTED : RECEIVER_NOT_EXPORTED);
} else {
return ctx.registerReceiver(receiver, filter);
}
} }
} }

View File

@@ -0,0 +1,100 @@
package org.briarproject.bramble.util;
import android.os.PowerManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
@ThreadSafe
@NotNullByDefault
public class RenewableWakeLock {
private static final Logger LOG =
Logger.getLogger(RenewableWakeLock.class.getName());
/**
* Automatically release the lock this many milliseconds after it's due
* to have been replaced and released.
*/
private static final int SAFETY_MARGIN_MS = 10_000;
private final PowerManager powerManager;
private final ScheduledExecutorService scheduler;
private final int levelAndFlags;
private final String tag;
private final long durationMs;
private final Runnable renewTask;
private final Object lock = new Object();
@Nullable
private PowerManager.WakeLock wakeLock; // Locking: lock
@Nullable
private ScheduledFuture future; // Locking: lock
public RenewableWakeLock(PowerManager powerManager,
ScheduledExecutorService scheduler, int levelAndFlags, String tag,
long duration, TimeUnit timeUnit) {
this.powerManager = powerManager;
this.scheduler = scheduler;
this.levelAndFlags = levelAndFlags;
this.tag = tag;
durationMs = MILLISECONDS.convert(duration, timeUnit);
renewTask = this::renew;
}
public void acquire() {
if (LOG.isLoggable(INFO)) LOG.info("Acquiring wake lock " + tag);
synchronized (lock) {
if (wakeLock != null) {
LOG.info("Already acquired");
return;
}
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
wakeLock.setReferenceCounted(false);
wakeLock.acquire(durationMs + SAFETY_MARGIN_MS);
future = scheduler.schedule(renewTask, durationMs, MILLISECONDS);
}
}
private void renew() {
if (LOG.isLoggable(INFO)) LOG.info("Renewing wake lock " + tag);
synchronized (lock) {
if (wakeLock == null) {
LOG.info("Already released");
return;
}
PowerManager.WakeLock oldWakeLock = wakeLock;
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
wakeLock.setReferenceCounted(false);
wakeLock.acquire(durationMs + SAFETY_MARGIN_MS);
oldWakeLock.release();
future = scheduler.schedule(renewTask, durationMs, MILLISECONDS);
}
}
public void release() {
if (LOG.isLoggable(INFO)) LOG.info("Releasing wake lock " + tag);
synchronized (lock) {
if (wakeLock == null) {
LOG.info("Already released");
return;
}
if (future == null) throw new AssertionError();
future.cancel(false);
future = null;
wakeLock.release();
wakeLock = null;
}
}
}

View File

@@ -9,7 +9,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.imposters.ByteBuddyClassImposteriser; import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -44,7 +44,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
private AndroidAccountManager accountManager; private AndroidAccountManager accountManager;
public AndroidAccountManagerTest() { public AndroidAccountManagerTest() {
context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE); context.setImposteriser(ClassImposteriser.INSTANCE);
app = context.mock(Application.class); app = context.mock(Application.class);
applicationInfo = new ApplicationInfo(); applicationInfo = new ApplicationInfo();
applicationInfo.dataDir = testDir.getAbsolutePath(); applicationInfo.dataDir = testDir.getAbsolutePath();
@@ -87,10 +87,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
File potatoFile = new File(potatoDir, "file"); File potatoFile = new File(potatoDir, "file");
File filesDir = new File(testDir, "filesDir"); File filesDir = new File(testDir, "filesDir");
File externalCacheDir = new File(testDir, "externalCacheDir"); File externalCacheDir = new File(testDir, "externalCacheDir");
File externalCacheDir1 = new File(testDir, "externalCacheDir1");
File externalCacheDir2 = new File(testDir, "externalCacheDir2");
File externalMediaDir1 = new File(testDir, "externalMediaDir1");
File externalMediaDir2 = new File(testDir, "externalMediaDir2");
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(prefs).edit(); oneOf(prefs).edit();
@@ -113,12 +109,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
will(returnValue(cacheDir)); will(returnValue(cacheDir));
oneOf(app).getExternalCacheDir(); oneOf(app).getExternalCacheDir();
will(returnValue(externalCacheDir)); will(returnValue(externalCacheDir));
oneOf(app).getExternalCacheDirs();
will(returnValue(
new File[] {externalCacheDir1, externalCacheDir2}));
oneOf(app).getExternalMediaDirs();
will(returnValue(
new File[] {externalMediaDir1, externalMediaDir2}));
}}); }});
assertTrue(dbDir.mkdirs()); assertTrue(dbDir.mkdirs());
@@ -135,10 +125,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertTrue(potatoFile.createNewFile()); assertTrue(potatoFile.createNewFile());
assertTrue(filesDir.mkdirs()); assertTrue(filesDir.mkdirs());
assertTrue(externalCacheDir.mkdirs()); assertTrue(externalCacheDir.mkdirs());
assertTrue(externalCacheDir1.mkdirs());
assertTrue(externalCacheDir2.mkdirs());
assertTrue(externalMediaDir1.mkdirs());
assertTrue(externalMediaDir2.mkdirs());
accountManager.deleteAccount(); accountManager.deleteAccount();
@@ -156,10 +142,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertFalse(potatoFile.exists()); assertFalse(potatoFile.exists());
assertFalse(filesDir.exists()); assertFalse(filesDir.exists());
assertFalse(externalCacheDir.exists()); assertFalse(externalCacheDir.exists());
assertFalse(externalCacheDir1.exists());
assertFalse(externalCacheDir2.exists());
assertFalse(externalMediaDir1.exists());
assertFalse(externalMediaDir2.exists());
} }
@After @After

View File

@@ -1,171 +1,103 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'androidx.annotation:annotation:1.5.0:annotation-1.5.0.jar:261fb7c0210858500bab66d34354972a75166ab4182add283780b05513d6ec4a', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db', 'com.android.tools.analytics-library:protos:26.5.1:protos-26.5.1.jar:8dde1130725461fe827f2a343d353f2b51e8870661fc860d7d5ebddb097ead4e',
'com.android.tools.ddms:ddmlib:31.13.0:ddmlib-31.13.0.jar:839957f961100713ea0eed628a8684cc39aa479631c36249793e6df7e0cd63d8', 'com.android.tools.analytics-library:shared:26.5.1:shared-26.5.1.jar:ccc2f3b00ec17b11401610ba68553544fc8fc517120e84439ac6eb86b875e18d',
'com.android.tools.emulator:proto:31.13.0:proto-31.13.0.jar:b77f81cc0751d79393ec4b2eb046f910d21abcd7608b5b0f5a1efea1b3243b48', 'com.android.tools.analytics-library:tracker:26.5.1:tracker-26.5.1.jar:3a76984c0fe2e847ca7a8b35b4780ef0447a9d1666946cb8e60466318e0ab5ae',
'com.android.tools.utp:android-device-provider-ddmlib-proto:31.13.0:android-device-provider-ddmlib-proto-31.13.0.jar:047aecdd66e106137f77a52c442f1b83db7d6e8496899800251f206c7f39de65', 'com.android.tools.build:aapt2-proto:0.4.0:aapt2-proto-0.4.0.jar:fac0435e08898f89eeeb9ca236bea707155ff816c12205ced285ad53604133ca',
'com.android.tools.utp:android-device-provider-ddmlib:31.13.0:android-device-provider-ddmlib-31.13.0.jar:e6c2d4674077610e165524df6e0617b6af0f91358075854d57c1072f5fb0c245', 'com.android.tools.build:apksig:3.5.1:apksig-3.5.1.jar:1fd33e7f009a2a0da766cfeec4211a09f548034b015c289a66d75dd8a9302f4a',
'com.android.tools.utp:android-device-provider-profile-proto:31.13.0:android-device-provider-profile-proto-31.13.0.jar:3e7b098f6e3ecae31b6f7909c343b4ec09aa18d8a89f41bf92077ba4b056f453', 'com.android.tools.build:apkzlib:3.5.1:apkzlib-3.5.1.jar:9f330167cbe973b7db407692f74f4f6453b7ffa5f2048934b06280c2ceee60fa',
'com.android.tools.utp:android-device-provider-profile:31.13.0:android-device-provider-profile-31.13.0.jar:1aae6909dd10cf15d100f24871a5b9310fff6d2556d67c3808a1e1663ef471ca', 'com.android.tools.build:builder-model:3.5.1:builder-model-3.5.1.jar:39ea3c82b76b6e0c9f9fa88d93e0edc1dd4a0f1dfae0ef6fbf2d451da47e5450',
'com.android.tools.utp:android-test-plugin-host-additional-test-output-proto:31.13.0:android-test-plugin-host-additional-test-output-proto-31.13.0.jar:6ba7e6ac2208d74c1bb5f1d1464abafc6a45d8710b20455a2dc02adf8726bc83', 'com.android.tools.build:builder-test-api:3.5.1:builder-test-api-3.5.1.jar:a1b59305584cbcaa078fdc9cfb80871012755b822dd32e8da19add6f7bbcb762',
'com.android.tools.utp:android-test-plugin-host-additional-test-output:31.13.0:android-test-plugin-host-additional-test-output-31.13.0.jar:fc5f224db1b1d21642339dff08e3bcc9bb3b6753c4e6a7d5e750769a7cbbbeb4', 'com.android.tools.build:builder:3.5.1:builder-3.5.1.jar:e3a8d382434c5f60990730c4719fc814e85a898a33a1e96c1df8d627d3c6eea6',
'com.android.tools.utp:android-test-plugin-host-apk-installer-proto:31.13.0:android-test-plugin-host-apk-installer-proto-31.13.0.jar:4f2b610542e91a35a396b04368a784036e42b8787021460550b9a3495bb8245b', 'com.android.tools.build:gradle-api:3.5.1:gradle-api-3.5.1.jar:be9b41859bace11998f66b04ed944f87e413f3ad6da3c4665587699da125addc',
'com.android.tools.utp:android-test-plugin-host-apk-installer:31.13.0:android-test-plugin-host-apk-installer-31.13.0.jar:ad4a65f53ed841253b875b3f4232826ca83e92d2f8df2c757944824d9f9bb1e2', 'com.android.tools.build:manifest-merger:26.5.1:manifest-merger-26.5.1.jar:dcad9ecb967251f4d750f55a4204a2b400e8fbfe5cb930a1d0d5dbe10ae8bdfc',
'com.android.tools.utp:android-test-plugin-host-coverage-proto:31.13.0:android-test-plugin-host-coverage-proto-31.13.0.jar:fa86719a3dc5de465f7e0c023184414c27f8fd53a34fd557289c0bf6df340244', 'com.android.tools.ddms:ddmlib:26.5.1:ddmlib-26.5.1.jar:b081aef2a4ed3f4d47cae4cdb128469735f25a114e026d37123bf9ffdec742a8',
'com.android.tools.utp:android-test-plugin-host-coverage:31.13.0:android-test-plugin-host-coverage-31.13.0.jar:6ef641e612a91dcf5c11eeec7e183ae616bccf18db968f8593c32c215ba45e6b', 'com.android.tools.external.com-intellij:intellij-core:26.5.1:intellij-core-26.5.1.jar:20eced30adc124805bd93488d9cd9d3e33e6bf7b48e9fe5a703d4983f894d450',
'com.android.tools.utp:android-test-plugin-host-device-info-proto:31.13.0:android-test-plugin-host-device-info-proto-31.13.0.jar:9683ac7648a7a41be9a1349f6981592944f627164898c3c8925a0beede8bb8bb', 'com.android.tools.external.com-intellij:kotlin-compiler:26.5.1:kotlin-compiler-26.5.1.jar:5aed762dd54875b77ae7018d97c05756ff0c5b9fd02ec595dd396ccd14cc22cb',
'com.android.tools.utp:android-test-plugin-host-device-info:31.13.0:android-test-plugin-host-device-info-31.13.0.jar:bbf196a617d5f501f2696bfeabbee4b687b4e809d8d56d9c74610094f8a007cb', 'com.android.tools.external.org-jetbrains:uast:26.5.1:uast-26.5.1.jar:4bc8653d6c0943f40fee963a149e36c6baa45683d2530968a13f5007e3c40740',
'com.android.tools.utp:android-test-plugin-host-emulator-control-proto:31.13.0:android-test-plugin-host-emulator-control-proto-31.13.0.jar:a4f34aae0f9ffa026dbf7151436dd7ae53becb72622b40f2c479cac8943d9319', 'com.android.tools.layoutlib:layoutlib-api:26.5.1:layoutlib-api-26.5.1.jar:88732f11396c427273e515d23042e35633f4fe4295528a99b866aa2adf0efd9c',
'com.android.tools.utp:android-test-plugin-host-emulator-control:31.13.0:android-test-plugin-host-emulator-control-31.13.0.jar:43f5a73b559758c1d3a119277a109ea552a3e4f75368842fb79af7308ad22465', 'com.android.tools.lint:lint-api:26.5.1:lint-api-26.5.1.jar:ec33fcd72bfaf70dd841e03fbfd93f109c2e575aec146067c606689c3972f0de',
'com.android.tools.utp:android-test-plugin-host-logcat-proto:31.13.0:android-test-plugin-host-logcat-proto-31.13.0.jar:c1f6ebbacdad559b6efe4eaa29561552b33156395f069cd9703fda09c462dea6', 'com.android.tools.lint:lint-checks:26.5.1:lint-checks-26.5.1.jar:a1b9607d484aaae7a71dcecdc76f8003d8239af226c776894a2cf63f9e6c60d7',
'com.android.tools.utp:android-test-plugin-host-logcat:31.13.0:android-test-plugin-host-logcat-31.13.0.jar:be7ccad0a7b8d062782b769030e17d573e96d302196b2f9985ec51cb42b6b76b', 'com.android.tools.lint:lint-gradle-api:26.5.1:lint-gradle-api-26.5.1.jar:82453fd98a8394cc84ed995c04d2cd744abd1d6589403427ba7eef53115406f3',
'com.android.tools.utp:android-test-plugin-result-listener-gradle-proto:31.13.0:android-test-plugin-result-listener-gradle-proto-31.13.0.jar:d429b9312dffa0503381d1ee1b18a999bd901e7456612b2fb48c6a5d5a2caf88', 'com.android.tools.lint:lint-gradle:26.5.1:lint-gradle-26.5.1.jar:59465b56cf7db77c656d5f8195d721c3d48b6bdd0502d774de335bfe4baff00b',
'com.android.tools.utp:android-test-plugin-result-listener-gradle:31.13.0:android-test-plugin-result-listener-gradle-31.13.0.jar:a1b95cbed5fd4b8c6a44e7db4642f1acc87f5bff956350e8ae644b505029c120', 'com.android.tools.lint:lint:26.5.1:lint-26.5.1.jar:336e4b04ec6f8b0f25879131b7a7862d77df83a1879ee5b71be26128755f8e2e',
'com.android.tools.utp:utp-common:31.13.0:utp-common-31.13.0.jar:cde678a64b13041cdd2cc9dad1685990a1d090fb04ff5da2261ff75a83598106', 'com.android.tools:annotations:26.5.1:annotations-26.5.1.jar:2c43c82f8c59d8f7a61e3239e1a2dc9f69dc342ec09af9b7c9f69b25337c0b6e',
'com.android.tools:annotations:31.13.0:annotations-31.13.0.jar:3b4bb9620c17d19e5bd91ac1988080553573b4c3b739fdd92416f42f2daf3e78', 'com.android.tools:common:26.5.1:common-26.5.1.jar:eccfa54486ed54c4e3123cc42195d023bd0dd21bcd2f0e4868e8c6fc70f8ef6b',
'com.android.tools:common:31.13.0:common-31.13.0.jar:b4b6f4ba94843c86e1365f294d9085b5f4f14f63fccd0f0e10da7fdbfa4c3d04', 'com.android.tools:dvlib:26.5.1:dvlib-26.5.1.jar:46f93ad498b4756e7d867d2fe38c38890a80e7407a4ae459e4a8c8d5c5aeacfe',
'com.google.android:annotations:4.1.1.4:annotations-4.1.1.4.jar:ba734e1e84c09d615af6a09d33034b4f0442f8772dec120efb376d86a565ae15', 'com.android.tools:repository:26.5.1:repository-26.5.1.jar:2b3ee791aa4c3e8ce60498c161a27ca7228816fc630eed4d9f25f2f36a106dce',
'com.google.api.grpc:proto-google-common-protos:2.17.0:proto-google-common-protos-2.17.0.jar:4ef1fe0c327fc1521d1d753b0b1c4a875a54bd14ebded3afff0ca395320b6ea9', 'com.android.tools:sdk-common:26.5.1:sdk-common-26.5.1.jar:365f749676c3574676fd465177c8a492f340816db2b520d6ed114d3b6e77bea7',
'com.google.api.grpc:proto-google-common-protos:2.48.0:proto-google-common-protos-2.48.0.jar:43ec7807459aaa4012e838a1be4ef2d590cf233305da60af5b54f08ec8cf2302', 'com.android.tools:sdklib:26.5.1:sdklib-26.5.1.jar:007da104afb27c8c682a1628023fe9ec438249c8d15ef0fd6624c5bb8e23b696',
'com.google.auto.service:auto-service-annotations:1.1.1:auto-service-annotations-1.1.1.jar:16a76dd00a2650568447f5d6e3a9e2c809d9a42367d56b45215cfb89731f4d24', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.auto.service:auto-service:1.1.1:auto-service-1.1.1.jar:1f48f451503e623daba7d9ed368cca0f81e1e3815653a4560113e12c0129ebd5', 'com.google.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81',
'com.google.auto:auto-common:1.2.1:auto-common-1.2.1.jar:f43f29fe2a6ebaf04b2598cdeec32a4e346d49a9404e990f5fc19c19f3a28d0e', 'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f',
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79', 'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.dagger:dagger-spi:2.24:dagger-spi-2.24.jar:c038445d14dbcb4054e61bf49e05009edf26fce4fdc7ec1a9db544784f68e718',
'com.google.code.gson:gson:2.10.1:gson-2.10.1.jar:4241c14a7727c34feea6507ec801318a3d4a90f070e4525681079fb94ee4c593', 'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64',
'com.google.code.gson:gson:2.11.0:gson-2.11.0.jar:57928d6e5a6edeb2abd3770a8f95ba44dce45f3b23b7a9dc2b309c581552a78b', 'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a',
'com.google.code.gson:gson:2.8.9:gson-2.8.9.jar:d3999291855de495c94c743761b8ab5176cfeabe281a5ab0d8e8d45326fd703e', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.crypto.tink:tink:1.7.0:tink-1.7.0.jar:88970a456a08ba4c66b01b23e5846ca1095cc14e54cb48363e5d2e15a1307308', 'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.dagger:dagger-compiler:2.51.1:dagger-compiler-2.51.1.jar:14cf2def1c4c8cd3b977840e297b463191d537cd1c8330992ca5c0b341a641ad', 'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26',
'com.google.dagger:dagger-spi:2.51.1:dagger-spi-2.51.1.jar:deb52030b92b27c5dcd76b2c0747f1cf105b60939f6073b43eb06cfe7c9ba601', 'com.google.guava:guava:27.0.1-jre:guava-27.0.1-jre.jar:e1c814fd04492a27c38e0317eabeaa1b3e950ec8010239e400fe90ad6c9107b4',
'com.google.dagger:dagger:2.48:dagger-2.48.jar:1fa226d2b4a02cc80950fa4d49a4a235cc8eced499b581fc358a55446a83f579', 'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
'com.google.dagger:dagger:2.51.1:dagger-2.51.1.jar:c3891a4c4a4e48682888ca321eaf8497004b286e1d9a2936867373219f7dd86d', 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
'com.google.devtools.ksp:symbol-processing-api:1.9.20-1.0.14:symbol-processing-api-1.9.20-1.0.14.jar:d0339396f40dc9eb3b3f7bc86257f93869ee23448fa31ec4a1de900c6b7ae6d7', 'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
'com.google.errorprone:error_prone_annotations:2.23.0:error_prone_annotations-2.23.0.jar:ec6f39f068b6ff9ac323c68e28b9299f8c0a80ca512dccb1d4a70f40ac3ec054', 'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
'com.google.errorprone:error_prone_annotations:2.28.0:error_prone_annotations-2.28.0.jar:f3fc8a3a0a4020706a373b00e7f57c2512dd26d1f83d28c7d38768f8682b231e', 'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
'com.google.errorprone:error_prone_annotations:2.30.0:error_prone_annotations-2.30.0.jar:144f3aefbd6e27daec55d3753b2c6b13c1afdaf0cf04816cdb564588ed92f1bd', 'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e', 'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26', 'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
'com.google.guava:failureaccess:1.0.2:failureaccess-1.0.2.jar:8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064', 'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4',
'com.google.guava:guava:32.0.1-jre:guava-32.0.1-jre.jar:bd7fa227591fb8509677d0d1122cf95158f3b8a9f45653f58281d879f6dc48c5', 'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
'com.google.guava:guava:33.0.0-jre:guava-33.0.0-jre.jar:f4d85c3e4d411694337cb873abea09b242b664bb013320be6105327c45991537', 'commons-codec:commons-codec:1.10:commons-codec-1.10.jar:4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569',
'com.google.guava:guava:33.3.1-jre:guava-33.3.1-jre.jar:4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90', 'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99', 'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
'com.google.j2objc:j2objc-annotations:2.8:j2objc-annotations-2.8.jar:f02a95fa1a5e95edb3ed859fd0fb7df709d121a35290eff8b74dce2ab7f4d6ed', 'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'com.google.j2objc:j2objc-annotations:3.0.0:j2objc-annotations-3.0.0.jar:88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.google.protobuf:protobuf-java-util:3.22.3:protobuf-java-util-3.22.3.jar:c615f76879dc5c303e4df5b94a6afa39534058c7545db2d483fd95d9f63c8bfe', 'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
'com.google.protobuf:protobuf-java-util:3.24.4:protobuf-java-util-3.24.4.jar:133c929e2cfe3990a105d18eaccc49122b2d2fb492b420ef02d5d9f937eaebb8', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'com.google.protobuf:protobuf-java:3.24.4:protobuf-java-3.24.4.jar:e5655522be1aa5cc1f2f092aa036b0445157f294928eedf1332ac938c7b69686', 'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'com.google.protobuf:protobuf-java:3.25.5:protobuf-java-3.25.5.jar:8540247fad9e06baefa8fb45eb313802d019f485f14300e0f9d6b556ed88e753', 'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
'com.google.protobuf:protobuf-kotlin:3.24.4:protobuf-kotlin-3.24.4.jar:508ca13d97b50f5404eaa37eb4493cb07884162eb7971bf924d8f803d4c21bb4', 'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
'com.google.testing.platform:android-device-provider-local:0.0.9-alpha03:android-device-provider-local-0.0.9-alpha03.jar:667a4d35bbba87d3c86f5180dfa521fdbd7a4ef5c60d949154b0301f3e232e1b', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'com.google.testing.platform:android-driver-instrumentation:0.0.9-alpha03:android-driver-instrumentation-0.0.9-alpha03.jar:507c632ec7db77bcb299b5519d59b14cc6243aac541767c632fdbeddc6226b07', 'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'com.google.testing.platform:android-test-plugin:0.0.9-alpha03:android-test-plugin-0.0.9-alpha03.jar:d6cb7e126f433037190bcd3c3b904b19ba842d46b17b0fd27c38cc4ccecbec90', 'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
'com.google.testing.platform:core-proto:0.0.9-alpha03:core-proto-0.0.9-alpha03.jar:d001eb0ccbbfc8cb9eaa193a358e63712974639775647be949ab232c2b29b407', 'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7',
'com.google.testing.platform:core:0.0.9-alpha03:core-0.0.9-alpha03.jar:6e1806d015c416596f53a45a3100e25743c313a6e3fc4f52f24e8b257f2c82ce', 'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
'com.google.testing.platform:launcher:0.0.9-alpha03:launcher-0.0.9-alpha03.jar:0012f980a059a0c4c216d0f1d0016867ab31eb8079e3f8f879f1f02b7be3a6e7', 'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e',
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'com.squareup:kotlinpoet:1.11.0:kotlinpoet-1.11.0.jar:2887ada1ca03dd83baa2758640d87e840d1907564db0ef88d2289c868a980492', 'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
'commons-io:commons-io:2.16.1:commons-io-2.16.1.jar:f41f7baacd716896447ace9758621f62c1c6b0a91d89acee488da26fc477c84f', 'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
'io.grpc:grpc-api:1.57.2:grpc-api-1.57.2.jar:42b72e6572c084055ac3ce03e6efe433eb05ef620b3daf5136a4359fc72cc3e1', 'org.briarproject:obfs4proxy-android:0.0.11-2:obfs4proxy-android-0.0.11-2.zip:57e55cbe87aa2aac210fdbb6cd8cdeafe15f825406a08ebf77a8b787aa2c6a8a',
'io.grpc:grpc-api:1.69.1:grpc-api-1.69.1.jar:a8d3d6dcc71f3ab613d668842282b488bdd93d3e99a0ef5dca7eee6fa734c283', 'org.briarproject:tor-android:0.3.5.9:tor-android-0.3.5.9.zip:853b0440feccd6904bd03e6b2de53a62ebcde1d58068beeadc447a7dff950bc8',
'io.grpc:grpc-context:1.57.2:grpc-context-1.57.2.jar:9b8ac88d9cef2819daffed7bdbd2f22680237d482c6c671fe02d36da3f08cf00', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'io.grpc:grpc-context:1.69.1:grpc-context-1.69.1.jar:45ef95b8c158a8b5bdd3acb67b9e682ef25414bb148f488ec847438ab64715d4', 'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
'io.grpc:grpc-core:1.57.2:grpc-core-1.57.2.jar:5a10070abfeb4966ec4d580961dcc4e7f69fa83ab25242f92c1765efb07b8606', 'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0',
'io.grpc:grpc-core:1.69.1:grpc-core-1.69.1.jar:51352cadaecbf9a4a4aa42d93f6f1fc728f1fd01b051680383ed09e5631ffbd0', 'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
'io.grpc:grpc-inprocess:1.69.1:grpc-inprocess-1.69.1.jar:b7c6ac0e3abf4b8d582610d632d79417bc3da81254e1a4bcf7f01e8db7bd55ef', 'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
'io.grpc:grpc-netty:1.57.2:grpc-netty-1.57.2.jar:9809d4c10c94d11e7b2946cdeb5b2884be20a09510289544f37569f02c877a21', 'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
'io.grpc:grpc-netty:1.69.1:grpc-netty-1.69.1.jar:52a86ed66f78933e83d1a3fb7162ad1667489564c4556366b7a3579c7024a447', 'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
'io.grpc:grpc-protobuf-lite:1.57.2:grpc-protobuf-lite-1.57.2.jar:fc4917dc5d419ac810fb3f27523c14e75e1fe50372154fab29324215ee6a955a', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'io.grpc:grpc-protobuf-lite:1.69.1:grpc-protobuf-lite-1.69.1.jar:c29f90fadf3c7620f9359a243c067dd85b73bd765b28f3d95df910ac2d331555', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'io.grpc:grpc-protobuf:1.57.2:grpc-protobuf-1.57.2.jar:31630d8a9e9f08a959862015e30a4863908be3680c3a686f4c1f08d2ffeaf706', 'org.jetbrains.kotlin:kotlin-reflect:1.3.50:kotlin-reflect-1.3.50.jar:64583199ea5a54aefd1bd1595288925f784226ee562d1dd279011c6075b3d7a4',
'io.grpc:grpc-protobuf:1.69.1:grpc-protobuf-1.69.1.jar:4c52ef948fb8987a3baa7d46ba362b7bf307dd3c51f29241cd5c598398a010df', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50:kotlin-stdlib-common-1.3.50.jar:8ce678e88e4ba018b66dacecf952471e4d7dfee156a8a819760a5a5ff29d323c',
'io.grpc:grpc-services:1.57.2:grpc-services-1.57.2.jar:057a43ba647833756ab2f851bac80a3b9f8dce026fc1ac1f17d4f315648f4172', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50:kotlin-stdlib-jdk7-1.3.50.jar:9a026639e76212f8d57b86d55b075394c2e009f1979110751d34c05c5f75d57b',
'io.grpc:grpc-stub:1.57.2:grpc-stub-1.57.2.jar:84d2af12719168f76375f2afdfd6eb5133a865edba9244d40e6b968e3adde1d3', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50:kotlin-stdlib-jdk8-1.3.50.jar:1b351fb6e09c14b55525c74c1f4cf48942eae43c348b7bc764a5e6e423d4da0c',
'io.grpc:grpc-stub:1.69.1:grpc-stub-1.69.1.jar:e39c63273d53052ebe9f638d8ae98176735ec567328d9a17092cddb6f239b8c2', 'org.jetbrains.kotlin:kotlin-stdlib:1.3.50:kotlin-stdlib-1.3.50.jar:e6f05746ee0366d0b52825a090fac474dcf44082c9083bbb205bd16976488d6c',
'io.grpc:grpc-util:1.69.1:grpc-util-1.69.1.jar:dd597bd675eaa042f3e3578648d9050c813c4595c5de6869ef9ddbb449006031', 'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'io.netty:netty-buffer:4.1.110.Final:netty-buffer-4.1.110.Final.jar:46d74e79125aacc055c31f18152fdc5d4a569aa8d60091203d0baa833973ac3c', 'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'io.netty:netty-buffer:4.1.93.Final:netty-buffer-4.1.93.Final.jar:007c7d9c378df02d390567d0d7ddf542ffddb021b7313dbf502392113ffabb08', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
'io.netty:netty-codec-http2:4.1.110.Final:netty-codec-http2-4.1.110.Final.jar:b546c75445a487bb7bcd5a94779caecce33582cf7be31b8b39fc0e65b1ee26fc', 'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
'io.netty:netty-codec-http2:4.1.93.Final:netty-codec-http2-4.1.93.Final.jar:d96cc09045a1341c6d47494352aa263b87b72fb1d2ea9eca161aa73820bfe8bb', 'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'io.netty:netty-codec-http:4.1.110.Final:netty-codec-http-4.1.110.Final.jar:dc0d6af5054630a70ff0ef354f20aa7a6e46738c9fc5636ed3d4fe77e38bd48d', 'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'io.netty:netty-codec-http:4.1.93.Final:netty-codec-http-4.1.93.Final.jar:dacf78ce78ab2d29570325db4cd2451ea589639807de95881a0fa7155a9e6b55', 'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
'io.netty:netty-codec-socks:4.1.110.Final:netty-codec-socks-4.1.110.Final.jar:976052a3c9bb280bc6d99f3a29e6404677cf958c3de05b205093d38c006b880c', 'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'io.netty:netty-codec-socks:4.1.93.Final:netty-codec-socks-4.1.93.Final.jar:0ea47b5ba23ca1da8eb9146c8fc755c1271414633b1e2be2ce1df764ba0fff2a', 'org.ow2.asm:asm-analysis:6.0:asm-analysis-6.0.jar:2f1a6387219c3a6cc4856481f221b03bd9f2408a326d416af09af5d6f608c1f4',
'io.netty:netty-codec:4.1.110.Final:netty-codec-4.1.110.Final.jar:9eccce9a8d827bb8ce84f9c3183fec58bd1c96a51010cf711297746034af3701', 'org.ow2.asm:asm-commons:6.0:asm-commons-6.0.jar:f1bce5c648a96a017bdcd01fe5d59af9845297fd7b79b81c015a6fbbd9719abf',
'io.netty:netty-codec:4.1.93.Final:netty-codec-4.1.93.Final.jar:990c378168dc6364c6ff569701f4f2f122fffe8998b3e189eba4c4d868ed1084', 'org.ow2.asm:asm-tree:6.0:asm-tree-6.0.jar:887998fb69727c8759e4d253f856822801e33f9fd4caa566b3ac58ee92106215',
'io.netty:netty-common:4.1.110.Final:netty-common-4.1.110.Final.jar:9851ec66548b9e0d41164ce98943cdd4bbe305f68ddbd24eae52e4501a0d7b1a', 'org.ow2.asm:asm-util:6.0:asm-util-6.0.jar:356afebdb0f870175262e5188f8709a3b17aa2a5a6a4b0340b04d4b449bca5f6',
'io.netty:netty-common:4.1.93.Final:netty-common-4.1.93.Final.jar:443bb316599fb16e3baeba2fb58881814d7ff0b7af176fe76e38071a6e86f8c0', 'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
'io.netty:netty-handler-proxy:4.1.110.Final:netty-handler-proxy-4.1.110.Final.jar:ad54ab4fe9c47ef3e723d71251126db53e8db543871adb9eafc94446539eff52', 'org.ow2.asm:asm:6.0:asm-6.0.jar:dd8971c74a4e697899a8e95caae4ea8760ea6c486dc6b97b1795e75760420461',
'io.netty:netty-handler-proxy:4.1.93.Final:netty-handler-proxy-4.1.93.Final.jar:2ac5f7fbefa0b73ef783889069344d5515505a14b2303be693c5002c486df2b4', ]
'io.netty:netty-handler:4.1.110.Final:netty-handler-4.1.110.Final.jar:d5a08d7de364912e4285968de4d4cce3f01da4bb048d5c6937e5f2af1f8e148a',
'io.netty:netty-handler:4.1.93.Final:netty-handler-4.1.93.Final.jar:4e5f563ae14ed713381816d582f5fcfd0615aefb29203486cdfb782d8a00a02b',
'io.netty:netty-resolver:4.1.110.Final:netty-resolver-4.1.110.Final.jar:a2e9b4ae7caa92fc5bd747e11d1dec20d81b18fc00959554302244ac5c56ce70',
'io.netty:netty-resolver:4.1.93.Final:netty-resolver-4.1.93.Final.jar:e59770b66e81822e5d111ac4e544d7eb0c543e0a285f52628e53941acd8ed759',
'io.netty:netty-transport-native-unix-common:4.1.110.Final:netty-transport-native-unix-common-4.1.110.Final.jar:51717bb7471141950390c6713a449fdb1054d07e60737ee7dda7083796cdee48',
'io.netty:netty-transport-native-unix-common:4.1.93.Final:netty-transport-native-unix-common-4.1.93.Final.jar:774165a1c4dbaacb17f9c1ad666b3569a6a59715ae828e7c3d47703f479a53e7',
'io.netty:netty-transport:4.1.110.Final:netty-transport-4.1.110.Final.jar:a42dd68390ca14b4ff2d40628a096c76485b4adb7c19602d5289321a0669e704',
'io.netty:netty-transport:4.1.93.Final:netty-transport-4.1.93.Final.jar:a5a78019bc1cd43dbc3c7b7cdd3801912ca26d1f498fb560514fee497864ba96',
'io.opencensus:opencensus-api:0.31.0:opencensus-api-0.31.0.jar:702ba55d78f39d55195dcf041fdfaab7a7490a9ac45013542487ed9e4d3a4d23',
'io.opencensus:opencensus-proto:0.2.0:opencensus-proto-0.2.0.jar:0c192d451e9dd74e98721b27d02f0e2b6bca44b51563b5dabf2e211f7a3ebf13',
'io.perfmark:perfmark-api:0.26.0:perfmark-api-0.26.0.jar:b7d23e93a34537ce332708269a0d1404788a5b5e1949e82f5535fce51b3ea95b',
'io.perfmark:perfmark-api:0.27.0:perfmark-api-0.27.0.jar:c7b478503ec524e55df19b424d46d27c8a68aeb801664fadd4f069b71f52d0f6',
'javax.annotation:javax.annotation-api:1.3.2:javax.annotation-api-1.3.2.jar:e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b',
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
'net.java.dev.jna:jna-platform:5.6.0:jna-platform-5.6.0.jar:9ecea8bf2b1b39963939d18b70464eef60c508fed8820f9dcaba0c35518eabf7',
'net.java.dev.jna:jna:5.6.0:jna-5.6.0.jar:5557e235a8aa2f9766d5dc609d67948f2a8832c2d796cea9ef1d6cbe0b3b7eaf',
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
'org.briarproject:dont-kill-me-lib:0.2.8:dont-kill-me-lib-0.2.8.aar:e21173e480ee3f2364c142cc14db8dc6447be91bde9e62e4985c485ea0af9126',
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
'org.briarproject:lyrebird-android:0.6.2:lyrebird-android-0.6.2.jar:2d70a38393ee6f1760a65a33dd971210efa06b5a355ebea829196b61fd9fd11a',
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
'org.briarproject:onionwrapper-android:0.1.4:onionwrapper-android-0.1.4.aar:15231f0b2ad44df8eb1dd362a989ba3f88ebdc9b02a9128daa72a8da83651bf0',
'org.briarproject:onionwrapper-core:0.1.4:onionwrapper-core-0.1.4.jar:d0a48de7198d48eb0182a25d109bc06811484d1da799c6dfa5e2581285880a49',
'org.briarproject:tor-android:0.4.8.21:tor-android-0.4.8.21.jar:0b797b2a4f07430b4465f784ee579c8c19b5def921a655caba4622b232ae2118',
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
'org.checkerframework:checker-qual:3.33.0:checker-qual-3.33.0.jar:e316255bbfcd9fe50d165314b85abb2b33cb2a66a93c491db648e498a82c2de1',
'org.checkerframework:checker-qual:3.41.0:checker-qual-3.41.0.jar:2f9f245bf68e4259d610894f2406dc1f6363dc639302bd566e8272e4f4541172',
'org.checkerframework:checker-qual:3.43.0:checker-qual-3.43.0.jar:3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6',
'org.codehaus.mojo:animal-sniffer-annotations:1.23:animal-sniffer-annotations-1.23.jar:9ffe526bf43a6348e9d8b33b9cd6f580a7f5eed0cf055913007eda263de974d0',
'org.codehaus.mojo:animal-sniffer-annotations:1.24:animal-sniffer-annotations-1.24.jar:c720e6e5bcbe6b2f48ded75a47bccdb763eede79d14330102e0d352e3d89ed92',
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
'org.jetbrains.kotlin:kotlin-reflect:1.6.10:kotlin-reflect-1.6.10.jar:3277ac102ae17aad10a55abec75ff5696c8d109790396434b496e75087854203',
'org.jetbrains.kotlin:kotlin-reflect:1.8.21:kotlin-reflect-1.8.21.jar:8a6cd5a3cf092acee274ce2c444dc36eefdb631579859dd4d857b3309a529c91',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.8.21:kotlin-stdlib-common-1.8.21.jar:6a44c9ecc9d7754d9e943fb1e3588c74d4a3f1785be51074f49d6c5723682a73',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.9.0:kotlin-stdlib-common-1.9.0.jar:283274204bd7c020789ec46f8f8e72af4244d7f550b3392a57e5ca006ad7aa2c',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.9.10:kotlin-stdlib-common-1.9.10.jar:cde3341ba18a2ba262b0b7cf6c55b20c90e8d434e42c9a13e6a3f770db965a88',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20:kotlin-stdlib-jdk7-1.8.20.jar:af1ec40c3b951afdcc0c2a0173c7b81763c5281c2d5bafbf0a8544a24c5dcc0c',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0:kotlin-stdlib-jdk7-1.9.0.jar:b7979a7aac94055f0d9f1fd3b47ce5ffe1cb6032a842ba9fbe7186f085289178',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10:kotlin-stdlib-jdk7-1.9.10.jar:ac6361bf9ad1ed382c2103d9712c47cdec166232b4903ed596e8876b0681c9b7',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.0:kotlin-stdlib-jdk7-2.2.0.jar:0d10bc0d42b8605f23629a3f31ea27c19cdbca9dcdf4f53f6d22cd6366836d18',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20:kotlin-stdlib-jdk8-1.8.20.jar:e398b67977622718bf18ff99b739c7d9da060f33fb458a2e25203221c16af010',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0:kotlin-stdlib-jdk8-1.9.0.jar:a59fa24fdf1ffb594baecdbf0fd10010f977cea10236d487fe3464977a7377fa',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.0:kotlin-stdlib-jdk8-2.2.0.jar:adc16648dbbcf35b0d10e7ec301c35d746d1c2fe460c606aba59f12b117cf9b0',
'org.jetbrains.kotlin:kotlin-stdlib:1.8.21:kotlin-stdlib-1.8.21.jar:042a1cd1ac976cdcfe5eb63f1d8e0b0b892c9248e15a69c8cfba495d546ea52a',
'org.jetbrains.kotlin:kotlin-stdlib:1.9.0:kotlin-stdlib-1.9.0.jar:35aeffbe2db5aa446072cee50fcee48b7fa9e2fc51ca37c0cc7d7d0bc39d952e',
'org.jetbrains.kotlin:kotlin-stdlib:1.9.10:kotlin-stdlib-1.9.10.jar:55e989c512b80907799f854309f3bc7782c5b3d13932442d0379d5c472711504',
'org.jetbrains.kotlin:kotlin-stdlib:1.9.20:kotlin-stdlib-1.9.20.jar:28a35bcdff46d864f80f346a617e486284b208d17378c41900dfb1de95a90e6c',
'org.jetbrains.kotlin:kotlin-stdlib:2.2.0:kotlin-stdlib-2.2.0.jar:65d12d85a3b865c160db9147851712a64b10dadd68b22eea22a95bf8a8670dca',
'org.jetbrains.kotlinx:atomicfu-jvm:0.22.0:atomicfu-jvm-0.22.0.jar:2da073727f3ab5e5584e74c12e11519c908ae2dfaf6aeb25ded42b6682297882',
'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3:kotlinx-coroutines-core-jvm-1.7.3.jar:1ab3acc38f3e7355c4f9d1ec62107a46fa73c899f3070d055e5d4373dfe67e12',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jetbrains:annotations:23.0.0:annotations-23.0.0.jar:7b0f19724082cbfcbc66e5abea2b9bc92cf08a1ea11e191933ed43801eb3cd05',
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b',
'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd',
'org.jmock:jmock-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04',
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
]
} }

View File

@@ -7,16 +7,13 @@ apply plugin: 'witness'
apply from: 'witness.gradle' apply from: 'witness.gradle'
dependencies { dependencies {
api 'org.briarproject:null-safety:0.1' implementation "com.google.dagger:dagger:2.24"
api 'com.google.code.findbugs:jsr305:3.0.2' implementation 'com.google.code.findbugs:jsr305:3.0.2'
api 'javax.inject:javax.inject:1'
api "com.google.dagger:dagger:$dagger_version"
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version" testImplementation 'junit:junit:4.12'
testImplementation "org.jmock:jmock:2.8.2"
testImplementation "junit:junit:$junit_version" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock:$jmock_version" testImplementation "org.jmock:jmock-legacy:2.8.2"
testImplementation "org.jmock:jmock-junit4:$jmock_version"
signature 'org.codehaus.mojo.signature:java16:1.1@signature' signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }
@@ -26,8 +23,8 @@ configurations {
testOutput.extendsFrom(testCompile) testOutput.extendsFrom(testCompile)
} }
task jarTest(type: Jar, dependsOn: testClasses) { task jarTest(type: Jar, dependsOn: testClasses) {
from sourceSets.test.output, sourceSets.main.output from sourceSets.test.output
archiveClassifier = 'test' classifier = 'test'
} }
artifacts { artifacts {
testOutput jarTest testOutput jarTest

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.api; package org.briarproject.bramble.api;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.briarproject.nullsafety.NotNullByDefault;
import java.util.Arrays; import java.util.Arrays;

View File

@@ -1,6 +0,0 @@
package org.briarproject.bramble.api;
public interface Cancellable {
void cancel();
}

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api;
import org.briarproject.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface Consumer<T> {
void accept(T t);
}

View File

@@ -6,14 +6,4 @@ package org.briarproject.bramble.api;
public interface FeatureFlags { public interface FeatureFlags {
boolean shouldEnableImageAttachments(); boolean shouldEnableImageAttachments();
boolean shouldEnableProfilePictures();
boolean shouldEnableDisappearingMessages();
boolean shouldEnablePrivateGroupsInCore();
boolean shouldEnableForumsInCore();
boolean shouldEnableBlogsInCore();
} }

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.api; package org.briarproject.bramble.api;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.api; package org.briarproject.bramble.api;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface Nameable { public interface Nameable {

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.api; package org.briarproject.bramble.api;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.api; package org.briarproject.bramble.api;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface Predicate<T> { public interface Predicate<T> {

View File

@@ -1,14 +1,8 @@
package org.briarproject.bramble.api; package org.briarproject.bramble.api;
import org.briarproject.bramble.util.StringUtils;
import java.util.ArrayList;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable;
public abstract class StringMap extends Hashtable<String, String> { public abstract class StringMap extends Hashtable<String, String> {
protected StringMap(Map<String, String> m) { protected StringMap(Map<String, String> m) {
@@ -58,31 +52,4 @@ public abstract class StringMap extends Hashtable<String, String> {
public void putLong(String key, long value) { public void putLong(String key, long value) {
put(key, String.valueOf(value)); put(key, String.valueOf(value));
} }
@Nullable
public int[] getIntArray(String key) {
String s = get(key);
if (s == null) return null;
// Handle empty string because "".split(",") returns {""}
if (s.length() == 0) return new int[0];
String[] intStrings = s.split(",");
int[] ints = new int[intStrings.length];
try {
for (int i = 0; i < ints.length; i++) {
ints[i] = Integer.parseInt(intStrings[i]);
}
} catch (NumberFormatException e) {
return null;
}
return ints;
}
public void putIntArray(String key, int[] value) {
List<String> intStrings = new ArrayList<>();
for (int integer : value) {
intStrings.add(String.valueOf(integer));
}
// Puts empty string if input array value is empty
put(key, StringUtils.join(intStrings, ","));
}
} }

View File

@@ -1,19 +1,19 @@
package org.briarproject.bramble.api; package org.briarproject.bramble.api;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
public class UniqueId extends Bytes { public abstract class UniqueId extends Bytes {
/** /**
* The length of a unique identifier in bytes. * The length of a unique identifier in bytes.
*/ */
public static final int LENGTH = 32; public static final int LENGTH = 32;
public UniqueId(byte[] id) { protected UniqueId(byte[] id) {
super(id); super(id);
if (id.length != LENGTH) throw new IllegalArgumentException(); if (id.length != LENGTH) throw new IllegalArgumentException();
} }

View File

@@ -1,35 +0,0 @@
package org.briarproject.bramble.api;
import org.briarproject.nullsafety.NotNullByDefault;
import java.lang.ref.WeakReference;
import javax.annotation.concurrent.GuardedBy;
import javax.inject.Provider;
/**
* A {@link Provider} that keeps a {@link WeakReference} to the last provided
* instance and provides the same instance again until the instance is garbage
* collected.
*/
@NotNullByDefault
public abstract class WeakSingletonProvider<T> implements Provider<T> {
private final Object lock = new Object();
@GuardedBy("lock")
private WeakReference<T> ref = new WeakReference<>(null);
@Override
public T get() {
synchronized (lock) {
T instance = ref.get();
if (instance == null) {
instance = createInstance();
ref = new WeakReference<>(instance);
}
return instance;
}
}
public abstract T createInstance();
}

View File

@@ -1,9 +1,8 @@
package org.briarproject.bramble.api.account; package org.briarproject.bramble.api.account;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -14,8 +13,7 @@ public interface AccountManager {
* Returns true if the manager has the database key. This will be false * Returns true if the manager has the database key. This will be false
* before {@link #createAccount(String, String)} or {@link #signIn(String)} * before {@link #createAccount(String, String)} or {@link #signIn(String)}
* has been called, and true after {@link #createAccount(String, String)} * has been called, and true after {@link #createAccount(String, String)}
* or {@link #signIn(String)} has returned true, until * or {@link #signIn(String)} has returned true, until the process exits.
* {@link #deleteAccount()} is called or the process exits.
*/ */
boolean hasDatabaseKey(); boolean hasDatabaseKey();
@@ -24,22 +22,25 @@ public interface AccountManager {
* before {@link #createAccount(String, String)} or {@link #signIn(String)} * before {@link #createAccount(String, String)} or {@link #signIn(String)}
* has been called, and non-null after * has been called, and non-null after
* {@link #createAccount(String, String)} or {@link #signIn(String)} has * {@link #createAccount(String, String)} or {@link #signIn(String)} has
* returned true, until {@link #deleteAccount()} is called or the process * returned true, until the process exits.
* exits.
*/ */
@Nullable @Nullable
SecretKey getDatabaseKey(); SecretKey getDatabaseKey();
/** /**
* Returns true if the encrypted database key can be loaded from disk. * Returns true if the encrypted database key can be loaded from disk, and
* the database directory exists and is a directory.
*/ */
boolean accountExists(); boolean accountExists();
/** /**
* Creates an identity with the given name and registers it with the * Creates an identity with the given name and registers it with the
* {@link IdentityManager}. Creates a database key, encrypts it with the * {@link IdentityManager}. Creates a database key, encrypts it with the
* given password and stores it on disk. {@link #accountExists()} will * given password and stores it on disk.
* return true after this method returns true. * <p/>
* This method does not create the database directory, so
* {@link #accountExists()} will continue to return false until the
* database directory is created.
*/ */
boolean createAccount(String name, String password); boolean createAccount(String name, String password);
@@ -53,19 +54,17 @@ public interface AccountManager {
* Loads the encrypted database key from disk and decrypts it with the * Loads the encrypted database key from disk and decrypts it with the
* given password. * given password.
* *
* @throws DecryptionException If the database key could not be loaded and * @return true if the database key was successfully loaded and decrypted.
* decrypted.
*/ */
void signIn(String password) throws DecryptionException; boolean signIn(String password);
/** /**
* Loads the encrypted database key from disk, decrypts it with the old * Loads the encrypted database key from disk, decrypts it with the old
* password, encrypts it with the new password, and stores it on disk, * password, encrypts it with the new password, and stores it on disk,
* replacing the old key. * replacing the old key.
* *
* @throws DecryptionException If the database key could not be loaded and * @return true if the database key was successfully loaded, re-encrypted
* decrypted. * and stored.
*/ */
void changePassword(String oldPassword, String newPassword) boolean changePassword(String oldPassword, String newPassword);
throws DecryptionException;
} }

View File

@@ -1,29 +0,0 @@
package org.briarproject.bramble.api.cleanup;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.nullsafety.NotNullByDefault;
import java.util.Collection;
/**
* An interface for registering a hook with the {@link CleanupManager}
* that will be called when a message's cleanup deadline is reached.
*/
@NotNullByDefault
public interface CleanupHook {
/**
* Called when the cleanup deadlines of one or more messages are reached.
* <p>
* The callee is not required to delete the messages, but the hook won't be
* called again for these messages unless another cleanup timer is set (see
* {@link DatabaseComponent#setCleanupTimerDuration(Transaction, MessageId, long)}
* and {@link DatabaseComponent#startCleanupTimer(Transaction, MessageId)}).
*/
void deleteMessages(Transaction txn, GroupId g,
Collection<MessageId> messageIds) throws DbException;
}

View File

@@ -1,42 +0,0 @@
package org.briarproject.bramble.api.cleanup;
import org.briarproject.bramble.api.cleanup.event.CleanupTimerStartedEvent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.nullsafety.NotNullByDefault;
/**
* The CleanupManager is responsible for tracking the cleanup deadlines of
* messages and passing them to their respective
* {@link CleanupHook CleanupHooks} when the deadlines are reached.
* <p>
* The CleanupManager responds to
* {@link CleanupTimerStartedEvent CleanupTimerStartedEvents} broadcast by the
* {@link DatabaseComponent}.
* <p>
* See {@link DatabaseComponent#setCleanupTimerDuration(Transaction, MessageId, long)},
* {@link DatabaseComponent#startCleanupTimer(Transaction, MessageId)},
* {@link DatabaseComponent#stopCleanupTimer(Transaction, MessageId)}.
*/
@NotNullByDefault
public interface CleanupManager {
/**
* When scheduling a cleanup task we overshoot the deadline by this many
* milliseconds to reduce the number of tasks that need to be scheduled
* when messages have cleanup deadlines that are close together.
*/
long BATCH_DELAY_MS = 1000;
/**
* Registers a hook to be called when messages are due for cleanup.
* This method should be called before
* {@link LifecycleManager#startServices(SecretKey)}.
*/
void registerCleanupHook(ClientId c, int majorVersion,
CleanupHook hook);
}

View File

@@ -1,32 +0,0 @@
package org.briarproject.bramble.api.cleanup.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a message's cleanup timer is started.
*/
@Immutable
@NotNullByDefault
public class CleanupTimerStartedEvent extends Event {
private final MessageId messageId;
private final long cleanupDeadline;
public CleanupTimerStartedEvent(MessageId messageId,
long cleanupDeadline) {
this.messageId = messageId;
this.cleanupDeadline = cleanupDeadline;
}
public MessageId getMessageId() {
return messageId;
}
public long getCleanupDeadline() {
return cleanupDeadline;
}
}

View File

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

View File

@@ -1,8 +1,8 @@
package org.briarproject.bramble.api.client; package org.briarproject.bramble.api.client;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;

View File

@@ -4,19 +4,18 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder; import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.InvalidMessageException; import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageContext; import org.briarproject.bramble.api.sync.MessageContext;
import org.briarproject.bramble.api.sync.validation.MessageValidator; import org.briarproject.bramble.api.sync.validation.MessageValidator;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.nullsafety.NotNullByDefault;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
@Immutable @Immutable
@@ -24,30 +23,17 @@ import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOC
public abstract class BdfMessageValidator implements MessageValidator { public abstract class BdfMessageValidator implements MessageValidator {
protected static final Logger LOG = protected static final Logger LOG =
getLogger(BdfMessageValidator.class.getName()); Logger.getLogger(BdfMessageValidator.class.getName());
protected final ClientHelper clientHelper; protected final ClientHelper clientHelper;
protected final MetadataEncoder metadataEncoder; protected final MetadataEncoder metadataEncoder;
protected final Clock clock; protected final Clock clock;
protected final boolean canonical;
/**
* Transitional alternative to
* {@link #BdfMessageValidator(ClientHelper, MetadataEncoder, Clock)} that
* accepts messages in non-canonical form, for backward compatibility.
*/
@Deprecated
protected BdfMessageValidator(ClientHelper clientHelper,
MetadataEncoder metadataEncoder, Clock clock, boolean canonical) {
this.clientHelper = clientHelper;
this.metadataEncoder = metadataEncoder;
this.clock = clock;
this.canonical = canonical;
}
protected BdfMessageValidator(ClientHelper clientHelper, protected BdfMessageValidator(ClientHelper clientHelper,
MetadataEncoder metadataEncoder, Clock clock) { MetadataEncoder metadataEncoder, Clock clock) {
this(clientHelper, metadataEncoder, clock, true); this.clientHelper = clientHelper;
this.metadataEncoder = metadataEncoder;
this.clock = clock;
} }
protected abstract BdfMessageContext validateMessage(Message m, Group g, protected abstract BdfMessageContext validateMessage(Message m, Group g,
@@ -63,7 +49,7 @@ public abstract class BdfMessageValidator implements MessageValidator {
"Timestamp is too far in the future"); "Timestamp is too far in the future");
} }
try { try {
BdfList bodyList = clientHelper.toList(m, canonical); BdfList bodyList = clientHelper.toList(m.getBody());
BdfMessageContext result = validateMessage(m, g, bodyList); BdfMessageContext result = validateMessage(m, g, bodyList);
Metadata meta = metadataEncoder.encode(result.getDictionary()); Metadata meta = metadataEncoder.encode(result.getDictionary());
return new MessageContext(meta, result.getDependencies()); return new MessageContext(meta, result.getDependencies());

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.api.client; package org.briarproject.bramble.api.client;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
@@ -9,18 +8,14 @@ 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.identity.Author;
import org.briarproject.bramble.api.mailbox.MailboxUpdate; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.mailbox.MailboxVersion;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; 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;
import org.briarproject.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
@NotNullByDefault @NotNullByDefault
@@ -49,29 +44,15 @@ public interface ClientHelper {
BdfList getMessageAsList(Transaction txn, MessageId m) throws DbException, BdfList getMessageAsList(Transaction txn, MessageId m) throws DbException,
FormatException; FormatException;
/**
* Transitional alternative to
* {@link #getMessageAsList(Transaction, MessageId)} that allows the
* message to be in non-canonical form, for backward compatibility.
*
* @param canonical True if the message must be in canonical form (a
* {@link FormatException} will be thrown if it's not.
*/
@Deprecated
BdfList getMessageAsList(Transaction txn, MessageId m, boolean canonical)
throws DbException, FormatException;
BdfDictionary getGroupMetadataAsDictionary(GroupId g) throws DbException, BdfDictionary getGroupMetadataAsDictionary(GroupId g) throws DbException,
FormatException; FormatException;
BdfDictionary getGroupMetadataAsDictionary(Transaction txn, GroupId g) BdfDictionary getGroupMetadataAsDictionary(Transaction txn, GroupId g)
throws DbException, FormatException; throws DbException, FormatException;
Collection<MessageId> getMessageIds(Transaction txn, GroupId g,
BdfDictionary query) throws DbException, FormatException;
BdfDictionary getMessageMetadataAsDictionary(MessageId m) BdfDictionary getMessageMetadataAsDictionary(MessageId m)
throws DbException, FormatException; throws DbException,
FormatException;
BdfDictionary getMessageMetadataAsDictionary(Transaction txn, MessageId m) BdfDictionary getMessageMetadataAsDictionary(Transaction txn, MessageId m)
throws DbException, FormatException; throws DbException, FormatException;
@@ -118,16 +99,6 @@ public interface ClientHelper {
BdfList toList(Message m) throws FormatException; BdfList toList(Message m) throws FormatException;
/**
* Transitional alternative to {@link #toList(Message)} that allows the
* message to be in non-canonical form, for backward compatibility.
*
* @param canonical True if the message must be in canonical form (a
* {@link FormatException} will be thrown if it's not.
*/
@Deprecated
BdfList toList(Message m, boolean canonical) throws FormatException;
BdfList toList(Author a); BdfList toList(Author a);
byte[] sign(String label, BdfList toSign, PrivateKey privateKey) byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
@@ -148,30 +119,4 @@ public interface ClientHelper {
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap( Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
BdfDictionary properties) throws FormatException; BdfDictionary properties) throws FormatException;
/**
* Parse and validate the elements of a Mailbox update message.
*
* @return the parsed update message
* @throws FormatException if the message elements are invalid
*/
MailboxUpdate parseAndValidateMailboxUpdate(BdfList clientSupports,
BdfList serverSupports, BdfDictionary properties)
throws FormatException;
List<MailboxVersion> parseMailboxVersionList(BdfList bdfList)
throws FormatException;
/**
* Retrieves the contact ID from the group metadata of the given contact
* group.
*/
ContactId getContactId(Transaction txn, GroupId contactGroupId)
throws DbException;
/**
* Stores the given contact ID in the group metadata of the given contact
* group.
*/
void setContactId(Transaction txn, GroupId contactGroupId, ContactId c)
throws DbException;
} }

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api.client;
public interface ContactGroupConstants {
/**
* Group metadata key for associating a contact ID with a contact group.
*/
String GROUP_KEY_CONTACT_ID = "contactId";
}

View File

@@ -2,9 +2,9 @@ package org.briarproject.bramble.api.client;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface ContactGroupFactory { public interface ContactGroupFactory {

View File

@@ -1,86 +0,0 @@
package org.briarproject.bramble.api.connection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.sync.OutgoingSessionRecord;
import org.briarproject.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface ConnectionManager {
/**
* Manages an incoming connection from a contact over a simplex transport.
*/
void manageIncomingConnection(TransportId t, TransportConnectionReader r);
/**
* Manages an incoming connection from a contact via a mailbox.
* <p>
* This method does not mark the tag as recognised until after the data
* has been read from the {@link TransportConnectionReader}, at which
* point the {@link TagController} is called to decide whether the tag
* should be marked as recognised.
*/
void manageIncomingConnection(TransportId t, TransportConnectionReader r,
TagController c);
/**
* Manages an incoming connection from a contact over a duplex transport.
*/
void manageIncomingConnection(TransportId t, DuplexTransportConnection d);
/**
* Manages an incoming handshake connection from a pending contact over a
* duplex transport.
*/
void manageIncomingConnection(PendingContactId p, TransportId t,
DuplexTransportConnection d);
/**
* Manages an outgoing connection to a contact over a simplex transport.
*/
void manageOutgoingConnection(ContactId c, TransportId t,
TransportConnectionWriter w);
/**
* Manages an outgoing connection to a contact via a mailbox. The IDs of
* any messages sent or acked are added to the given
* {@link OutgoingSessionRecord}.
*/
void manageOutgoingConnection(ContactId c, TransportId t,
TransportConnectionWriter w, OutgoingSessionRecord sessionRecord);
/**
* Manages an outgoing connection to a contact over a duplex transport.
*/
void manageOutgoingConnection(ContactId c, TransportId t,
DuplexTransportConnection d);
/**
* Manages an outgoing handshake connection to a pending contact over a
* duplex transport.
*/
void manageOutgoingConnection(PendingContactId p, TransportId t,
DuplexTransportConnection d);
/**
* An interface for controlling whether a tag should be marked as
* recognised.
*/
interface TagController {
/**
* This method is only called if a tag was read from the corresponding
* {@link TransportConnectionReader} and recognised.
*
* @param exception True if an exception was thrown while reading from
* the {@link TransportConnectionReader}, after successfully reading
* and recognising the tag.
* @return True if the tag should be marked as recognised.
*/
boolean shouldMarkTagAsRecognised(boolean exception);
}
}

View File

@@ -1,130 +0,0 @@
package org.briarproject.bramble.api.connection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.nullsafety.NotNullByDefault;
import java.util.Collection;
/**
* Keeps track of which contacts are currently connected by which transports.
*/
@NotNullByDefault
public interface ConnectionRegistry {
/**
* Registers an incoming connection from the given contact over the given
* transport. The connection's {@link Priority priority} can be set later
* via {@link #setPriority(ContactId, TransportId, InterruptibleConnection,
* Priority)} if a priority record is received from the contact.
* <p>
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
* {@link ContactConnectedEvent} if this is the only connection with the
* contact.
*/
void registerIncomingConnection(ContactId c, TransportId t,
InterruptibleConnection conn);
/**
* Registers an outgoing connection to the given contact over the given
* transport.
* <p>
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
* {@link ContactConnectedEvent} if this is the only connection with the
* contact.
* <p>
* If the registry has any "better" connections with the given contact, the
* given connection will be interrupted. If the registry has any "worse"
* connections with the given contact, those connections will be
* interrupted.
* <p>
* Connection A is considered "better" than connection B if both
* connections have had their priorities set, and either A's transport is
* {@link PluginConfig#getTransportPreferences() preferred} to B's, or
* they use the same transport and A has higher {@link Priority priority}
* than B.
* <p>
* For backward compatibility, connections without priorities are not
* considered better or worse than other connections.
*/
void registerOutgoingConnection(ContactId c, TransportId t,
InterruptibleConnection conn, Priority priority);
/**
* Unregisters a connection with the given contact over the given transport.
* <p>
* Broadcasts {@link ConnectionClosedEvent}. Also broadcasts
* {@link ContactDisconnectedEvent} if this is the only connection with
* the contact.
*/
void unregisterConnection(ContactId c, TransportId t,
InterruptibleConnection conn, boolean incoming, boolean exception);
/**
* Sets the {@link Priority priority} of a connection that was previously
* registered via {@link #registerIncomingConnection(ContactId, TransportId,
* InterruptibleConnection)}.
* <p>
* If the registry has any "better" connections with the given contact, the
* given connection will be interrupted. If the registry has any "worse"
* connections with the given contact, those connections will be
* interrupted.
* <p>
* Connection A is considered "better" than connection B if both
* connections have had their priorities set, and either A's transport is
* {@link PluginConfig#getTransportPreferences() preferred} to B's, or
* they use the same transport and A has higher {@link Priority priority}
* than B.
* <p>
* For backward compatibility, connections without priorities are not
* considered better or worse than other connections.
*/
void setPriority(ContactId c, TransportId t, InterruptibleConnection conn,
Priority priority);
/**
* Returns any contacts that are connected via the given transport.
*/
Collection<ContactId> getConnectedContacts(TransportId t);
/**
* Returns any contacts that are connected via the given transport or any
* {@link PluginConfig#getTransportPreferences() better} transport.
*/
Collection<ContactId> getConnectedOrBetterContacts(TransportId t);
/**
* Returns true if the given contact is connected via the given transport.
*/
boolean isConnected(ContactId c, TransportId t);
/**
* Returns true if the given contact is connected via any transport.
*/
boolean isConnected(ContactId c);
/**
* Registers a connection with the given pending contact. Broadcasts
* {@link RendezvousConnectionOpenedEvent} if this is the only connection
* with the pending contact.
*
* @return True if this is the only connection with the pending contact,
* false if it is redundant and should be closed
*/
boolean registerConnection(PendingContactId p);
/**
* Unregisters a connection with the given pending contact. Broadcasts
* {@link RendezvousConnectionClosedEvent}.
*/
void unregisterConnection(PendingContactId p, boolean success);
}

View File

@@ -1,19 +0,0 @@
package org.briarproject.bramble.api.connection;
import org.briarproject.nullsafety.NotNullByDefault;
/**
* A duplex sync connection that can be closed by interrupting its outgoing
* sync session.
*/
@NotNullByDefault
public interface InterruptibleConnection {
/**
* Interrupts the connection's outgoing sync session. If the underlying
* transport connection is alive and the remote peer is cooperative, this
* should result in both sync sessions ending and the connection being
* cleanly closed.
*/
void interruptOutgoingSession();
}

View File

@@ -3,7 +3,7 @@ package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
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.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -3,8 +3,8 @@ package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.nullsafety.NotNullByDefault;
import java.io.IOException; import java.io.IOException;

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -11,8 +11,9 @@ import org.briarproject.bramble.api.db.PendingContactExistsException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Collection; import java.util.Collection;
@@ -107,32 +108,6 @@ public interface ContactManager {
*/ */
String getHandshakeLink() throws DbException; String getHandshakeLink() throws DbException;
/**
* Returns the handshake link that needs to be sent to a contact we want
* to add.
*/
String getHandshakeLink(Transaction txn) throws DbException;
/**
* Creates a {@link PendingContact} from the given handshake link and
* alias, adds it to the database and returns it.
*
* @param link The handshake link received from the pending contact
* @param alias The alias the user has given this pending contact
* @throws UnsupportedVersionException If the link uses a format version
* that is not supported
* @throws FormatException If the link is invalid
* @throws GeneralSecurityException If the pending contact's handshake
* public key is invalid
* @throws ContactExistsException If a contact with the same handshake
* public key already exists
* @throws PendingContactExistsException If a pending contact with the same
* handshake public key already exists
*/
PendingContact addPendingContact(Transaction txn, String link, String alias)
throws DbException, FormatException, GeneralSecurityException,
ContactExistsException, PendingContactExistsException;
/** /**
* Creates a {@link PendingContact} from the given handshake link and * Creates a {@link PendingContact} from the given handshake link and
* alias, adds it to the database and returns it. * alias, adds it to the database and returns it.
@@ -166,24 +141,11 @@ public interface ContactManager {
Collection<Pair<PendingContact, PendingContactState>> getPendingContacts() Collection<Pair<PendingContact, PendingContactState>> getPendingContacts()
throws DbException; throws DbException;
/**
* Returns a list of {@link PendingContact PendingContacts} and their
* {@link PendingContactState states}.
*/
Collection<Pair<PendingContact, PendingContactState>> getPendingContacts(Transaction txn)
throws DbException;
/** /**
* Removes a {@link PendingContact}. * Removes a {@link PendingContact}.
*/ */
void removePendingContact(PendingContactId p) throws DbException; void removePendingContact(PendingContactId p) throws DbException;
/**
* Removes a {@link PendingContact}.
*/
void removePendingContact(Transaction txn, PendingContactId p)
throws DbException;
/** /**
* Returns the contact with the given ID. * Returns the contact with the given ID.
*/ */
@@ -217,11 +179,6 @@ public interface ContactManager {
*/ */
Collection<Contact> getContacts() throws DbException; Collection<Contact> getContacts() throws DbException;
/**
* Returns all contacts.
*/
Collection<Contact> getContacts(Transaction txn) throws DbException;
/** /**
* Removes a contact and all associated state. * Removes a contact and all associated state.
*/ */
@@ -258,6 +215,16 @@ public interface ContactManager {
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId) boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException; throws DbException;
/**
* Returns the {@link AuthorInfo} for the given author.
*/
AuthorInfo getAuthorInfo(AuthorId a) throws DbException;
/**
* Returns the {@link AuthorInfo} for the given author.
*/
AuthorInfo getAuthorInfo(Transaction txn, AuthorId a) throws DbException;
interface ContactHook { interface ContactHook {
/** /**

View File

@@ -2,8 +2,8 @@ package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.nullsafety.NotNullByDefault;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -1,8 +1,9 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.UniqueId; import org.briarproject.bramble.api.UniqueId;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
/** /**
@@ -16,4 +17,9 @@ public class PendingContactId extends UniqueId {
public PendingContactId(byte[] id) { public PendingContactId(byte[] id) {
super(id); super(id);
} }
@Override
public boolean equals(@Nullable Object o) {
return o instanceof PendingContactId && super.equals(o);
}
} }

View File

@@ -2,7 +2,7 @@ package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -1,35 +0,0 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when the alias for a contact changed.
*/
@Immutable
@NotNullByDefault
public class ContactAliasChangedEvent extends Event {
private final ContactId contactId;
@Nullable
private final String alias;
public ContactAliasChangedEvent(ContactId contactId,
@Nullable String alias) {
this.contactId = contactId;
this.alias = alias;
}
public ContactId getContactId() {
return contactId;
}
@Nullable
public String getAlias() {
return alias;
}
}

View File

@@ -2,7 +2,7 @@ package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -2,7 +2,7 @@ package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -2,7 +2,7 @@ package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -2,7 +2,7 @@ package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -3,7 +3,7 @@ package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.api.crypto; package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.Bytes;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.api.crypto; package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.Bytes;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;

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